css3无原生水波纹动画,悬停效果靠:hover触发transform+opacity的径向渐变遮罩缩放实现;点击式水波纹必须用javascript获取鼠标坐标并动态创建元素。

直接说结论:CSS3 本身没有“水波纹”原生动画,所谓按钮悬停水波纹,本质是用 :hover 触发 transform + opacity 的径向渐变遮罩缩放动画,靠视觉错觉模拟水波扩散——不是加个 transition 就能出效果,关键在遮罩层的定位和缩放基准点。
为什么纯 transition 做不出真实水波纹?
CSS 过渡只能平滑变化可动画属性(如 opacity、transform、background-size),但“从点击点向外扩散”的水波需要动态响应鼠标坐标,这必须靠 JavaScript 获取位置并设置自定义 CSS 变量;纯 CSS 只能做**固定中心点**的模拟效果,适合按钮整体悬停(非点击),且中心默认在 50% 50%。
- 常见错误:只给
background加transition,但径向渐变本身不支持过渡,必须配合background-size或transform: scale() - 更隐蔽的问题:没设
background-repeat: no-repeat,导致渐变重复铺满,缩放时出现多圈“伪波纹” - 兼容性注意:
radial-gradient在旧版 Safari 需要-webkit-radial-gradient前缀(iOS 12.2+ 已基本无需)
用 background + scale() 实现固定中心水波(推荐初学者)
这是最易理解、兼容性好、无需 JS 的方案。核心是把径向渐变作为背景图,用 transform: scale() 放大它,再用 transition 控制缩放过程。
button {
position: relative;
overflow: hidden;
background: #4a6fa5;
color: white;
border: none;
padding: 12px 24px;
}
button::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: radial-gradient(circle, rgba(255,255,255,0.4) 0%, transparent 70%);
transform: translate(-50%, -50%) scale(0);
transition: transform 0.6s ease-out;
z-index: 0;
}
button:hover::before {
transform: translate(-50%, -50%) scale(3);
}
- 必须设
position: relative给按钮,否则::before无法精确定位到中心 -
transform: translate(-50%, -50%)是关键,让缩放以中心为原点,不是左上角 -
scale(3)数值根据按钮大小调整:太小(如scale(1.5))波纹不明显,太大(如scale(5))会溢出容器
用 background-size 替代 scale()(更轻量,但控制稍弱)
如果不想用伪元素或担心层级问题,可以直接操作按钮自身的背景渐变尺寸。缺点是无法单独控制透明度变化,波纹边缘较硬。
button {
background: #4a6fa5;
background-image: radial-gradient(circle, rgba(255,255,255,0.4) 0%, transparent 70%);
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 0 0;
transition: background-size 0.6s ease-out;
}
button:hover {
background-size: 300% 300%;
}
-
background-size: 0 0必须写全,不能只写0,否则 Firefox 下可能不触发过渡 -
300% 300%表示渐变图放大到按钮宽高的 3 倍,确保能覆盖整个按钮;数值过小(如150%)会导致波纹只在中心区域可见 - 这个方案无法像
scale()那样叠加opacity渐隐,若需更柔和效果,得额外加一层半透明白色覆盖层
真正点击水波纹必须用 JavaScript(别被“纯 CSS”误导)
如果你看到“点击按钮出现从点击点出发的水波”,那一定用了 JS。CSS 无法读取鼠标坐标,event.clientX 和 event.clientY 是唯一途径。初学者常误以为加个 transition 就够了,结果卡在定位不准、缩放偏移、多次点击残留上。
- 核心逻辑:监听
click,计算相对于按钮左上角的坐标,生成一个span或div作为波纹元素,设置其left/top并用scale()动画 - 容易忽略的细节:必须用
getBoundingClientRect()获取按钮实际位置,不能直接用offsetLeft(受transform影响) - 性能隐患:没做节流或未清理已结束的波纹 DOM,连续点击会堆积大量元素,内存泄漏
复杂点不在动画本身,而在坐标映射和生命周期管理——哪怕只是加个水波纹,也已经跨进交互逻辑的门槛了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











