自定义 v-ripple 指令实现水波纹效果:监听点击事件,根据 clientX/clientY 或 touches 计算相对坐标,动态创建带定位与 scale/opacity 动画的 ripple 元素,注入 CSS 关键帧,支持参数配置及动画结束自动清理。

用自定义指令实现按钮水波纹效果,核心是监听点击事件、动态创建波纹元素、按点击位置定位并触发动画。不依赖第三方库,Vue 2 和 Vue 3 均可适配,关键在事件坐标计算、样式注入与动画清理。
绑定点击事件并获取精确坐标
水波纹必须从鼠标/手指点击处中心扩散,不能简单居中。需区分桌面端 clientX/clientY 与移动端 touches[0].clientX,再结合目标元素的 getBoundingClientRect() 计算相对偏移:
- 用
e.clientX - rect.left得到波纹水平起始位置 - 用
e.clientY - rect.top得到垂直起始位置 - 移动端需兼容
e.touches && e.touches.length,避免undefined报错
动态创建并定位波纹元素
每次点击都新建一个 <span class="ripple"></span>,插入按钮内部,并设置绝对定位:
- 按钮容器需提前设
position: relative和overflow: hidden - 波纹元素宽高取
Math.max(el.clientWidth, el.clientHeight),确保覆盖全区域 - 通过
style.left和style.top设置为点击点中心(减去半径) - 添加
pointer-events: none,避免遮挡后续点击
注入 CSS 动画并复用关键帧
避免重复插入相同 @keyframes ripple,提升性能:
- 首次使用时检查
document.styleSheets[0].rules最后一项是否为ripple动画 - 未存在则用
sheet.insertRule()插入标准扩散+渐隐动画 - 动画推荐:从
scale(0) opacity:1到scale(2.5) opacity:0,缓动用cubic-bezier(0.23, 1, 0.32, 1) - 波纹元素添加
animation: ripple 0.6s ease-out forwards
支持参数配置与自动清理
让指令更实用,需支持传参和资源回收:
- 接收
v-ripple="{ color: '#fff', time: 700 }",缺省值兜底 - 动画结束触发
animationend事件,自动移除波纹 DOM 节点 - 若用户快速连点,前一个波纹未消失时,先
removeChild再新建,防止堆叠 - 在
unmounted(Vue 3)或unbind(Vue 2)钩子中清除事件监听器
不复杂但容易忽略细节。写好一次,全局 v-ripple 复用,按钮交互质感立刻提升。

















