非线性心跳动画关键在于模拟生物节律:快速收缩→短暂停顿→缓慢舒张→回弹;需用分段关键帧、cubic-bezier缓动、infinite循环、animation-fill-mode: forwards及transform-origin: center确保真实搏动感。

用 @keyframes 定义非线性缩放曲线
心跳动画的关键不是匀速放大缩小,而是模拟生物节律:快速收缩 → 短暂停顿 → 缓慢舒张 → 回弹。直接写 0% { transform: scale(1); } 100% { transform: scale(1.2); } 会显得机械。应该用 ease-out 和 ease-in 分段控制节奏,比如前 30% 快速放大(模拟心肌收缩),中间 20% 几乎静止(平台期),后 50% 缓慢回落并略低于基准(模拟回弹惯性):
@keyframes heartbeat {
0% { transform: scale(1); }
30% { transform: scale(1.15); }
50% { transform: scale(1.12); }
100% { transform: scale(0.98); }
}
注意:最后回弹到 0.98 而不是 1,能强化“搏动感”;若设为 1,动画结束时会丢失弹性反馈。
animation 属性必须加 infinite 和合理时长
心跳频率约 60–100 次/分钟,对应单次动画周期应为 0.6s–1s。但 CSS 动画默认从头开始重播,会导致两次收缩之间出现“卡顿空隙”。必须用 infinite + 合理的 animation-duration,并配合 animation-timing-function: steps(1, end) 以外的缓动函数:
-
animation: heartbeat 0.8s ease-in-out infinite;—— 适合安静状态下的基础心跳 -
animation: heartbeat 0.6s cubic-bezier(0.3, 1.2, 0.4, 1) infinite;—— 更强收缩感(cubic-bezier提前加速、延迟减速) - 别漏掉
animation-fill-mode: forwards;,否则动画结束后元素会跳回原始尺寸
避免 transform-origin 导致视觉偏移
缩放默认以元素中心为原点,但如果元素本身有边框、阴影或父容器设置了 overflow: hidden,缩放时边缘可能被裁切或抖动。常见错误是没显式声明原点:
- 给需要心跳的元素加
transform-origin: center;(显式声明更可控) - 若元素是图标或按钮,且父容器高度固定,建议同时设
will-change: transform;防止渲染闪烁 - 慎用
scaleZ()或scale3d():纯 2D 心跳用scale()足够,引入 Z 轴反而增加 GPU 开销且无视觉增益
在 React/Vue 中动态控制启停要绕开内联 style 冲突
如果心跳要响应用户操作(比如点击暂停),别直接用 style={{ animation: 'heartbeat 0.8s infinite' }}。React 的内联 style 会覆盖 class 里定义的 animation,且无法单独停止单个属性。推荐做法:
- 用 className 切换,例如
className={isBeating ? 'heart--beating' : 'heart'},CSS 中定义.heart--beating { animation: heartbeat 0.8s infinite; } - Vue 用户可用
:class="{ 'heart--beating': isBeating }" - 想临时暂停?改用
animation-play-state: paused;,比移除整个 animation 更平滑
真正难的是让多个心跳元素不同步——比如模拟多器官搏动,这时得用 JS 随机设置 animation-delay,但别用 Math.random() 在每次 render 里生成,会导致重渲染时重置动画进度。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











