注册
web

html翻页时钟 效果

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Flip Clock</title>
<style>
body {
background: #111;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
font-family: 'Courier New', monospace;
color: white;
}

.clock {
display: flex;
gap: 20px;
}

.card-container {
width: 80px;
height: 120px;
position: relative;
perspective: 500px;
background: #2c292c;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
}

/* 中间分割线 */
.card-container::before {
content: "";
position: absolute;
left: 0;
top: 50%;
width: 100%;
height: 4px;
background: #120f12;
z-index: 10;
}

.card-item {
position: absolute;
width: 100%;
height: 50%;
left: 0;
overflow: hidden;
background: #2c292c;
color: white;
text-align: center;
font-size: 64px;
font-weight: bold;
backface-visibility: hidden;
transition: transform 0.4s ease-in-out;
}

/* 下层数字:初始对折(背面朝上) */
.card1 { /* 下层上半 */
top: 0;
line-height: 120px; /* 整体高度对齐 */
}
.card2 { /* 下层下半 */
top: 50%;
line-height: 0;
transform-origin: center top;
transform: rotateX(180deg);
z-index: 2;
}

/* 上层数字:当前显示 */
.card3 { /* 上层上半 */
top: 0;
line-height: 120px;
transform-origin: center bottom;
z-index: 3;
}
.card4 { /* 上层下半 */
top: 50%;
line-height: 0;
z-index: 1;
}

/* 翻页动画触发 */
.flip .card2 {
transform: rotateX(0deg);
}
.flip .card3 {
transform: rotateX(-180deg);
}

/* 冒号分隔符 */
.colon {
font-size: 64px;
display: flex;
align-items: center;
color: #aaa;
}
</style>
</head>
<body>
<div class="clock">
<div class="card-container flip" id="hour" data-number="00">
<div class="card1 card-item">00</div>
<div class="card2 card-item">00</div>
<div class="card3 card-item">00</div>
<div class="card4 card-item">00</div>
</div>

<div class="colon">:</div>

<div class="card-container flip" id="minute" data-number="00">
<div class="card1 card-item">00</div>
<div class="card2 card-item">00</div>
<div class="card3 card-item">00</div>
<div class="card4 card-item">00</div>
</div>

<div class="colon">:</div>

<div class="card-container flip" id="second" data-number="00">
<div class="card1 card-item">00</div>
<div class="card2 card-item">00</div>
<div class="card3 card-item">00</div>
<div class="card4 card-item">00</div>
</div>
</div>

<script>
function setHTML(dom, nextValue) {
const curValue = dom.dataset.number;
if (nextValue === curValue) return;

// 更新 DOM 结构:下层为新值,上层为旧值
dom.innerHTML = `
<div class="card1 card-item">${nextValue}</div>
<div class="card2 card-item">${nextValue}</div>
<div class="card3 card-item">${curValue}</div>
<div class="card4 card-item">${curValue}</div>
`
;

// 触发重绘以重启动画
dom.classList.remove('flip');
void dom.offsetWidth; // 强制重排
dom.classList.add('flip');

dom.dataset.number = nextValue;
}

function updateClock() {
const now = new Date();
const h = String(now.getHours()).padStart(2, '0');
const m = String(now.getMinutes()).padStart(2, '0');
const s = String(now.getSeconds()).padStart(2, '0');

setHTML(document.getElementById('hour'), h);
setHTML(document.getElementById('minute'), m);
setHTML(document.getElementById('second'), s);
}

// 初始化
updateClock();
// setTimeout(updateClock,1000)

setInterval(updateClock, 1000);
</script>
</body>
</html>


在这里插入图片描述
这个翻页时钟(Flip Clock)通过 CSS 3D 变换 + 动画类切换 + DOM 内容动态更新 的方式,模拟了类似机械翻页牌的效果。下面从结构、样式和逻辑三方面详细分析其实现原理:




🔧 一、HTML 结构设计


每个时间单位(小时、分钟、秒)由一个 .card-container 容器表示,内部包含 4 个 .card-item 元素


<div class="card-container" id="second">
<div class="card1">00</div> <!-- 下层上半 -->
<div class="card2">00</div> <!-- 下层下半(初始翻转180°)-->
<div class="card3">00</div> <!-- 上层上半(当前显示)-->
<div class="card4">00</div> <!-- 上层下半 -->
</div>

四个卡片的作用:



  • .card3.card4:组成当前显示的数字(上半+下半),正常显示。
  • .card1.card2:组成即将翻出的新数字,但初始时 .card2rotateX(180deg) 翻转到背面(不可见)。
  • 中间有一条 ::before 伪元素作为“折痕”,增强翻页视觉效果。



🎨 二、CSS 样式与 3D 翻转原理


关键 CSS 技术点:


1. 3D 空间设置


.card-container {
perspective: 500px; /* 创建 3D 视角 */
}


  • perspective 让子元素的 3D 变换有景深感。

2. 上下两半的定位与旋转轴


.card2 {
transform-origin: center top;
transform: rotateX(180deg); /* 初始翻到背面 */
}
.card3 {
transform-origin: center bottom;
}


  • .card2顶部边缘旋转 180°,藏在下方背面。
  • .card3底部边缘旋转,用于向上翻折。

3. 翻页动画(通过 .flip 类触发)


.flip .card2 {
transform: rotateX(0deg); /* 展开新数字下半部分 */
}
.flip .card3 {
transform: rotateX(-180deg); /* 当前数字上半部分向上翻折隐藏 */
}


  • 动画持续 0.4s,使用 ease-in-out 缓动。
  • .card1.card4 始终保持静态,作为背景支撑。


视觉效果



  • 上半部分(.card3)向上翻走(像书页翻开)
  • 下半部分(.card2)从背面转正,露出新数字
  • 中间的“折痕”让翻页更真实




⚙️ 三、JavaScript 动态更新逻辑


核心函数:setHTML(dom, nextValue)


步骤分解:



  1. 对比新旧值:如果相同,不更新(避免无谓动画)。
  2. 重写整个容器的 HTML

    • 下层(新值).card1.card2 显示 nextValue
    • 上层(旧值).card3.card4 显示 curValue


  3. 触发动画
    dom.classList.remove('flip');
    void dom.offsetWidth; // 强制浏览器重排(关键!)
    dom.classList.add('flip');


    • 先移除 .flip,再强制重排(flush styles),再加回 .flip,确保动画重新触发。


  4. 更新 data-number 保存当前值。

时间更新:



  • 每秒调用 updateClock(),获取当前时分秒(两位数格式)。
  • 分别调用 setHTML 更新三个容器。



🌟 四、为什么能实现“翻页”错觉?


元素初始状态翻页后状态视觉作用
.card3显示旧数字上半向上翻转 180° 隐藏模拟“翻走”的上半页
.card2旧数字下半(翻转180°藏起)转正显示新数字下半模拟“翻出”的下半页
.card1 / .card4静态背景不变提供视觉连续性


💡 关键技巧



  • 利用 两个完整数字(新+旧)叠加,通过控制上下半部分的旋转,制造“翻页”而非“淡入淡出”。
  • 强制重排(offsetWidth 是确保 CSS 动画每次都能重新触发的经典 hack。




✅ 总结


这个 Flip Clock 的精妙之处在于:



  1. 结构设计:4 个卡片分工明确,上下层分离。
  2. CSS 3D:利用 rotateX + transform-origin 实现真实翻页。
  3. JS 控制:动态替换内容 + 巧妙触发动画。
  4. 性能优化:仅在值变化时更新,避免无效渲染。

这是一种典型的 “用 2D DOM 模拟 3D 物理效果” 的前端动画范例,既高效又视觉惊艳。


作者:大时光
来源:juejin.cn/post/7606183276772999231

0 个评论

要回复文章请先登录注册