纯 css 百叶窗切换必须用 clip-path + @keyframes 驱动单层遮罩动画,禁用多 dom 拼接或 transform 模拟,否则 safari 16.4 以下及旧版 chrome/移动端会失效卡顿;核心是通过 --i 动态注入条纹偏移,配合 inset() 实现响应式垂直百叶窗。

纯 CSS 实现页面级百叶窗切换,必须用 clip-path + @keyframes 驱动单层遮罩动画,不能靠多 DOM 元素拼接或 transform 模拟 —— 否则在 Safari 16.4 以下、旧版 Chrome 或移动端会直接失效或卡顿。
为什么不用多个 div 拼百叶窗条带
常见错误是把容器拆成 10 个 div,每个设 background-position 偏移再逐个 transform: scaleX(0) 展开。这会导致:
- DOM 节点爆炸,页面重排压力大,尤其在 iOS Safari 上频繁掉帧
- 响应式时条带宽度无法随容器缩放自动对齐(
calc()在伪类中不可用) - 两张图切换时,新图必须提前叠在旧图下方且完全对齐,否则出现错位白边
-
will-change: transform对大量元素启用后反而触发渲染异常
clip-path inset() 实现垂直百叶窗的最小可靠写法
适用于现代浏览器(Chrome 107+、Firefox 110+、Safari 16.4+),8 条竖向条纹为例:
body {
--blinds-count: 8;
--blinds-step: calc(100% / var(--blinds-count));
}
.page-enter {
clip-path: inset(0 calc(var(--i) * var(--blinds-step)) 0 calc((var(--i) + 1) * var(--blinds-step)));
animation: blinds-in 0.8s forwards;
}
@keyframes blinds-in {
0% { clip-path: inset(0 100% 0 0); }
12.5% { clip-path: inset(0 87.5% 0 0); }
25% { clip-path: inset(0 75% 0 0); }
/* …直到 100% { clip-path: inset(0 0 0 0); } */
}
注意:--i 必须通过 JS 注入每个条纹元素的 style 属性,或用 CSS 自定义属性配合 :nth-child() 伪类控制;硬写死百分比值会失去响应能力。
Safari 15.x 或 Chrome 98–106 的降级方案
这些版本不支持 clip-path 动画,但支持 transform + opacity 硬件加速。需改用绝对定位子容器:
- 父容器设
position: relative; overflow: hidden; - 每个条纹为
position: absolute;,宽 =100% / n,高 =100%,background-image相同但background-position-x错开 - 动画用
transform: translateX(-100%)→translateX(0),加transition-delay控制顺序 - 务必加
transform: translateZ(0)到父容器,强制 GPU 渲染 - 禁用
will-change: clip-path—— 它在 Safari 中会引发闪烁,毫无益处
两张页面内容真正切换的关键细节
很多人只做了遮罩动,忘了内容怎么换。正确做法是:
- 旧页面保持可见,新页面用
position: absolute; top: 0; left: 0; width: 100%; height: 100%;叠在其上,初始opacity: 0 - 触发切换时,先让新页面
opacity: 1,再立即启动clip-path动画 —— 否则动画过程中会看到旧页背景透出 - 动画结束后的
forwards状态必须确保clip-path: inset(0 0 0 0),否则后续交互可能被裁剪 - 若用 Vue/React,避免在动画中直接
v-if或key切换 DOM,应先挂载新节点再触发动画
最易忽略的是:clip-path 动画一旦开始,就不再响应容器尺寸变化 —— 所以所有计算必须基于初始渲染时的宽高,响应式切换需监听 resize 并重置动画状态。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











