说在前面
🎈大家在做页面的时候,loading 基本都绕不开,转圈圈我们看多了,今天就来实现一个不一样的正方体翻滚 loading 动画。
效果展示
codepen
码上掘金
在线体验:code.juejin.cn/pen/7638926…
实现代码
1、先搭好结构
轨道 + 立方体 + 阴影 + 进度条
先把 DOM 骨架搭起来,核心是 .cube 里放 3 个面(正面、右侧、顶部),再加阴影和进度条区域。
<div class="loader" aria-label="正方体原地向右翻滚 loading">
<span class="track"></span>
<span class="shadow"></span>
<span class="contact-shadow"></span>
<div class="cube">
<span class="face front"></span>
<span class="face right"></span>
<span class="face top"></span>
</div>
<div class="progress-wrap">
<span class="progress-label">Loading</span>
<span class="progress-rail"><span class="progress-fill" id="progress-fill"></span></span>
<span class="progress-num" id="progress-num">0%</span>
</div>
</div>
这里的关键点:
.loader提供perspective,让 3D 变换有空间感。.cube开启transform-style: preserve-3d,子元素才能保持 3D 位置。- 轨道和阴影分层,后面动画同步会更自然。
2、做出立方体
3 个面拼出“可读”的 3D 体块
立方体不是一整块图片,而是 3 个面通过 translateZ / rotateX / rotateY 拼出来。
.cube {
width: 72px;
height: 72px;
transform-style: preserve-3d;
transform: translateY(0px) rotateX(-8deg) rotateY(8deg) rotateZ(0deg);
}
.front { transform: translateZ(36px); }
.right { transform: rotateY(90deg) translateZ(36px); }
.top { transform: rotateX(90deg) translateZ(36px); }
为什么只放 3 个面?
- 这个视角里,主要可见面就是 front/right/top。
- 少渲染几个面,性能更稳。
- 再配合渐变和内阴影,视觉上已经足够“像一个金属方块”。
3、做滚动节奏
关键帧里“抬起 + 落地 + 旋转 90°”
最关键的是 @keyframes roll-right。每次翻滚都不是匀速平转,而是:
- 抬起(
translateY(-14px)) - 旋转到下一边(
rotateZ +90deg) - 落地停一下(让节奏更像“滚”)
@keyframes roll-right {
0% { transform: translateY(0px) rotateX(-8deg) rotateY(8deg) rotateZ(0deg); }
11% { transform: translateY(-14px) rotateX(-8deg) rotateY(8deg) rotateZ(46deg); }
22% { transform: translateY(0px) rotateX(-8deg) rotateY(8deg) rotateZ(92deg); }
28% { transform: translateY(0px) rotateX(-8deg) rotateY(8deg) rotateZ(90deg); }
100% { transform: translateY(0px) rotateX(-8deg) rotateY(8deg) rotateZ(360deg); }
}
这套百分比节点的作用,就是让动画从“转圈圈”变成“有重心变化的翻滚”。
4、阴影同步
让“重量感”真实起来
只做方块旋转会有点“飘”,所以再加两层阴影:
- (1)
shadow:大阴影,模拟整体投影。 - (2)
contact-shadow:接触阴影,强调落地那一瞬间。
@keyframes shadow-sync {
0%, 22%, 28%, 47%, 53%, 72%, 78%, 97%, 100% { transform: scaleX(0.9); opacity: 0.5; }
11%, 37.5%, 62.5%, 87.5% { transform: scaleX(0.78); opacity: 0.34; }
}
@keyframes contact-sync {
0%, 22%, 28%, 47%, 53%, 72%, 78%, 97%, 100% { transform: scaleX(0.98); opacity: 0.6; }
11%, 37.5%, 62.5%, 87.5% { transform: scaleX(0.74); opacity: 0.24; }
}
当正方体“抬起”时阴影变小、变淡;落地时恢复,这样肉眼会觉得它是有重量的。
5、进度条逻辑
这里只是模拟一下进度加载过程,实际还要根据业务接口返回的具体进度来进行渲染
这里我拆成两段:
- (1)快速阶段:12 秒冲到 80%。
- (2)慢速阶段:每 2 秒 +1%,直到 100%。
const FAST_TARGET = 80;
const FAST_DURATION_MS = 12000;
const SLOW_STEP_MS = 2000;
const startFastPhase = () => {
const start = performance.now();
const tick = (now) => {
const t = Math.min(1, (now - start) / FAST_DURATION_MS);
value = Math.floor(t * FAST_TARGET);
render(value);
if (t < 1) requestAnimationFrame(tick);
else startSlowPhase();
};
requestAnimationFrame(tick);
};
源码地址
gitee
github
🌟 觉得有帮助的可以点个 star~
🖊 有什么问题或错误可以指出,欢迎 pr~
📬 有什么想要实现的功能或想法可以联系我~
公众号
关注公众号『 前端也能这么有趣 』,获取更多有趣内容。
发送 加群 还可以加入群聊,一起来学习(摸鱼)吧~
说在后面
🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。