缓动函数必须通过CSS的transition-timing-function或animation-timing-function实现,推荐使用cubic-bezier()自定义贝塞尔曲线以确保跨浏览器一致性与精确控制。

缓动函数必须用 transition-timing-function 或 animation-timing-function
HTML5 动画本身不提供“缓动函数”这个独立 API,所有缓动效果都依赖 CSS 的时间函数控制。直接写 ease、ease-in-out 是最常见做法,但真正可控的方案是用 cubic-bezier() 自定义贝塞尔曲线。
关键点:
-
transition和@keyframes animation都支持timing-function,但作用对象不同:前者控制整个过渡过程,后者只作用于单个@keyframes规则(比如0%→100%) -
浏览器默认的
ease等价于cubic-bezier(0.25, 0.1, 0.25, 1),不是线性,也不是对称缓入缓出 - 如果在
@keyframes中写了多个关键帧(如0%、50%、100%),每个区间都需单独指定缓动——CSS 不会自动插值跨段
用 cubic-bezier() 替代预设关键词更可靠
预设值如 ease-in 在不同浏览器渲染略有差异,而 cubic-bezier(x1, y1, x2, y2) 是 W3C 标准,行为一致。x 坐标必须在 [0,1] 区间,y 坐标可超出(实现“回弹”或“过冲”效果)。
常用自定义示例:
.bounce-in {
animation: slideIn 0.6s cubic-bezier(0.215, 0.61, 0.355, 1);
}
@keyframes slideIn {
from { transform: translateY(30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}注意:
- y 值为负数可实现“先反向再前进”的抽搐感(如
cubic-bezier(0.5, -0.5, 0.5, 1.5)) - 两个控制点完全相同(如
cubic-bezier(0.5, 0.5, 0.5, 0.5))等价于linear - 不要用 JS 动态拼接
cubic-bezier()字符串来改样式——重排重绘开销大,优先用 CSS 变量或预设 class 切换
JS 控制动画时,别绕过 CSS timing-function 直接用 requestAnimationFrame 手写缓动
除非你有特殊需求(比如物理模拟、逐帧音频同步),否则纯 JS 实现缓动既难调试又难维护。现代浏览器对 CSS 缓动优化极好,GPU 加速也更稳定。
立即学习“前端免费学习笔记(深入)”;
如果必须用 JS 控制节奏(例如滚动触发动画),推荐组合方案:
- 用
IntersectionObserver检测元素进入视口 - 给目标元素添加预设 class(如
animate-fade-up),其 CSS 已内置animation-timing-function - 避免在
raf回调里反复读写element.style.transform—— 这会强制同步布局计算
错误示范(性能差):
function animateY(el, targetY, duration) {
const start = Date.now();
function step() {
const elapsed = Date.now() - start;
const t = Math.min(elapsed / duration, 1);
// 手写 easeOutCubic:t³ - 3t² + 3t
const eased = t * t * t - 3 * t * t + 3 * t;
el.style.transform = `translateY(${eased * targetY}px)`;
if (t < 1) requestAnimationFrame(step);
}
step();
}移动端要注意 will-change 和硬件加速触发条件
即使用了缓动函数,iOS Safari 和部分安卓 WebView 仍可能掉帧。根本原因常是浏览器没启用 GPU 加速。
补救措施:
- 对动画元素加
will-change: transform(仅在动画前设置,结束后移除) - 确保动画属性是
transform或opacity—— 改left、top或width会触发 layout,无法硬件加速 - 避免在动画中同时修改多个 transform 属性(如
scale和rotate混用),某些旧版 WebView 会退化到 CPU 渲染
一个易被忽略的事实:iOS 15+ 要求 transform 必须包含至少一个 3D 分量(哪怕只是 translateZ(0))才能稳定启用 GPU 加速。所以生产环境建议写成:transform: translateX(10px) translateZ(0)。











