用 transform 和 opacity 在合成器上做动画、组织好你的 @keyframes,并尊重用户的动效偏好。
在 Kodokon 中打开本课在选择动画什么之前,先回顾一下渲染管线:布局(几何)、绘制(像素)、合成(在 GPU 上组装图层)。对 width 或 top 做动画会在每一帧触发一次布局,往往紧接着一次绘制:这正是掉到 60 fps 以下、产生卡顿的配方。transform 和 opacity 由合成器直接处理,无需再经过前两个阶段。专业法则:尽可能只对这两个属性做动画,其余的都用它们来模拟(用 translate 代替 top,用 scale 代替 width)。
.button {
transform: translateY(0);
transition:
transform 0.2s cubic-bezier(0.2, 0.8, 0.2, 1),
opacity 0.15s ease-out;
}
.button:hover {
transform: translateY(-2px);
}
.button:active {
transform: translateY(0) scale(0.97);
}当过渡不再够用时(中间步骤、循环、自动开始),就转向 @keyframes。有两个设置决定了一段动画是干净利落还是有视觉 bug:animation-fill-mode 控制开始之前的状态(backwards 在延迟期间应用第一个关键帧)和结束之后的状态(forwards 冻结最后一个关键帧);both 则两者兼备。还要注意,时序函数是应用在每一对关键帧之间,而不是覆盖总时长:你可以在某个关键帧内部重新定义它。
@keyframes slide-in {
from {
transform: translateX(-100%);
opacity: 0;
}
70% {
transform: translateX(8px);
opacity: 1;
}
to {
transform: translateX(0);
}
}
.toast {
animation: slide-in 0.35s ease-out both;
}有两个值得了解的互补工具。will-change: transform 会在动画开始前把元素提升到它自己的图层上,避免一次迟到的栅格化;但每个图层都会消耗 GPU 内存,所以要在动画即将开始前才应用它、之后再移除,绝不要把它作为永久样式放在几十个元素上。steps(n) 用离散的跳跃取代连续插值:对精灵图、打字机式光标或时钟来说不可或缺。
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}transform: translateX(100px) 而非 left: 100px?transform 对旧版浏览器的支持更好transform 由合成器处理,无需在每一帧重新计算布局left 只对绝对定位元素有效,而 transform 到处都能用transform 接受负值,而 left 不接受animation-fill-mode: both 有什么作用?will-change 的正确用法是什么?* 上全局应用,以加速整个页面transform: translateZ(0) 搭配使用transition