因为:active伪类无法获取鼠标点击坐标,所有涟漪只能从中心或固定点触发,必须用js动态计算位置并配合css变量与伪元素实现真实点击点扩散。

为什么直接用 :active 无法实现真实点击位置涟漪
因为 :active 只能触发全局状态,伪元素(如 ::after)无法读取鼠标点击坐标,所有涟漪都从中心或固定点开始——看起来像“硬编码动画”,不是用户所点之处。这不是 SCSS 的限制,而是 CSS 原生伪类的固有行为。SCSS 只能帮你写得更简洁,不能绕过这个底层约束。
::after 涟漪必须配合 JS 动态定位,SCSS 负责封装复用逻辑
SCSS 的价值在于把重复样式抽成可配置的 mixin,比如颜色、持续时间、圆角适配等,但定位和尺寸计算仍需 JS 完成。常见错误是试图在 SCSS 里用 @if $position == 'click' 做条件判断,这毫无意义——编译时变量早已固化,运行时坐标根本进不去。
- 必须给按钮加
position: relative和overflow: hidden - SCSS 中用
@mixin ripple-base($color: rgba(0,0,0,0.15), $duration: 0.6s)封装伪元素基础样式 - JS 中仍要调用
getBoundingClientRect()算相对偏移,再设left/top和transform-origin - 别在 SCSS 里写
transform: scale(#{ $scale })—— 这会导致所有实例共用同一缩放值,失去点击响应性
SCSS 封装时最容易被忽略的三个细节
一是 pointer-events: none 必须写死在伪元素上,不能靠父级继承或动态插入;二是 transform-origin 必须设为 center center(初始)→ 再由 JS 改成 #{ $x }px #{ $y }px,否则缩放中心错位;三是动画结束后的清理:SCSS 无法监听 transitionend,所以 JS 必须在动画完成后手动移除或重置 style.left/style.top,否则残留 inline 样式会干扰下一次点击。
- 用
@include ripple-base(#3b82f6, 0.5s)生成的 CSS 里,transition应只含transform和opacity,避免left/top参与过渡(会卡顿) - 移动端必须监听
touchstart并取touches[0].clientX/Y,不能只靠click事件 - 如果按钮有
border或padding,JS 计算坐标时得减去clientLeft/clientTop,SCSS 封装不解决这个
为什么不用 SCSS 生成多个 class 来切换颜色
因为每次换色都要预编译一堆 class(如 .ripple-red、.ripple-blue),既膨胀 CSS 体积,又要求 HTML 显式加 class。真正灵活的方式是 JS 设置 element.style.setProperty('--ripple-color', color),然后 SCSS 里用 background: var(--ripple-color, rgba(0,0,0,0.15))。注意:变量必须作用于按钮自身,且按钮要声明 position: relative,否则 ::after 读不到该变量。
复杂点在于,Safari 对 hsl() 变量值解析不稳定,推荐始终用 rgb() 或十六进制;另外,var(--ripple-color) 在伪元素中生效的前提是父元素已声明该变量——这个“声明动作”只能由 JS 完成,SCSS 编译不出运行时行为。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











