为何在 pageswap 中设置 viewTransitionName 后跨文档动画仍然存在?
结论
跨文档 View Transition 根据捕获时的页面渲染状态生成旧页面快照。页面加载后已经完成的 DOM 修改也会反映在快照中,浏览器并非固定使用首次加载时的 DOM。pageswap 是修改旧页面快照状态的时机,在事件处理函数中同步修改样式可以影响随后捕获的快照。Chrome 跨文档过渡文档
需要注意 none 的含义:view-transition-name: none 会取消元素的独立快照,但元素仍可能包含在 root 或其他祖先元素的快照中,随那份快照一起动画。仅凭当前代码,无法确定观察到的效果由哪一层快照产生。
为什么设置 none 后仍然会动?
浏览器默认给文档根元素设置 view-transition-name: root,用于页面整体过渡。为 main 设置独立名称后,它可以拥有自己的过渡快照;改为 none 后,它通常会重新成为根快照的一部分。
例如:
main {
view-transition-name: none;
}
即使 main 没有独立的过渡快照,它仍可能随着整页快照淡入淡出。因此,看到动画不能说明样式修改没有生效。view-transition-name 说明
还要检查目标页面:pageswap 中的代码只修改即将离开的文档。如果新页面的 main 仍有独立名称,就可能产生只有新状态的入场动画。新旧两侧名称不一致时,对应内容也可能参与动画。CSS View Transitions 规范
先确认修改是否生效
在原有 pageswap 处理函数中,紧接赋值语句加入日志:
main.style.viewTransitionName = 'none';
console.log({
mainName: getComputedStyle(main).viewTransitionName,
rootName: getComputedStyle(document.documentElement).viewTransitionName
});
排查时按以下顺序检查:
- 确认处理函数实际执行,且
e.viewTransition存在。 - 确认选中的是预期的
main,其计算样式为none。如果不是,检查是否选错元素、是否受到!important影响,或是否被后续代码覆盖。 - 检查
main的祖先、后代以及新页面是否还设置了其他过渡名称。 - 结合
::view-transition-group(root)、::view-transition-old(...)、::view-transition-new(...)的样式,确认可见动画属于哪份快照。
同文档过渡中的 none 也遵循相同语义。如果两种场景的效果不同,需要结合两侧样式和实际参与捕获的元素判断。
如何让 main 不显示过渡动画?
如果希望其他区域保留动画,让 main 直接切换,可以让它保持独立快照,再禁用该快照组的动画。以下示例需要放在两个参与过渡的页面中,并替换原先将 main 名称改为 none 的逻辑:
@view-transition {
navigation: auto;
}
main {
view-transition-name: main-content;
}
::view-transition-group(main-content),
::view-transition-old(main-content),
::view-transition-new(main-content) {
animation: none;
}
::view-transition-old(main-content),
::view-transition-new(main-content) {
mix-blend-mode: normal;
}
这段代码关闭了快照组的位置、尺寸插值动画,以及新旧内容的淡入淡出。mix-blend-mode: normal 用于取消默认快照混合效果。内容或布局有差异时会直接切换;如果 main 的后代还有独立过渡名称,也需要分别处理。
如果只想让显式命名的区域参与过渡,也可以在两个页面中设置:
:root {
view-transition-name: none;
}
这会取消默认整页快照,影响所有依赖该快照的内容,并不限于 main。
如果想跳过本次导航的整个视图过渡,可以直接使用:
window.addEventListener('pageswap', (e) => {
if (e.viewTransition) {
e.viewTransition.skipTransition();
}
});
快照时机与恢复样式的注意事项
影响旧快照的修改应在 pageswap 处理函数中同步完成,不要先等待网络请求或定时器。需要动态调整新页面的名称时,应在目标文档的 pagereveal 中处理,并确保监听器在首次渲染机会之前注册。
原代码在旧页面中使用 e.viewTransition.finished 恢复样式,这是官方示例采用的清理方式之一。不过,该 Promise 不能用来判断新页面动画何时结束:旧文档隐藏后,它就会兑现。Chrome 事件与 Promise 说明
将值赋为 '' 会移除对应的内联声明。如果修改前已有内联值,应先保存再恢复。目前没有完整 CSS、目标页面代码和浏览器信息,无法据此认定是浏览器实现问题。
备注:内容仅供参考。
