涟漪效果的核心原理是用JS获取点击坐标并动态设置CSS变量,使::after伪元素圆心精准落在点击处,再通过transform: scale()动画实现扩散。纯CSS无法读取坐标,故必须结合JS计算偏移量、监听click事件、设置--x/--y变量,并配合relative定位、border-radius:50%及cubic-bezier过渡。
涟漪效果的核心原理是什么
本质是用
伪元素
在点击位置生成一个圆形,初始
,再通过
动画撑开,配合
淡出。关键不是“画圆”,而是让圆心精准落在鼠标点击坐标 —— 这必须靠 JS 获取
并设置为伪元素的
偏移。
为什么不能只用纯 CSS 实现完整涟漪
因为纯 CSS 无法读取点击坐标。常见误区是写死
,结果涟漪永远从盒子中心炸开,和点击位置无关。真实交互中用户可能点在右下角,涟漪也得从那里开始扩散。
必须用 JS 计算偏移量,并动态写入内联样式或自定义属性:
监听
事件,获取
和
把这两个值设为元素的
和
CSS 中用
定位伪元素圆心
如何用
伪元素画出圆形涟漪
伪元素本身不接受 JS 操作,所以定位和动画必须靠 CSS 变量 +
配合。注意几个硬性约束:
立即学习
“
前端免费学习笔记(深入)
”;
使用HTML,CSS,JavaScript开发Android应用程序 英文文字pdf版附源文件
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
下载
必须加在目标盒子上,否则
的
会相对 body 定位
要设
、
、
动画用
,别用
,否则触发重排,卡顿
过渡时间建议
,比线性更自然
示例关键 CSS:
点击后如何清理已播放的涟漪动画
连续点击会叠加多个
动画实例?不会 —— 伪元素只有一个,但多次设置
会让它跳到新位置并重新播放 scale 动画。问题在于:如果用户快速连点,前一个动画还没结束,新动画会打断它,导致缩放不完整。
解决办法是加节流或等动画结束再允许下一次触发:
用
判断是否还在动(不推荐,性能差)
更稳妥的是用
记录状态,比如设置
,0.45s 后置为
或者直接在每次点击时先重置:
,再设新值,强制重绘
真正容易被忽略的是:如果盒子有
或
,
计算要减去它们,否则偏移错位。用
是最稳的。
::afterscale(0)transform: scale()opacityclientX/clientYleft/topleft: 50%; top: 50%clicke.clientX - rect.lefte.clientY - rect.topstyle.setProperty('--x', x + 'px')--yleft: var(--x); top: var(--y)::aftertransformposition: relative::afterabsolute::aftercontent: ''border-radius: 50%background: rgba(0,0,0,0.1)transform: scale(0) → scale(3)width/heighttransition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94).ripple {
position: relative;
overflow: hidden;
}
.ripple::after {
content: '';
position: absolute;
width: 10px; height: 10px;
background: rgba(0,0,0,0.2);
border-radius: 50%;
transform: translate(-50%, -50%) scale(0);
transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
left: var(--x, 50%);
top: var(--y, 50%);
}::after--x/--ygetComputedStyle(el).getPropertyValue('transform')setTimeoutisRippling = truefalseel.style.removeProperty('--x'); el.style.removeProperty('--y')borderpaddingrect.left/topgetBoundingClientRect()