真正可用的震动动画必须用 translateX() 实现小幅高频非对称位移,关键帧至少4帧且首尾为0,响应明确事件触发、单次执行、避免混用 rotate/scale,注意 Safari 贝塞尔兼容性与节流控制。

震动动画必须用 translateX(),别碰 rotate()
纯靠 rotate() 实现的“抖动”看起来像抽搐,视觉重心飘、不稳,用户容易不适。真正可用的震动是沿 X 轴做小幅(±2–5px)、高频、非对称位移,模拟物理惯性回弹。关键帧至少 4 帧:0% → 25% → 50% → 100%,且 0% 和 100% 必须都是 translateX(0),否则动画结束会偏移或跳帧。
推荐写法:
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-4px); }
50% { transform: translateX(3px); }
75% { transform: translateX(-2px); }
}
中间值故意不对称(比如 -4 → +3 → -2),比对称的 ±4 更自然;避免在 keyframes 里混用 scale() 或 rotate(),它们会干扰定位基准。
触发震动不能写死在初始 class 上
元素一加载就抖,不是反馈,是 bug。震动必须是响应式行为——比如表单校验失败、按钮点击无响应、拖拽释放越界等明确事件之后。
立即学习“前端免费学习笔记(深入)”;
- 错误做法:
.input-error { animation: shake 0.4s; }→ 用户还没输完就抖 - 正确做法:定义独立触发类
.shake-trigger,只含动画声明 - JS 中用
element.classList.add('shake-trigger')动态添加 - 监听
animationend事件后立刻remove,防止重复叠加
别设 animation-iteration-count: infinite —— 抖一次就够了,重复会引发视觉疲劳。
cubic-bezier() 在 Safari 上容易失效
你抄来的高性能缓动曲线 cubic-bezier(.36,.07,.19,.97) 在 Chrome/Firefox 没问题,但在 Safari 15.4 之前会直接退化成 linear,因为 Safari 对贝塞尔控制点 y 值校验更严:超出 [0,1] 就丢弃整条曲线。
安全写法:
- 确保所有 y 值在 0–1 范围内,例如
cubic-bezier(.36, .1, .19, .9) - 或改用
steps(2, end)实现机械式短抖(适合表单错误提示) - 如需兼容旧 Safari,可加降级:先写
ease-in-out,再覆盖自定义曲线
另外,prefers-reduced-motion 是系统级开关,但多数用户根本没开,不能依赖它自动禁用震动。
批量震动前必须节流,否则 GPU 内存可能爆掉
单个按钮或输入框抖一下没问题,transform 是合成层友好操作。但如果你在长列表每行都加 shake-trigger,或滚动中持续触发,低端机大概率掉帧甚至白屏。
实际处理建议:
- 多数场景只需高亮首个错误项,其余用文字提示,而非全部抖动
- 真要批量触发,加 300ms 节流,比如用
setTimeout去重 - 检查是否真需要“每个独立抖”——UI 设计上,克制比炫技更重要
震动不是装饰,是信号。抖得不准、抖得太多、抖得不合时宜,比不抖更伤体验。



















