缓动函数必须通过CSS的transition-timing-function或animation-timing-function实现,推荐使用cubic-bezier()自定义贝塞尔曲线以确保跨浏览器一致性与精确控制。
transition-timing-function 或 animation-timing-function
HTML5 动画本身不提供“缓动函数”这个独立 API,所有缓动效果都依赖 CSS 的时间函数控制。直接写 ea、
seease-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; }
}注意:
cubic-bezier(0.5, -0.5, 0.5, 1.5))cubic-bezier(0.5, 0.5, 0.5, 0.5))等价于 linear
cubic-bezier() 字符串来改样式——重排重绘开销大,优先用 CSS 变量或预设 class 切换requestAnimationFrame 手写缓动除非你有特殊需求(比如物理模拟、逐帧音频同步),否则纯 JS 实现缓动既难调试又难维护。现代浏览器对 CSS 缓动优化极好,GPU 加速也更稳定。
如果必须用 JS 控制节奏(例如滚动触发动画),推荐组合方案:
IntersectionObserver 检测元素进入视口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,无法硬件加速scale 和 rotate 混用),某些旧版 WebView 会退化到 CPU 渲染一个易被忽略的事实:iOS 15+ 要求 transform 必须包含至少一个 3D 分量(哪怕只是 translateZ(0))才能稳定启用 GPU 加速。所以生产环境建议写成:transform: translateX(10px) translateZ(0)。