Dock 弹跳效果核心是 CSS 关键帧动画 + 硬件加速,需用 cubic-bezier 模拟惯性、幅度 2–6px、时长 0.4–0.6s、最后一帧必须归零,配合 will-change 和事件驱动控制触发时机。

用 transform + animation 实现 Dock 弹跳效果的核心逻辑
Mac Dock 的弹跳提示(比如新消息、下载完成时图标上下轻微跃动)不是靠 JS 控制帧,而是纯 CSS 关键帧动画 + 硬件加速。关键在于「小幅度、非线性、带回弹感」——不能用匀速 translateY,得用 cubic-bezier(0.2, 0.8, 0.4, 1) 这类缓动模拟物理惯性。
实操建议:
- 动画只作用于
transform和opacity,避免触发重排(top/left会掉帧) - 弹跳幅度控制在
2px ~ 6px之间,超过 8px 就像抽搐,失去 Dock 的克制感 - 必须加
will-change: transform,尤其在 Safari 下,否则 iOS/iPadOS 上动画卡顿明显 - 动画时长建议
0.4s ~ 0.6s,太短没存在感,太长像加载中
@keyframes bounce-dock 的标准写法与常见错误
很多人直接抄网上的「bounce」动画,结果弹两下就停,或者最后悬在半空——Dock 的弹跳是「起跳 → 落地 → 微回弹 → 归位」四段,且最后一帧必须是 transform: translateY(0)。
正确写法示例(可直接复用):
立即学习 “ 前端免费学习笔记(深入)”;
@@keyframes bounce-dock {0% { transform: translateY(0); } 20% {transform: translateY(-4px); } 40% {transform: translateY(0); } 60% {transform: translateY(-1px); } 80% {transform: translateY(0); } 100% {transform: translateY(0); } }
常见错误现象:
- 最后一帧没归零 → 图标永久偏移,后续 hover 或点击错位
- 用
scaleY模拟压缩 → 视觉失真,Dock 从不缩放图标 - 加了
infinite却没控制触发时机 → 动画一直跑,CPU 占用升高 - 在
:hover里直接写动画 → 鼠标划过就触发,实际 Dock 只对状态变更响应
如何用 JS 控制弹跳时机,又不破坏 CSS 动画的流畅性
触发弹跳的时机比动画本身更重要:新消息来了才动,不是每秒轮询。别用 setInterval 去反复加 class,要用事件驱动。
实操建议:
- 给图标容器加一个数据属性,比如
data-dock-state="idle",状态变更为"alert"时再触发 - 用
element.classList.add("dock-bounce"),并在动画结束回调里立刻remove—— 避免 class 积压 - 监听
animationend事件,但必须过滤掉webkitAnimationEnd(Safari),且只响应bounce-dock名称 - 同一图标 5 秒内重复 alert,应合并为一次动画,加个节流标记
data-bounce-pending="true"
Safari 和旧版 Chrome 的兼容性坑点
macOS 上 Safari 是主力,但它对 @keyframes 的解析更严格:少一个分号、多一个空格,整段动画就静音。而且它不支持 animation-composition: add,没法叠加多个动画。
必须处理的兼容性问题:
- Safari 15.4 之前不支持
animation-timeline: view(),所以别想做「进入视口才启动」这种高级玩法 - 旧版 Chrome(cubic-bezier 超出 [0,1] 范围的值会静默失败,建议用在线工具转成
steps()备用方案 - 如果图标用了
filter: drop-shadow(),动画期间阴影会闪烁,解决方案是把 shadow 提到伪元素上,主图层只做 transform - macOS Ventura 后,系统级动画速率受「减少运动」设置影响,需检测
window.matchMedia("(prefers-reduced-motion: reduce)").matches并禁用动画
最易被忽略的是:Dock 弹跳从来不是孤立动画,它和图标背景高亮、badge 数字浮现是协同的。单独调好 bounce,但 badge 没跟上时间轴,整体就显得零碎。节奏对齐比单个动画精致更重要。