译者注: 本文内容为 Clocks - CSS Animation https://cssanimation.rocks/clocks/ 的译文
时钟
这是关于时间的. 在本文中, 我们将接受创建和设置时钟动画的挑战, 使用简单的 CSS 动画和 JavaScript 来触发它们.
这是我们使用 html,CSS,SVG 背景和一些 JavaScript 创建的时钟. 我们将使用 CSS 动画或过渡进行任何移动, 并依靠 JavaScript 来设置初始时间并添加基本的 CSS 变换.
HTML
要开始使用, 我们需要一些 HTML.
- <article class="clock">
- <div class="hours-container">
- <div class="hours"></div>
- </div>
- <div class="minutes-container">
- <div class="minutes"></div>
- </div>
- <div class="seconds-container">
- <div class="seconds"></div>
- </div>
- </article>
我最初的方法是为每只指针使用三个元素. 然后我回去把每个包装在一个容器元素中. 虽然简单的 HTML 可以用于基本的 CSS 动画, 但是当我们想要定位指针并为它们制作动画时, 我们还需要包含元素.
钟面
我们将从一个基本的时钟设计开始, 圆脸, 简单的小时, 分钟和秒针.
- .clock {
- border-radius: 50%;
- background: #fff url(/images/posts/clocks/ios_clock.svg) no-repeat center;
- background-size: 88%;
- height: 20em;
- padding-bottom: 31%;
- position: relative;
- width: 20em;
- }
- .clock.simple:after {
- background: #000;
- border-radius: 50%;
- content: "";
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- width: 5%;
- height: 5%;
- z-index: 10;
- }
你可以在这里获得 SVG 背景. 我还添加了一个伪元素来向中心添加一个纯黑色圆圈. 然后可以根据需要将时钟的指针放在该伪元素后面.
我们现在应该有这样的结果.
在添加指针之前, 我们需要放置容器.
- .minutes-container, .hours-container, .seconds-container {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- }
这会将每个容器堆叠在时钟之上. 接下来我们创造指针.
时针
每只指针都被赋予 absolute 属性值并放置在十二点钟位置. 我们将从时针开始.
- .hours {
- background: #000;
- height: 20%;
- left: 48.75%;
- position: absolute;
- top: 30%;
- transform-origin: 50% 100%;
- width: 2.5%;
- }
我正在使用百分比来简化时钟缩放. 这是一项复杂的工作, 但能让它们更容易适应视图或缩小手机. 我们还将 transform-origin 属性设置为指针的底部, 以便以后可以旋转.
分针
分针跟时针类似, 但更长更细.
- .minutes {
- background: #000;
- height: 40%;
- left: 49%;
- position: absolute;
- top: 10%;
- transform-origin: 50% 100%;
- width: 2%;
- }
秒针
秒针再次变细, 但也进一步向下设置, 使其比中心延伸得更远. 为此, transform-origin 为 80%. 这使得 20%的手伸出中心.
- .seconds {
- background: #000;
- height: 45%;
- left: 49.5%;
- position: absolute;
- top: 14%;
- transform-origin: 50% 80%;
- width: 1%;
- z-index: 8;
- }
动画
停止的时钟每天只能正确两次. 让我们添加一些动画来让时钟表现得更像真实的东西.
有些时钟会随着每秒跳跃, 通常会产生滴答声. 有些时钟会随着手的平稳移动而发出呜呜声. 我们两个都试试. 首先, 我们会让双手平稳移动.
我们可以使用一个 keyframe 来指示手在 360 度左右移动(暗示 0%的起始位置).
- @keyframes rotate {
- 100% {
- transform: rotateZ(360deg);
- }
- }
如果使用 animation 属性应用于元素, 则此关键帧会告诉元素在 360 度左右设置动画. 我们将在指针上使用 linear 计时功能, 使指针平稳移动.
- .hours-container {
- animation: rotate 43200s infinite linear;
- }
- .minutes-container {
- animation: rotate 3600s infinite linear;
- }
- .seconds-container {
- animation: rotate 60s infinite linear;
- }
时针设置为每 12 小时 (43,400 秒) 旋转一次. 分针每小时旋转一次, 秒针每分钟旋转一次.
把它放在一起, 我们现在有运动!
如果你的眼睛非常敏锐, 你甚至可以让分针移动. 它需要一个小时才能完成一个旋转, 十二个小时的时间来完成它的循环.
秒针需要 60 秒, 因此更容易注意到.
添加停顿
通过让秒针在 60 个单独的动作中全天候移动, 我们可以让指针更像普通时钟. 实现此目的的一种简单方法是使用 steps 计时功能. 然后每只指针的 animation 属性变为:
- .minutes-container {
- animation: rotate 3600s infinite steps(60);
- }
- .seconds-container {
- animation: rotate 60s infinite steps(60);
- }
分针和秒针现在分六步走动. 浏览器会自动计算这 60 个步骤中的每个步骤移动的距离.
正确的时间
一切都很好, 时间看起来不错, 但准确度如何呢? 使用一点 JavaScript, 我们可以让时间对我们的访问者来说是正确的. 这是代码:
- /*
- * Starts any clocks using the user's local time
- * From: cssanimation.rocks/clocks
- */
- function initLocalClocks() {
- // Get the local time using JS
- var date = new Date;
- var seconds = date.getSeconds();
- var minutes = date.getMinutes();
- var hours = date.getHours();
- // Create an object with each hand and it's angle in degrees
- var hands = [
- {
- hand: 'hours',
- angle: (hours * 30) + (minutes / 2)
- },
- {
- hand: 'minutes',
- angle: (minutes * 6)
- },
- {
- hand: 'seconds',
- angle: (seconds * 6)
- }
- ];
- // Loop through each of these hands to set their angle
- for (var j = 0; j <hands.length; j++) {
- var elements = document.querySelectorAll('.' + hands[j].hand);
- for (var k = 0; k < elements.length; k++) {
- elements[k].style.webkitTransform = 'rotateZ('+ hands[j].angle +'deg)';
- elements[k].style.transform = 'rotateZ('+ hands[j].angle +'deg)';
- // If this is a minute hand, note the seconds position (to calculate minute position later)
- if (hands[j].hand === 'minutes') {
- elements[k].parentNode.setAttribute('data-second-angle', hands[j + 1].angle);
- }
- }
- }
- }
此功能将时间 (小时, 分钟和秒) 转换为每只指针的相应角度. 然后循环遍历每只手并使用 rotateZ 的 style.transform 属性应用该角度.
这适用于除 Safari 或其他需要前缀的浏览器之外的大多数浏览器. 为此, 我们还使用了 style.webkitTransform 属性.
然后, 将时钟设置为当前系统时间.
对齐秒针和分针
当首次在屏幕上绘制时钟时, 在分针需要移动之前不到一分钟. 为了实现这一点, 我们可以计算出第一分钟结束的时间并手动轻推分针. 由于我们使用 JavaScript 进行第一次移动, 我们可以使用 setInterval 每分钟继续旋转 6 度.
在我们移动分针之前, 我们需要告知我们当前的分钟数. 您可能已经注意到这些线条.
- if (hands[j].hand === 'minutes') {
- elements[k].parentNode.setAttribute('data-second-angle', hands[j + 1].angle);
- }
这些额外的行检查指针是否是 "分钟" 指针, 如果是, 则使用秒针的当前角度设置数据属性.
使用此数据属性集, 我们可以使用此数据计算何时移动分针.
- /*
- * Set a timeout for the first minute hand movement (Less than 1 minute), then rotate it every minute after that
- */
- function setUpMinuteHands() {
- // Find out how far into the minute we are
- var containers = document.querySelectorAll('.minutes-container');
- var secondAngle = containers[0].getAttribute("data-second-angle");
- if (secondAngle> 0) {
- // Set a timeout until the end of the current minute, to move the hand
- var delay = (((360 - secondAngle) / 6) + 0.1) * 1000;
- setTimeout(function() {
- moveMinuteHands(containers);
- }, delay);
- }
- }
- /*
- * Do the first minute's rotation
- */
- function moveMinuteHands(containers) {
- for (var i = 0; i < containers.length; i++) {
- containers[i].style.webkitTransform = 'rotateZ(6deg)';
- containers[i].style.transform = 'rotateZ(6deg)';
- }
- // Then continue with a 60 second interval
- setInterval(function() {
- for (var i = 0; i < containers.length; i++) {
- if (containers[i].angle === undefined) {
- containers[i].angle = 12;
- } else {
- containers[i].angle += 6;
- }
- containers[i].style.webkitTransform = 'rotateZ('+ containers[i].angle +'deg)';
- containers[i].style.transform = 'rotateZ('+ containers[i].angle +'deg)';
- }
- }, 60000);
- }
添加弹跳
由于我们现在使用 JavaScript 来移动分针, 我们应该删除动画属性. 我们可以用过渡替换它, 而不是简单地删除它. 这是一个为动画添加更多角色的机会.
当 JavaScript 为手设置新角度时, 元素上的 CSS 转换将告诉浏览器为此新位置设置动画. 这意味着 JavaScript 只处理简单的角度变化, 浏览器可以处理它的动画.
在我们这样做之前, 我们应该更新代码以使用 JavaScript 来移动秒针. 让我们使用此代码为秒针容器设置动画, 每分钟动画 60 次.
- /*
- * Move the second containers
- */
- function moveSecondHands() {
- var containers = document.querySelectorAll('.seconds-container');
- setInterval(function() {
- for (var i = 0; i < containers.length; i++) {
- if (containers[i].angle === undefined) {
- containers[i].angle = 6;
- } else {
- containers[i].angle += 6;
- }
- containers[i].style.webkitTransform = 'rotateZ('+ containers[i].angle +'deg)';
- containers[i].style.transform = 'rotateZ('+ containers[i].angle +'deg)';
- }
- }, 1000);
- }
通过 JavaScript 处理秒针和分针, 更新 CSS 以使用 transitions 替换 animation 属性.
- .minutes-container {
- transition: transform 0.3s cubic-bezier(.4,2.08,.55,.44);
- }
- .seconds-container {
- transition: transform 0.2s cubic-bezier(.4,2.08,.55,.44);
- }
这些转换适用于 transform 属性并使用 cubic-bezier 定时函数. 这个计时功能让秒针有些反弹.
iOS7 风格
我非常喜欢 Apple iOS 7 中使用的时钟的简单性. 他们已经伸长了指针, 但我更喜欢较短的版本.
通过设计针部样式并添加带有数字的背景图像, 我们可以轻松创建此外观.
这种设计本身就是 Hans Hilfiker 对瑞士铁路时钟 https://www.youtube.com/watch?v=IvIvKiDWDks 的演变. 通过改变一些样式并添加新的背景图像, 我们可以使我们的时钟适应这种风格.
如果您想出其他设计, 请告诉我.
使用 Moment.JS
我计划这篇文章的第一个想法之一是重建酒店钟表场景, 三个时钟显示不同的时区.
调整不同时区的最简单方法是使用令人惊叹的 Moment.JS 时区库 http://momentjs.com/timezone/ .
您可以在此 Codepen https://codepen.io/donovanh/full/vEjywy/ 上查看代码.
浏览器兼容性
现代浏览器可以处理所涉及的 CSS 过渡和动画. IE10 +, 最近的 Chrome 和 Firefox 支持它们没有前缀, Safari 需要 - webkit 前缀.
不要忘记在 JavaScript 中使用前缀属性.
(完)
本文作者 Thinker
本文如有错误之处, 请留言, 我会及时更正
来源: https://juejin.im/post/5c7630a26fb9a049a62d4087