@starting-style 可使 transition 在元素首次渲染时触发入场动画,但需浏览器支持(chrome/edge 117+,firefox 不支持),且须避免与 display: none 或插入即加 class 混用,否则动画跳过。

@starting-style 能让 transition 在元素首次渲染时就触发入场动画,但前提是浏览器支持(Chrome 117+,Edge 117+,Firefox 尚未支持),且不能和 display: none 或 DOM 插入后立即加 class 的写法混用——否则动画直接跳过。
为什么 transition 在元素一上来就失效
常见错误是这样写:
.card {
opacity: 0;
transform: scale(0.95);
transition: all 0.25s ease-out;
}
.card.show {
opacity: 1;
transform: scale(1);
}
然后 JS 动态插入:
const el = document.createElement('div');
el.className = 'card show';
document.body.append(el); // ❌ 动画不触发,直接显示
原因:浏览器在 append 的同一帧内完成样式计算和布局,.card 从无到有,没有“前一个样式状态”,transition 找不到起始值,直接渲染终态。
过去得靠 el.clientWidth 强制重排、或 setTimeout 延迟加 class——现在可以用 @starting-style 声明“它本该有的初始样子”。
@starting-style 必须配合 transition 属性使用
它本身不触发动画,只是提供一个“渲染前的快照”,让 transition 有据可依。关键点:
-
@starting-style里写的样式必须和元素最终进入时的“非活跃态”一致(比如opacity: 0、transform: scale(0)) - 目标状态(比如
.card.show)必须显式声明transition,且属性名要匹配 - 不能写成
@starting-style { .card.show { ... } }—— 它只作用于元素本身,不认 class 切换逻辑 - 若用
visibility: hidden → visible,@starting-style无效,因为visibility不参与过渡插值
正确写法示例:
.card {
transition: opacity 0.25s ease-out, transform 0.25s ease-out;
}
.card.show {
opacity: 1;
transform: scale(1);
}
@starting-style {
.card {
opacity: 0;
transform: scale(0.95);
}
}
动态创建元素时仍需注意 DOM 插入时机
即使用了 @starting-style,如果元素插入后立刻被 JS 修改了影响布局的属性(比如改了 innerHTML、加了 inline style),仍可能打断初始样式快照。稳妥做法:
- 先创建元素,设置 class(如
class="card"),再 append - 不要在 append 后立即调用
el.classList.add('show')—— 这会绕过@starting-style的初始上下文 - 推荐统一用 class 切换控制状态:
<div class="card"> → 后续 JS 只做 <code>el.classList.add('show'),让 CSS 自动接管 - 若必须 JS 控制入场,确保 class 添加发生在下一次样式计算之前(通常就是 append 后直接加,现代浏览器已能识别)
- 用
@supports (@starting-style) {}包裹增强样式,避免干扰旧环境 - 对不支持的浏览器,降级为
animation+@keyframes,或保留el.clientHeight触发方式 - 特别注意 SSR 场景:服务端不解析
@starting-style,首屏直出的元素不会带初始动画,需客户端 hydrate 后补触发
兼容性兜底必须手动处理
@starting-style 目前仅 Chromium 系列支持(截至 2026 年 10 月),Safari 和 Firefox 仍忽略该规则。实际项目中不能只依赖它:
最易被忽略的一点:它只对「首次渲染」有效。元素后续反复 show/hide,@starting-style 不再介入——那部分得靠常规 transition 或 animation 控制。











