简单的翻转时钟效果

需求

时分秒为翻转卡片,与系统时间同步翻转

效果图

原文地址:juejin.cn/post/723596…,转载请注明。
简单的翻转时钟效果

实现步骤

卡片翻转

  1. 用四个div模拟翻转卡片
<div class="card-container">
  <div class="card-item card1">1</div>
  <div class="card-item card2">1</div>
  <div class="card-item card3">0</div>
  <div class="card-item card4">0</div>
</div>

2. .card1.card2表示下一个数,.card和.card4表示当前展示数字

初始状态:

  • card1为下一个数的上半部分,card2为下半部分,card2翻转叠在card1上;

  • card3为当前数的上半部分,card4为当前数的下半部分,card3叠在card1card2上。

简单的翻转时钟效果

翻转原理:

  • card2向下(面向自己)旋转到0deg,即卡片的下半部分;
  • card3向下旋转到-180deg,即卡片的下半部分(背面对着自己);
  • card2与card3同步旋转
  • 旋转结束后,card1和card2在上面(层级显示),card4被card3盖住,card3背对,card2正面显示
  1. 搞明白div是如何翻转之后,设置css样式

    1. 数字分为上半部分和下半部分,以card1和card2为例。
    .card-item { /* 公共样式 */
     width: 100%;
     height: 50%;
     background-color: #000;
     position: absolute; /* 相同的位置,半张卡片重叠 */ 
     color: #fff;
     text-align: center;
     font-size: 10em;
     line-height: 200px; /*设置行高,字体在盒子里的位置将变化*/
     overflow: hidden; /*设置行高后,半边字溢出,将其隐藏起来*/
    };
    /*通用样式设置之后,card1与card2位置叠在一起, 将card2往下移
    .card2 {
     top: 50%; /* 下移 */
     line-height: 0; /* 调整下半边的字 */
     margin-top: 1px;
    }
    

    2. 设置card2的初始状态

    backface-visibility: hidden; /* 背面不可见 */
    z-index: 2; /* 调整层级,以免翻转时被card4盖住 */
    transform-origin: center top; /* 旋转中心为卡片中心,即衔接的中心 */
    transform:rotateX(180deg); /* 旋转, tops: 为了有3d效果,可以为父元素加perspective*/
    

    3. card2的旋转动画

    @keyframes flip {
        to {
            transform: rotateX(0deg);
        }
    }
    

    4. card3的旋转动画

    类似的样式设置不再赘述。
    
    @keyframes flip1 {
        to {
            transform: rotateX(180deg);
        }
    }
    

    5. 为卡片加上无限动画,时间为1s

    /* backwards: 一次动画后,回到动画开始前的状态*/
    animation: flip1 1s ease-in-out backwards infinite; 
    

    至此,单个卡片的旋转就完成了,也就是时钟里部分结束。

时分翻转

在前面卡片翻转中,设置动画持续时间为1s,正好对应秒表。但是,对于一分钟(一小时)更新一次的分表(时表),动画需要进行暂停以及有条件的开启。

时分表和秒表具有相同的html结构以及css样式,以下是基于秒表结构的改变:

  1. 动画初始化为暂停状态
animation-play-state: paused;

2. 监听秒表动画的每一次结束,计算当前时间以及下一秒的时间,如果下一秒时分需要变化,则为其开启动画

/* 获取时分秒的各个元素 */
doms = {
    second1: ..., // 秒表的card1
    second2: ...,  // 秒表的card2
    container: ..., // 父元素
}
second2.addEventListener('animationiteration', function (e) {
   changeTime(); // 获取更改当前时间以及下一秒时间函数
   doms.second1.innerHTML = zeroFormat(nextSecond); // 页面元素值替换为下一秒的值
   doms.second2.innerHTML = zeroFormat(nextSecond); // zeroFormat为补0函数
   doms.second3.innerHTML = zeroFormat(nowSecond);
   doms.second4.innerHTML = zeroFormat(nowSecond);
   if (nextMinute !== nowMinute) { // 如果下一秒需要改变分钟, 开启分表的card2和card3动画
   doms.minute2.style.animationPlayState = "running";
   doms.minute3.style.animationPlayState = "running";
   }
   if (nowHour !== nowHour) {
   doms.hour2.style.animationPlayState = "running";
   doms.hour3.style.animationPlayState = "running";
   }
})

3. 动画开启,执行一秒后,分/时表的动画需要再次暂停。为了代码不冗余,使用事件代理,对上述代码进行改造。
1. 如果触发animationiteration事件的是秒表元素,则改变秒表卡片的数字,判断时分是否需要变化,需要变化则开启动画,对应changeAnimationState函数。
2. 如果触发的是时/分表元素,则表示时/分表动画结束,需要暂停响应的动画,并改变卡片的数字

doms.container.addEventListener("animationiteration", function (e) {
 if (e.target.classList.contains("second")) {
   changeAnimationState();
 } else if (e.target.classList.contains("minute")) {
   doms.minute1.innerHTML = zeroFormat(nextMinute === 59 ? 0 : nextMinute + 1);
   doms.minute2.innerHTML = zeroFormat(nextMinute === 59 ? 0 : nextMinute + 1);
   doms.minute3.innerHTML = zeroFormat(nextMinute);
   doms.minute4.innerHTML = zeroFormat(nextMinute);
   e.target.style.animationPlayState = "paused";
 } else {
   doms.hour1.innerHTML = zeroFormat(nextHour === 23 ? 0 : nextHour + 1);
   doms.hour2.innerHTML = zeroFormat(nextHour === 23 ? 0 : nextHour + 1);
   doms.hour3.innerHTML = zeroFormat(nextHour);
   doms.hour4.innerHTML = zeroFormat(nextHour);
   e.target.style.animationPlayState = "paused";
 }
});
function changeAnimationState() {
 changeTime();
 doms.second1.innerHTML = zeroFormat(nextSecond);
 doms.second2.innerHTML = zeroFormat(nextSecond);
 doms.second3.innerHTML = zeroFormat(nowSecond);
 doms.second4.innerHTML = zeroFormat(nowSecond);
 if (nextMinute !== nowMinute) {
   doms.minute2.style.animationPlayState = "running";
   doms.minute3.style.animationPlayState = "running";
 }
 if (nowHour !== nowHour) {
   doms.hour2.style.animationPlayState = "running";
   doms.hour3.style.animationPlayState = "running";
 }
}

为了简单,设定全局元素nowSecond, nextSecond, nowMinute, nextMinute, nowHour, nextHour

function changeTime() {
 var now = new Date();
 var next = new Date(now.getTime() + 1000);
 nowSecond = now.getSeconds();
 nextSecond = next.getSeconds();
 nowMinute = now.getMinutes();
 nextMinute = next.getMinutes();
 nowHour = now.getHours();
 nextHour = next.getHours();
}

至此,时/分/秒表的翻转功能基本上就实现了。

时钟初始化

页面刚进入,需要获取当前时间进行渲染,监听onload事件

// 分别为时分秒卡片设置当前显示值,以及被覆盖的下一翻转显示值
window.onload = function () {
  changeTime(); // 获取时间
  doms.second1.innerHTML = zeroFormat(nextSecond); 
  doms.second2.innerHTML = zeroFormat(nextSecond);
  doms.second3.innerHTML = zeroFormat(nowSecond);
  doms.second4.innerHTML = zeroFormat(nowSecond);
  doms.minute1.innerHTML = zeroFormat(nextMinute);
  doms.minute2.innerHTML = zeroFormat(nextMinute);
  doms.minute3.innerHTML = zeroFormat(nowMinute);
  doms.minute4.innerHTML = zeroFormat(nowMinute);
  doms.hour1.innerHTML = zeroFormat(nextHour);
  doms.hour2.innerHTML = zeroFormat(nextHour);
  doms.hour3.innerHTML = zeroFormat(nowHour);
  doms.hour4.innerHTML = zeroFormat(nowHour);
};

所有功能实现完毕!

不足

  1. 一共使用了12个卡片元素(.card-item),可能会有更好的解决翻转的方式
  2. 分别获取了12个元素对应的dom,并对其innerHtml进行了修改(有点傻)
  3. 卡片可以抽离为组件,思考:如何设计组件的接收属性,可以适配时/分/秒

翻转时钟完整代码见github: github.com/gmx-white/c… flip-clock分支

原文链接:https://juejin.cn/post/7235965783632429116 作者:gmx_white

(0)
上一篇 2023年5月22日 上午10:00
下一篇 2023年5月23日 上午10:05

相关推荐

发表评论

登录后才能评论