真实水波晃动需多层非线性动画叠加:水平/垂直位移、轻微扭曲、透明度与模糊联动,配合错频相位、惯性缓动及容器相对单位,模拟扰动传播而非简单变形。

用 @keyframes 做晃动,不是来回缩放就完事
纯 transform: scale() 或 rotate() 的简单抖动,看着像鼠标卡顿,不是水波。真实晃动是多层、非线性、带衰减的微扰——水面受扰后不会立刻回弹,而是有惯性起伏+边缘弥散。关键是要模拟「扰动传播」而非「元素变形」。
实操建议:
- 用多个
@keyframes分层定义:一层控制水平位移(translateX),一层控制垂直起伏(translateY),一层控制轻微扭曲(skewY),避免全靠rotate造成机械感 - 时间函数必须用
cubic-bezier(0.3, 1.2, 0.5, 1)类非对称缓动,起始快、收尾慢,模仿水体惯性 - 动画时长设为
2.4s~3.8s,太短像抖动,太长失去“被触碰”的即时反馈感 - 别只动一个元素:给容器加
overflow: hidden,内部用伪元素::before和::after分别跑不同相位的动画,制造错落感
transform + opacity + filter: blur() 联动才像水
单靠位移只是“晃”,加了透明度和模糊才像“水在晃”。水波晃动时,高点更透、边缘更虚,这是视觉锚点。
常见错误现象:opacity 从 1→0.6 线性变化,结果像盖了层灰布;或 blur(2px) 固定不变,失去动态弥散感。
实操建议:
- 把
opacity和filter: blur()放进同一段@keyframes,且曲线要反向:峰值位移时opacity: 0.7+blur(3px),回中点时恢复opacity: 1+blur(0) - 慎用
blur()超过4px:移动端性能陡降,iOS Safari 容易掉帧 - 如果目标是背景水波,优先用
backdrop-filter配合半透容器,比在内容层加filter更轻量
响应式水波晃动必须绕开 vh/vw 绝对单位
用 10vh 做位移幅度,在手机上可能晃得像地震,在桌面又纹丝不动。水波的“晃感”应基于容器自身尺寸,而不是视口。
使用场景:做卡片悬停水波、模块呼吸感、或加载中背景微动。
实操建议:
- 位移值统一用
em或%:比如translateY(0.3em),让晃动幅度随字体大小/容器高度自适应 - 避免在
@keyframes里写calc(5vh - 20px):部分浏览器解析不稳定,尤其 Safari 16.6+ 有已知渲染偏差 - 如需严格控制幅度,改用 JS 注入 CSS 变量:在
resize时计算--wave-amplitude: calc(3% + 0.5vh),再在动画中引用translateY(var(--wave-amplitude))
真·逼真晃动,得靠多层错频动画叠加
自然界没有单一频率的水波晃动。风、深度、表面张力共同作用,形成主波+次波+高频涟漪。CSS 里靠多个 @keyframes 不同周期、不同振幅、不同相位来模拟。
性能影响:三层动画叠加在低端 Android 上可能触发重绘,但只要只动 transform 和 opacity,GPU 仍能接管。
实操建议:
- 主层:周期
3.2s,位移 ±0.4em,用cubic-bezier(0.4, 0.8, 0.6, 1) - 次层:周期
1.9s,位移 ±0.15em,相位偏移0.3s,加轻微skewY(0.5deg) - 表层涟漪:周期
0.7s,仅用opacity快闪(0.9→0.95→0.9),不位移,模拟光斑跳动 - 所有层用同一容器的
::before、::after和子元素实现,避免额外 DOM 节点
真实水波晃动最易被忽略的点,是它永远不“干净”——没有完美对称的往返,没有恒定的节奏,也没有完全同步的层次。用 CSS 模拟时,宁可手动微调三处 0.03s 的 delay 或改一个 cubic-bezier 的中间值,也别追求数学上的“精确”。浏览器渲染本身就有帧抖动,那点不完美,恰恰是可信的开始。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











