transition声明必须写在基础选择器里,否则reduce后仍会动;正确做法是将transition置于元素基础规则中,再用@media (prefers-reduced-motion: reduce)统一设为none,并覆盖transform、opacity等所有可能引发运动的属性。

transition声明必须写在基础选择器里,否则reduce后仍会动
很多人把 transition 写在 :hover 或媒体查询块里,以为加了 @media (prefers-reduced-motion: reduce) 就能关掉——结果发现悬停时动画还在。根本原因是:浏览器只在起始态和结束态都匹配同一组 transition 声明时,才启用插值;如果 transition 只出现在 :hover 中,基础态没声明,那移出瞬间直接跳变,prefers-reduced-motion 也救不了。
正确做法是把 transition 放在元素的基础规则中,再用媒体查询统一覆盖:
.card {
opacity: 1;
transform: translateY(0);
transition: opacity 0.2s, transform 0.3s ease-out;
}
@media (prefers-reduced-motion: reduce) {
.card {
transition: none;
}
}
-
transition: none比transition: opacity 0s更可靠,后者在某些旧 Safari 中仍有微弱插值 - 别漏掉
transform—— 它常被用作高性能位移,但也是动画源之一 - 如果用了 JS 切换 class(如
.card.is-expanded),transition同样必须写在.card基础规则里,不能只写在.is-expanded
哪些transition属性必须一并禁用
prefers-reduced-motion: reduce 不是“关动画开关”,而是告诉浏览器“用户明确要求减少运动”,所以你要主动拦截所有可能触发视觉运动的 CSS 属性。光关 animation 不够,transition 的常见目标属性也得清空:
-
transform:平移、缩放、旋转——最常用也最容易被忽略 -
opacity:渐隐渐显虽轻量,但快速闪烁仍可能触发眩晕 -
width/height:transition这俩会重排,且在 reduce 模式下更易感知抖动 -
margin/padding:同上,布局变化比颜色更干扰 - 别忘了
box-shadow和filter:模糊、阴影扩散也算运动类动效
稳妥写法是统一设 transition: none,而不是逐个写 transition-property: none —— 后者在部分浏览器中不生效。
立即学习“前端免费学习笔记(深入)”;
需要保留反馈时怎么写过渡
完全去掉所有过渡会削弱交互反馈,尤其按钮按压、菜单展开这类操作。这时可以保留极简、低风险的视觉变化:
- 只用
opacity+ 极短时间:比如transition: opacity 0.1s,避免多属性叠加 - 禁用
visibility: hidden配合opacity动画——两者同时过渡会导致渲染异常;改用 JS 控制visibility立即切换,仅让opacity动画 - 慎用
opacity: 0→opacity: 1:哪怕 0.1s,也可能被识别为闪烁;更安全的是opacity: 0.95→opacity: 1 - 不要用
height: auto过渡:起止值不可计算,浏览器会 fallback 到 jump,prefers-reduced-motion无法干预
JS监听和class切换要不要做
纯 CSS 方案(@media (prefers-reduced-motion: reduce))已覆盖绝大多数场景,但如果你的动效依赖 JS 控制(比如轮播、手风琴菜单、模态框淡入),就得配合 window.matchMedia:
const motionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
document.documentElement.classList.toggle('reduced-motion', motionQuery.matches);
motionQuery.addEventListener('change', e => {
document.documentElement.classList.toggle('reduced-motion', e.matches);
});
- 推荐用
document.documentElement加 class,方便全局控制:比如.reduced-motion .carousel { animation: none; } - 别只监听一次——系统设置可能在页面运行中更改,需绑定
change事件 - 移动端真机测试必不可少:iOS 设置路径是「设置 > 辅助功能 > 动作 > 减少动态效果」,Android 各厂商路径不同,Chrome DevTools 的 Rendering 面板仅模拟,不等价
真正容易被忽略的,是那些没写 transition 却靠 JS 直接改 style.transform 的动画——它们不受 @media 影响,必须靠 JS 检查 matchMedia 结果后跳过执行。


















