翻转时钟动画的实现
原创大约 3 分钟
翻转时钟动画实现
1. 背景
今天遇到一个特殊的需求, 就是需要实现类似于翻转式的时钟动画,他的效果大概是这样子的。
首先老惯例,先到网上找找有没有插件 😁😁😁😁,找了一圈后发现没有能满足定制化需求的插件。只能自己开干了。
2. 分析
参考网上的案例,我们可以将该动画分为四个部分:
1.首先我们需要两对盒子,分别显示翻转前的数字的上半部分,下半部分, 以及显示翻转后数字的上下部分.
2.先将翻转后的后半部分先向上旋转0.5turn
, 动画开始:翻转前的上半部分开始向下旋转0.5turn
3.后半部分的数字变化成下一秒时间,然后他原先向上旋转的部分开始恢复原来的样子
4.时分秒进阶的话就每次检查时间时分是否出现变动,出现变动的话就执行动画。
大概就是这个样子!!!😃😃😃😃😃
3. 实现
html 部分
<div class="clock">
<div class="flip">
<div class="digital front">
<div class="before">
<div class="content">
<span class="date">0</span>
<span class="unit">min</span>
</div>
</div>
<div class="after">
<div class="content">
<span class="date">0</span>
<span class="unit">min</span>
</div>
</div>
</div>
<div class="digital back">
<div class="before">
<div class="content">
<span class="date">1</span>
<span class="unit">min</span>
</div>
</div>
<div class="after">
<div class="content">
<span class="date">1</span>
<span class="unit">min</span>
</div>
</div>
</div>
</div>
<em class="divider">:</em>
<div class="flip">
<div class="digital front">
<div class="before">
<div class="content">
<span class="date">0</span>
<span class="unit">sec</span>
</div>
</div>
<div class="after">
<div class="content">
<span class="date">0</span>
<span class="unit">sec</span>
</div>
</div>
</div>
<div class="digital back">
<div class="before">
<div class="content">
<span class="date">1</span>
<span class="unit">sec</span>
</div>
</div>
<div class="after">
<div class="content">
<span class="date">1</span>
<span class="unit">sec</span>
</div>
</div>
</div>
</div>
</div>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(144deg, #f13073 0%, #e42ca5 100%);
}
/* 时钟盒子 */
.clock {
display: flex;
align-items: center;
/* 分割点 */
.divider {
font-size: 100px;
font-style: normal;
color: white;
/* 时钟卡片 */
.flip {
position: relative;
width: 123px;
height: 169px;
background-color: white;
border-radius: 20px;
}
/* 内容 */
.content {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 123px;
height: 169px;
border-radius: 20px;
}
.content::after,
.content::before {
position: absolute;
top: 50%;
transform: translateY(-50%);
content: '';
display: block;
width: 5px;
height: 28px;
background-color: rgba(162, 162, 162, 1);
}
.content::after {
left: 0;
}
.content::before {
right: 0;
}
.content .date {
color: #f13073;
font-size: 81px;
font-weight: bold;
}
.content .unit {
font-size: 31px;
color: #a9a9a9;
}
.after .content {
transform: translateY(-50%);
/*上下部分盒子*/
.digital .before,
.digital .after {
position: absolute;
left: 0;
right: 0;
overflow: hidden;
border-radius: 20px;
background-color: #fff;
perspective: 160px;
.digital .before {
top: 0;
bottom: 50%;
border-radius: 20px 20px 0px 0px;
border-bottom: 2px dashed #a9a9a9;
.digital .after {
top: 50%;
bottom: 0;
border-radius: 0px 0px 20px 20px;
.clock .flip .back .before,
.clock .flip .front .after {
z-index: 1;
.clock .flip .back .after {
z-index: 2;
}
.clock .flip .front .before {
z-index: 3;
}
.clock .flip .back .after {
transform-origin: center top;
transform: rotateX(0.5turn);
}
.clock .flip.running .front .before {
transform-origin: center bottom;
animation: frontFlipDown 0.6s ease-in-out;
backface-visibility: hidden;
}
.clock .flip.running .back .after {
animation: backFlipDown 0.6s ease-in-out;
@keyframes frontFlipDown {
to {
transform: rotateX(0.5turn);
}
@keyframes backFlipDown {
to {
transform: rotateX(0);
}
}
js 部分
class Flipper {
constructor(node, currentTime, nextTime) {
this.isFlipping = false
this.duration = 600
this.flipNode = node
this.frontNode = node.querySelector('.front')
this.backNode = node.querySelector('.back')
this.setFrontTime(currentTime)
this.setBackTime(nextTime)
}
setFrontTime = function (time) {
let textNode = this.frontNode.querySelectorAll('.date')
Array.from(textNode).forEach((item) => {
item.innerText = time
})
}
setBackTime(time) {
let textNode = this.backNode.querySelectorAll('.date')
Array.from(textNode).forEach((item) => {
item.innerText = time
})
}
flipDown(currentTime, nextTime) {
if (this.isFlipping) {
return
}
this.isFlipping = true
// 设置变化前时间
this.setFrontTime(currentTime)
// 设置变化后时间
this.setBackTime(nextTime)
this.flipNode.classList.add('running')
let timer = setTimeout(() => {
this.flipNode.classList.remove('running')
this.isFlipping = false
// 设置变化前卡片为下一个时间
this.setFrontTime(nextTime)
clearTimeout(timer)
timer = null
}, this.duration)
}
}
// 格式化获取时间
function getTimeFromDate(date) {
return date.toTimeString().slice(3, 8).split(':')
}
// 获取时间显示框
let flips = document.querySelectorAll('.flip')
// 当前时间
let now = new Date()
let nowTimeStr = getTimeFromDate(new Date(now.getTime() - 1000))
let nextTimeStr = getTimeFromDate(now)
let flippers = Array.from(flips).map(function (flip, i) {
return new Flipper(flip, nowTimeStr[i], nextTimeStr[i])
})
setInterval(() => {
let now = new Date()
let nowTimeStr = getTimeFromDate(new Date(now.getTime() - 1000))
let nextTimeStr = getTimeFromDate(now)
for (let i = 0; i < flippers.length; i++) {
if (nowTimeStr[i] === nextTimeStr[i]) {
continue
}
flippers[i].flipDown(nowTimeStr[i], nextTimeStr[i])
}
}, 1000)