尊重「减少动态效果」
操作系统提供了一个无障碍偏好:减少动态效果。开启它的用户往往对大幅运动敏感——前庭功能障碍者会因屏幕上的滑动、缩放、旋转产生眩晕与恶心。Web 端通过 matchMedia('(prefers-reduced-motion: reduce)') 读到这一偏好。
1 · 同一个入场的两种走法
2 · 降级不等于静止
开启这个偏好的用户依然需要知道发生了什么。直接删掉过渡会让界面状态突变、丢失因果线索。正确做法是保留必要反馈——淡入、颜色变化、短促的 opacity 过渡——只移除让人眩晕的大幅运动:长距离 translate、整体 scale、rotate、视差。
本页给了两档降级策略:退化为温和的 opacity 淡入,或对极敏感的循环与抖动动画直接瞬时到位。
注 · 页内的模拟开关与系统偏好取「或」而非「覆盖」。这不只是为了演示方便:真实产品里通常也会给用户一个应用内的动效开关,而它应当只能加强限制、不能推翻系统设置——用户在系统里说了「减少动态」,应用不该用自己的开关把它打开。
3 · 偏好会在运行时变化
matchMedia(query) 返回的 MediaQueryList 不只能即时读 .matches,还能监听 change 事件。用户在系统设置里切换偏好时事件会触发,界面应据此即时调整后续动画——而不是只在页面加载时读一次。
import { Animation, easingFns } from '@vega/anim';
const mql = matchMedia('(prefers-reduced-motion: reduce)');
function entrance() {
if (!mql.matches) { // 完整动态: 大幅运动
new Animation({
keyframes: [0, 1], duration: 720, ease: easingFns.backOut,
onUpdate: (p) => {
card.style.transform =
`translateY(${(1 - p) * -120}px) scale(${0.4 + p * 0.6}) rotate(${(1 - p) * -180}deg)`;
},
});
return;
}
// 减少动态: 去掉位移/缩放/旋转, 退化为温和淡入 (或直接 set 到位)
new Animation({
keyframes: [0, 1], duration: 360, ease: easingFns.easeOut,
onUpdate: (o) => { card.style.transform = 'none'; card.style.opacity = o; },
});
}
mql.addEventListener('change', entrance); // 偏好变化即时响应
entrance();