css实现简单的翻转时钟效果
<h2>需求</h2><blockquote>
<p>时分秒为翻转卡片,与系统时间同步翻转</p>
</blockquote>
<h2>效果图</h2>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202305/2023052416085747.gif" /></p>
<h2>实现步骤</h2>
<h3>卡片翻转</h3>
<p>1.用四个div模拟翻转卡片</p>
<div class="jb51code">
<pre class="brush:xhtml;">
<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></pre>
</div>
<p>2. <code>.card1</code>和<code>.card2</code>表示下一个数,<code>.card</code>和.<code>card4</code>表示当前展示数字</p>
<blockquote>
<p><strong>初始状态:</strong></p>
<ul>
<li><code>card1</code>为下一个数的上半部分,<code>card2</code>为下半部分,<code>card2</code>翻转叠在<code>card1</code>上;</li>
<li><code>card3</code>为当前数的上半部分,<code>card4</code>为当前数的下半部分,<code>card3</code>叠在<code>card1</code>,<code>card2</code>上。</li>
</ul>
</blockquote>
<p style="text-align:center"><img alt="翻转卡片示意图.png" src="https://img.jbzj.com/file_images/article/202305/2023052416085748.png" /></p>
<blockquote>
<p><strong>翻转原理:</strong></p>
</blockquote>
<ul>
<li><code>card2</code>向下(面向自己)旋转到<code>0deg</code>,即卡片的下半部分;</li>
<li><code>card3</code>向下旋转到<code>-180deg</code>,即卡片的下半部分(背面对着自己);</li>
<li>card2与card3同步旋转</li>
<li>旋转结束后,card1和card2在上面(层级显示),card4被card3盖住,card3背对,card2正面显示</li>
</ul>
<p>3.搞明白div是如何翻转之后,设置css样式</p>
<p>数字分为上半部分和下半部分,以card1和card2为例。</p>
<div class="jb51code">
<pre class="brush:css;">
.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;
}</pre>
</div>
<p>2. 设置card2的初始状态</p>
<div class="jb51code">
<pre class="brush:css;">
backface-visibility: hidden; /* 背面不可见 */
z-index: 2; /* 调整层级,以免翻转时被card4盖住 */
transform-origin: center top; /* 旋转中心为卡片中心,即衔接的中心 */
transform:rotateX(180deg); /* 旋转, tops: 为了有3d效果,可以为父元素加perspective*/</pre>
</div>
<p>3. card2的旋转动画</p>
<div class="jb51code">
<pre class="brush:css;">
@keyframes flip {
to {
transform: rotateX(0deg);
}
}</pre>
</div>
<p>4. card3的旋转动画</p>
<p>类似的样式设置不再赘述。</p>
<div class="jb51code">
<pre class="brush:css;">
@keyframes flip1 {
to {
transform: rotateX(180deg);
}
}</pre>
</div>
<p>5. 为卡片加上无限动画,时间为1s</p>
<div class="jb51code">
<pre class="brush:css;">
/* backwards: 一次动画后,回到动画开始前的状态*/
animation: flip1 1s ease-in-out backwards infinite; </pre>
</div>
<p>至此,单个卡片的旋转就完成了,也就是时钟里<code>秒</code>部分结束。</p>
<h3>时分翻转</h3>
<blockquote>
<p>在前面卡片翻转中,设置动画持续时间为1s,正好对应秒表。但是,对于一分钟(一小时)更新一次的分表(时表),动画需要进行暂停以及有条件的开启。</p>
</blockquote>
<p>时分表和秒表具有相同的html结构以及css样式,以下是基于秒表结构的改变:</p>
<p>动画初始化为暂停状态</p>
<div class="jb51code">
<pre class="brush:css;">
animation-play-state: paused;</pre>
</div>
<p>2. 监听秒表动画的每一次结束,计算当前时间以及下一秒的时间,如果下一秒时分需要变化,则为其开启动画</p>
<div class="jb51code">
<pre class="brush:css;">
/* 获取时分秒的各个元素 */
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";
}
})</pre>
</div>
<p>3. 动画开启,执行一秒后,分/时表的动画需要再次暂停。为了代码不冗余,使用事件代理,对上述代码进行改造。1. 如果触发<code>animationiteration事件</code>的是秒表元素,则改变秒表卡片的数字,判断时分是否需要变化,需要变化则开启动画,对应changeAnimationState函数。2. 如果触发的是时/分表元素,则表示时/分表动画结束,需要暂停响应的动画,并改变卡片的数字</p>
<div class="jb51code">
<pre class="brush:js;">
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";
}
});</pre>
</div>
<div class="jb51code">
<pre class="brush:js;">
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";
}
}</pre>
</div>
<p>为了简单,设定全局元素<code>nowSecond, nextSecond, nowMinute, nextMinute, nowHour, nextHour</code></p>
<div class="jb51code">
<pre class="brush:js;">
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();
}</pre>
</div>
<p>至此,时/分/秒表的翻转功能基本上就实现了。</p>
<h3>时钟初始化</h3>
<blockquote>
<p>页面刚进入,需要获取当前时间进行渲染,监听<code>onload</code>事件</p>
</blockquote>
<div class="jb51code">
<pre class="brush:js;">
// 分别为时分秒卡片设置当前显示值,以及被覆盖的下一翻转显示值
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);
};</pre>
</div>
<p><strong>所有功能实现完毕!</strong></p>
<p>不足一共使用了12个卡片元素(<code>.card-item</code>),可能会有更好的解决翻转的方式分别获取了12个元素对应的dom,并对其innerHtml进行了修改(有点傻)卡片可以抽离为组件,思考:如何设计组件的接收属性,可以适配时/分/秒</p>
<p>翻转时钟完整代码见<a href="https://github.com/gmx-white/css-wheel" rel="nofollow" target="_blank">https://github.com/gmx-white/css-wheel</a></p>
頁:
[1]