mask-image配合mask-position动画是实现平滑、无重排、可叠加渐变背景波浪效果最可控的方式,但需注意ios safari 15.4前不支持、mask-size单位间必须有空格(如120px 30px)、须设mask-repeat: repeat-x且兼容性需用@supports或js检测。

直接用 mask-image 配合 mask-position 动画,是目前实现平滑、无重排、可叠加渐变背景的波浪背景最可控的方式——但 iOS Safari 15.4 之前不支持,且 mask-size 值中若漏掉空格(比如写成 120px30px)会静默失效。
为什么 mask-image 比 background-position 平移 PNG 更好
用 PNG 波浪图 + background-position 滚动,看似简单,但实际有硬伤:必须手动切图保证无缝,缩放或响应式时易露缝;无法和线性渐变背景叠加(PNG 不透明区域会盖掉渐变);动画触发重绘而非合成,滚动时容易卡顿。
mask-image 的优势在于它只控制“显示区域”,底层仍可用 background: linear-gradient(...) 或纯色,完全保留 CSS 渐变的灵活性和硬件加速能力。位移靠 mask-position,不改变布局,也不重排。
关键点:
- 遮罩本身可以是 SVG 路径(精准正弦波)或
radial-gradient(快速模拟) - 必须设
mask-repeat: repeat-x,否则只显示一个波峰 -
mask-size单位之间**必须有空格**,120px 30px✅,120px30px❌(iOS 和部分 Android WebView 直接忽略整条声明) - 要兼容老浏览器,得用
@supports (mask-image: none)包裹,并提供clip-path或 PNG 回退
用 radial-gradient 快速写出可动波浪底边
不用 SVG,也能用多组偏移的径向渐变拼出周期性起伏。核心是让每个圆心在容器底部(at X% 100%),半径略大于“谷深”,形成透明→黑→透明的过渡带,视觉上就是波峰。
示例代码:
div.wave-bg {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
-webkit-mask-image: radial-gradient(circle at 20% 100%, transparent 40%, black 41%),
radial-gradient(circle at 60% 100%, transparent 40%, black 41%),
radial-gradient(circle at 100% 100%, transparent 40%, black 41%);
mask-image: radial-gradient(circle at 20% 100%, transparent 40%, black 41%),
radial-gradient(circle at 60% 100%, transparent 40%, black 41%),
radial-gradient(circle at 100% 100%, transparent 40%, black 41%);
mask-repeat: repeat-x;
mask-size: 120px 60px; /* 注意:中间有空格 */
animation: wave-move 8s linear infinite;
}
<p>@keyframes wave-move {
0% { mask-position: 0 0; }
100% { mask-position: -120px 0; }
}</p>
说明:
- 三个
radial-gradient圆心水平错开,形成连续波峰;垂直都在100%,确保波形贴底 -
transparent 40%, black 41%控制波峰宽度(1% 的过渡带产生柔和边缘) -
mask-size: 120px 60px中的120px对应动画位移量,必须匹配@keyframes中的-120px - 动画只需驱动
mask-position,无需改background-position
用 SVG data URI 实现精确正弦波并避免 base64 兼容问题
需要更真实波形时,SVG 是首选。但别用 base64 编码的 SVG——iOS Safari 对 base64 + mask-image 的解析极不稳定。改用 UTF-8 URL 编码的内联 SVG(data:image/svg+xml;utf8,),既轻量又可靠。
关键陷阱:
- SVG 中的
&必须编码为&,否则解析失败 -
<path></path>的d属性里不能换行,所有空格/换行需删除或编码 - 务必设置
viewBox,否则mask-size行为不可控 - 颜色填
black(遮罩中黑色=显示,透明=隐藏),别用#000或rgb(0,0,0)——某些旧 WebView 对非关键词颜色识别异常
最小可用 SVG 遮罩示例(单周期正弦波):
.wave-precise {
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg viewbox="0 0 1200 300" xmlns="http://www.w3.org/2000/svg"><path fill="black" d="M0,150 Q150,100 300,150 T600,150 T900,150 T1200,150 L1200,300 L0,300 Z"></path></svg>");
mask-image: url("data:image/svg+xml;utf8,<svg viewbox="0 0 1200 300" xmlns="http://www.w3.org/2000/svg"><path fill="black" d="M0,150 Q150,100 300,150 T600,150 T900,150 T1200,150 L1200,300 L0,300 Z"></path></svg>");
mask-repeat: repeat-x;
mask-size: 1200px 300px;
animation: wave-shift 6s linear infinite;
}
移动端真机测试最容易被忽略的三个点
写完代码在 Chrome DevTools 里看着很顺,一上真机就失效,大概率栽在这三处:
- iOS Safari 15.4 以下版本压根不认
mask-image,连@supports都检测不到——必须加 JS 特征检测:if ('maskImage' in document.documentElement.style) {...} - Android WebView(尤其微信内置)对
mask-size单位空格极其敏感,复制粘贴时容易带全角空格或不可见字符,建议手敲 - 动画卡顿不是因为逻辑错,而是没加
will-change: mask-position,尤其在低端安卓机上,不提示合成层,浏览器默认走软件渲染
真正难的不是写出波浪,而是让波浪在各种屏幕、各种 WebView 里都稳定呼吸——空格、前缀、降级路径、真机验证,缺一不可。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











