@starting-style 仅在元素首次渲染且无任何样式时生效,必须配合animation使用;它不是独立样式规则,而是动画启动前的初始快照,仅Chromium 119+支持。

为什么 @starting-style 不能直接用在普通元素上?
@starting-style 不是给任意元素加个 class 就能触发的“动画开关”。它只在元素首次渲染且尚未应用任何样式(包括内联 style、CSS 类、JS 设置)时生效,且必须配合 animation 使用——不是作为独立样式规则,而是作为动画启动前的“初始快照”。
常见错误现象:@starting-style 写了但动画没从设定值开始,比如设了 opacity: 0 却看到元素直接显示再淡入。原因通常是:
- 元素已有 class 或内联
style,导致浏览器跳过 starting-style 阶段 - 动画未声明
animation-fill-mode: both,导致动画结束后样式回退,掩盖了 starting-style 的作用 - 在 React/Vue 等框架中,组件挂载时 DOM 已带样式,
@starting-style被忽略
怎么写一个真正生效的入场动画?
关键在于“零样式起点 + 动画接管”。以淡入+缩放为例:
@starting-style {
opacity: 0;
transform: scale(0.95);
}
<p>.my-element {
animation: fadeInScale 0.3s cubic-bezier(0.2, 0, 0.38, 0.9);
animation-fill-mode: both;
}</p><p>@keyframes fadeInScale {
to {
opacity: 1;
transform: scale(1);
}
}使用场景注意点:
立即学习“前端免费学习笔记(深入)”;
-
@starting-style必须放在@keyframes和动画规则之前(CSS 解析顺序敏感) - 只能声明那些会被动画
@keyframes覆盖的属性;声明color却在动画里只改opacity,该 color 不生效 - 不支持简写属性(如
margin),必须用margin-top、margin-bottom等展开写 - 目前仅 Chromium 119+(Chrome/Edge)支持,Firefox 和 Safari 完全不识别,需 fallback
React 中怎么避免被 SSR 或 hydration 破坏?
服务端渲染或 hydrate 后,元素往往已带 class 或 style,@starting-style 失效。可行方案:
- 用
useEffect延迟添加动画 class(让元素先“裸”渲染一次):const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); return <div className={mounted ? "my-element" : ""} /> - 或更稳妥:用
requestAnimationFrame在下一帧移除干扰样式:useEffect(() => { requestAnimationFrame(() => { element?.classList.add("my-element"); }); }, []); - 不要依赖
useState初始 false → true 的切换来“模拟首次渲染”,因为 DOM 节点可能已在 SSR 中存在样式
和 animation-delay: -1ms 这类 hack 比有什么区别?
animation-delay: -1ms 是强行让动画从第 0 帧开始播放,但它无法解决“初始样式被 JS 或 SSR 提前写死”的问题;而 @starting-style 是浏览器原生提供的样式层隔离机制,确保动画起始值不受外部样式污染。
性能影响很小,但兼容性是硬伤:
- 若需支持 Safari,必须提供
@keyframesfallback 并禁用@starting-style区块(可用@supports (animation: name 1s) and (not (starting-style: {}))检测) - 不要把它当作通用“隐藏→显示”方案,它只对 CSS 动画驱动的首次渲染 有效;JS 控制显隐仍得靠 class 切换或
display
真正容易被忽略的是:这个特性不是“让动画更好看”,而是“让动画起始状态可控”。一旦 DOM 有任一外部样式介入,它就失效——所以检查元素 computed style 的第一帧,比写十行 CSS 更重要。


















