CSS 动画工程实践:关键帧、性能、取消与减少动态效果
本文目录
transition 适合状态之间插值;animation 可按关键帧描述多段样式变化,并配置重复、反向或结束状态。二者都不替代语义:按钮仍需真实 disabled/loading 状态,不能只靠透明度判断业务是否完成。
关键帧与状态
@keyframes 定义动画各时刻的样式。简写 animation 可设置名称、时长、缓动、延迟、重复次数、方向、填充模式和播放状态。复杂组件可拆开长属性,避免参数顺序难以读懂。
@keyframes notice-enter {
from {
opacity: 0;
transform: translateY(0.5rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.notice {
animation: notice-enter 180ms ease-out both;
}
forwards/both 让最终样式保留在动画后,组件移除、状态切换时要明确谁管理清理。避免无限循环和超大重复次数伪装成免费效果;长时间运动需要暂停或停止操作。
性能先测量
动画属性可能触发布局、绘制或合成。频繁改变宽高、布局位置、阴影和滤镜可能增加布局/绘制工作;transform 与 opacity 在常见情形更容易由合成阶段处理,但并非零成本。大面积模糊、过多图层和滥用 will-change 仍消耗显存和合成资源。
先用 Performance 与渲染诊断工具,再在目标低端设备上测量。限制并发动画层,避免每帧 JavaScript 同步读写布局。滚动动画和连续触发的组件要处理页面隐藏、重复启动及卸载。
事件与取消边界
animationend 在正常结束时触发;节点提前移除、动画被取消、样式被替换或 reduced-motion 下动画不运行时,它不会是可靠的唯一完成信号。事件会冒泡,应核对目标和动画名。可监听 animationcancel,并由业务状态或兜底逻辑覆盖动画没有启动的分支。动画不能成为提交、授权或验证的唯一触发点。
const panel = document.querySelector('.notice');
function onAnimationEnd(event) {
if (event.target !== panel || event.animationName !== 'notice-enter') return;
panel.dataset.visible = 'true';
}
panel.addEventListener('animationend', onAnimationEnd);
function disposePanel() {
panel.removeEventListener('animationend', onAnimationEnd);
}
若使用 Web Animations API 的完成 Promise,也要处理取消、reject、用户偏好和节点销毁。组件状态应能在没有动画的情况下直接到达最终结果。
尊重减少动态效果
一些用户会因缩放、平移、视差或闪烁而不适。使用 prefers-reduced-motion 为非必要效果提供静态或缩短版本;这不是一刀切关闭所有动效,需根据内容意义和用户控制权判断。避免每秒超过三次闪烁,并让运动信息同时由文本或图标表达。
@media (prefers-reduced-motion: reduce) {
.notice {
animation-duration: 1ms;
animation-iteration-count: 1;
scroll-behavior: auto;
}
.parallax-scene,
.decorative-loop {
animation: none;
transform: none;
}
}
验收清单
- 正常完成、连续触发、取消、卸载和减少动态效果下,业务状态均正确。
- 过滤子元素的结束事件,卸载时移除监听器。
- 检查长帧、布局/绘制成本,并用移动设备实际验证。
- 减少动态效果偏好下,内容和操作仍可理解、可完成。
- 持续运动可暂停,闪烁、视差和自动出现内容不会强迫用户观看。
参考:MDN CSS animations、MDN animation、MDN @keyframes、MDN animationcancel、MDN prefers-reduced-motion、web.dev animations guide、W3C WCAG 2.2。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。