过渡失效主因是transition未持久挂载或属性不可动画,应将其抽离至基础类,状态类仅控制值;display/visibility隐藏致快照为空;background-image等需严格写法;样式覆盖需查Computed面板;DOM更新后需强制重排。

过渡效果失效,90%不是浏览器不支持,而是 transition 声明没“挂住”或属性根本不可动画——修法很简单:把 transition 拆出来,固定在基础类里,别让它随状态 class 一起被删掉。
transition 写在被移除的 class 里就失效
这是最隐蔽也最高频的问题。比如你写:
.box-collapsed { opacity: 0; transition: opacity 0.3s ease; }
.box-expanded { opacity: 1; }
然后用 JS 切换:el.classList.remove('box-collapsed'); el.classList.add('box-expanded'); —— 动画直接跳变。
-
transition必须存在于元素“整个过渡周期”的计算样式中,移除含它的 class 就等于拔掉动画引擎的电源 - 正确做法是抽离到一个永不被移除的基础类,例如
.box,里面只写transition: opacity 0.3s ease - 状态类(如
.box--expanded)只管改opacity、transform等值,不碰transition
用了 display: none 或 visibility: hidden 就拍不到快照
View Transitions API 或某些 JS 驱动的切换逻辑,依赖浏览器对“旧状态”的视觉捕获。如果旧元素在 document.startViewTransition() 调用前已不可见,快照就是空的。
立即学习“前端免费学习笔记(深入)”;
-
display: none和visibility: hidden会让元素彻底退出渲染树,快照里没有它 - 哪怕
opacity: 0.001+pointer-events: none,也比隐藏强;它仍参与布局和快照 - 确保
view-transition-name在新旧 DOM 节点上完全一致(大小写、空格、动态生成的 ID 都算错) - 刚
createElement出来的节点,必须先append()到 document,再设view-transition-name
background-image / filter / border 这些属性容易踩坑
它们不是“不能动”,而是对写法极度敏感:
-
background-image:JS 直接赋值el.style.backgroundImage = 'url(...)'会绕过 CSS transition;必须用 class 控制,且 CSS 中显式声明transition: background-image 0.3s -
filter:Chrome/Edge 要求两端结构一致,比如filter: grayscale(100%) blur(0px)→filter: grayscale(0%) blur(2px);混用none或漏函数名就跳变 -
border:复合属性border不可过渡;得拆成border-color、border-width单独声明,且border-style(如solid)必须两端一致
样式注入冲突导致 transition 被覆盖
第三方脚本、UI 库或内联 style 可能悄悄重写你的 transition,尤其在混合开发或微前端场景下。
- 用浏览器 DevTools 的 Computed 面板,直接查元素最终生效的
transition值是否被 override - 避免用
!important修复,优先提升选择器权重,比如从.btn改为#app .btn - 真正隔离样式只能靠 Shadow DOM:
el.attachShadow({ mode: 'closed' })后注入CSSStyleSheet,否则全局样式永远有穿透风险 - 纯 HTML 环境下没有“CSS Module”,
import './x.module.css'会静默失败,别信构建产物文档
最容易被忽略的,是“过渡起点”其实取决于 DOM 更新时机——JS 批量改样式后不强制重排,浏览器就合并渲染,起始帧压根没被记录。所以关键操作前加一句 void el.offsetHeight,不是玄学,是告诉浏览器:“停,先拍一帧”。



















