无法用text-decoration直接实现波浪线,需用伪元素+径向渐变、clip-path动画或SVG<path>三种方案:前者适合静态单行装饰,中者适合动态交互反馈,后者兼容性最佳且可响应文字宽度变化。

直接用 text-decoration 无法画出真正波浪线,浏览器原生只支持直线或点划线;要实现视觉上连续起伏的波浪下划线,必须绕过这个限制,用伪元素 + 贝塞尔曲线或重复渐变来模拟。
用 ::after 伪元素 + radial-gradient 模拟短波浪线
适合单行文字下的装饰性波浪下划线,比如标题、按钮文字。核心是把圆点渐变旋转后横向平铺,形成锯齿感近似波浪:
-
radial-gradient(circle, #007bff 2px, #007bff, transparent 3px, transparent)控制单个“波峰”大小和间距 -
background-size: 8px 8px决定波长,数值越小波越密 -
transform: rotate(90deg)把竖向圆点阵转成横向起伏,再用background-repeat: repeat-x横向铺开 - 必须设
position: relative在父元素上,否则::after无法精确定位到底部
示例代码:
.wave-underline {
position: relative;
display: inline-block;
}
.wave-underline::after {
content: '';
position: absolute;
left: 0; bottom: -4px;
width: 100%; height: 8px;
background: radial-gradient(circle, #007bff 2px, #007bff, transparent 3px, transparent);
background-size: 8px 8px;
transform: rotate(90deg);
background-repeat: repeat-x;
}
用 clip-path + @keyframes 做动态波浪下划线
适合需要呼吸感、缓慢起伏的交互反馈场景(如输入框聚焦、悬停提示)。但注意:clip-path 动画在 Safari 旧版和部分安卓 WebView 中不触发硬件加速,可能卡顿:
立即学习“前端免费学习笔记(深入)”;
- 动画目标是改变
clip-path的polygon()坐标点,不能直接 animated属性 - 推荐用 5–7 个顶点控制波形,太多点会导致计算开销上升
- 必须搭配
will-change: clip-path或transform: translateZ(0)强制 GPU 加速 - 避免在
:hover中直接写完整动画,应预定义@keyframes并通过 class 切换触发
关键帧片段:
@keyframes wave-und {
0% { clip-path: polygon(0% 100%, 10% 90%, 20% 100%, 30% 90%, ..., 100% 100%); }
50% { clip-path: polygon(0% 100%, 10% 85%, 20% 100%, 30% 85%, ..., 100% 100%); }
100% { clip-path: polygon(0% 100%, 10% 90%, 20% 100%, 30% 90%, ..., 100% 100%); }
}
用 SVG <path> 嵌入实现高保真波浪线
这是最可控、兼容性最好(IE11+)、且能响应文字宽度变化的方式,特别适合长文本或需要精确对齐的 UI 区域:
- 把
<svg>内联进 HTML,设width="100%" height="8",viewBox宽度需略大于文字实际宽度(建议加 20px 余量) - 用
M+C贝塞尔曲线绘制平滑波,例如d="M0,4 C30,0 90,8 120,4 V8 H0 Z" - SVG 内部用
<use>复用路径可减少体积,但动态修改需 JS 操作setAttribute('d', ...) - 不要给 SVG 设固定
height,而是靠vertical-align: middle或 flex 对齐文字基线
最小可用结构:
<span class="text">欢迎来到首页</span> <svg viewBox="0 0 120 8" width="100%" height="8" style="vertical-align: middle"> <path d="M0,4 C30,0 90,8 120,4 V8 H0 Z" fill="#007bff"/> </svg>
真正难的不是画出波浪,而是让波浪始终贴合文字长度变化、不随字体缩放错位、在 Retina 屏上不模糊——这些细节往往要靠 getBoundingClientRect() 测量 + ResizeObserver 监听,而不是靠一套 CSS 规则通吃。



















