用 body::before 伪元素实现帷幕效果,DOMContentLoaded 时添加类触发动画,transform: scaleY(0) 向上收起,z-index 需高于第三方组件,Safari 需强制重绘,样式与脚本应内联防 FOUC。

用 body 伪元素模拟帷幕,配合 DOMContentLoaded 控制显隐
网页加载完成时“帷幕揭开”效果,本质是用一个全屏遮罩层盖住页面,等 DOM 就绪后快速移除或动画收起。不用 JS 动态插入 DOM 元素,直接用 ::before 伪元素挂载在 body 上更轻量、无 FOUC 风险。
关键点在于:遮罩必须初始为 display: block(不能靠 opacity: 0 起家),且需设 z-index 高于所有内容;过渡必须只作用于 transform 或 opacity,避免触发布局重排。
-
body::before必须声明content: ""、position: fixed、inset: 0、background: #fff - 过渡属性写死为
transition: transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1),比opacity更顺滑,也避免白屏闪烁 - JS 触发时机用
document.addEventListener('DOMContentLoaded', ...),不是window.onload——后者等图片/资源,会延迟“揭开”
transform: scaleY(1) 向下拉开 vs scaleY(0) 向上收起的取舍
“揭开”方向影响用户感知:向下拉开(从顶部展开)更符合“帷幕”物理直觉;向上收起(从底部缩回)容易让底部内容突然弹入视野,有跳动感。
实操中统一用 transform: scaleY(1) 作为结束态,起始态设为 transform: scaleY(0) 并锚定顶部(transform-origin: top),这样动画全程只影响遮罩自身,不挤压页面流。
立即学习“前端免费学习笔记(深入)”;
- 若用
scaleY(0)+transform-origin: bottom,遮罩会从底部向上“吞掉”内容,视觉上像被吸走,不符合“揭开”语义 - 不要用
height: 0 → height: 100vh,会触发重排,动画卡顿,且 Safari 对height过渡支持不稳定 - 移动端需加
will-change: transform到伪元素,否则 iOS Safari 可能掉帧
兼容性陷阱:Safari 15.4+ 对 body::before 的渲染异常
Safari 15.4 到 16.1 之间有个 bug:body::before 在 DOMContentLoaded 触发瞬间可能未渲染,导致 JS 执行时找不到该元素,transform 无法生效,帷幕卡死。
绕过方式不是加延时,而是强制 Safari 重绘:在添加类名前,先读一次伪元素的 offsetHeight。
document.addEventListener('DOMContentLoaded', () => {
const curtain = document.body;
// 强制 Safari 重绘伪元素
getComputedStyle(curtain).opacity;
curtain.classList.add('curtain-open');
});
- 仅 Safari 需要这行,Chrome/Firefox 无此问题,但加了也无害
- 不要用
setTimeout(..., 0),不可靠,且破坏同步语义 - 如果项目用了 CSS-in-JS(如 Emotion),确保
body::before样式没被隔离或提升失效
首屏内容已渲染但 JS 加载慢时的降级处理
如果 JS 文件放在底部或被 defer,而用户网速差,可能出现“内容先闪一下,再被帷幕盖住又揭开”的闪烁。这不是 CSS 问题,是资源加载顺序失控。
解法是把帷幕逻辑内联进 <head>,哪怕只几行:
<style>
body::before {
content: "";
position: fixed;
inset: 0;
background: #fff;
z-index: 9999;
transition: transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1);
transform-origin: top;
}
body.curtain-open::before {
transform: scaleY(0);
}
</style>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.body.classList.add('curtain-open');
});
</script>
- 样式和脚本都内联,确保在 HTML 解析到
</head>时就位 - 避免用外部 CSS 文件控制帷幕状态,否则存在样式未加载完就执行 JS 的竞态
- 如果用了 SSR(如 Next.js),服务端不能输出
curtain-open类,否则首屏会直接无遮罩——必须纯客户端控制
z-index 必须高于所有第三方组件(比如弹窗库、广告 SDK 插入的 div),它们常硬编码 z-index: 2147483647。帷幕至少设成 z-index: 2147483648,不然会穿帮。










