内容摘要: 鼠标移上去,光效要像被指尖划过的水面,从边缘泛出涟漪——这是我在一个后台管理项目里调了三天的效果。核心就一句话:光效不是“加上去的”,是“算出来的”。用CSS变量存鼠标坐标,再让一个伪元素的渐变背景
鼠标移上去,鼠示伸缩光光效要像被指尖划过的标移水面,从边缘泛出涟漪——这是动显我在一个后台管理项目里调了三天的效果。核心就一句话:光效不是鼠示伸缩光“加上去的”,是标移“算出来的”。用CSS变量存鼠标坐标,动显再让一个伪元素的鼠示伸缩光渐变背景跟着坐标走,光就活了。标移
先看最土的动显办法。有人用JS监听mousemove,鼠示伸缩光给元素塞一堆span,标移每个span做延迟动画。动显那玩意儿卡得掉帧,鼠示伸缩光还污染DOM。标移我后来全删了,动显换成纯CSS方案:父元素设`--x`和`--y`两个变量,JS每帧只更新这两个值,剩下全交给`background: radial-gradient(circle at var(--x) var(--y), rgba(255,255,255,0.3) 0%, transparent 60%)`。伪元素`::before`铺满整个div,默认透明度0,鼠标移入时透明度跳到1,移出时过渡回0。光效是“显示”出来的,不是“移动”出来的——你只需要让光斑中心点追着鼠标跑,视觉上就成了伸缩光效。
这里有个细节最容易被忽略:光效的“伸缩”不是靠缩放,是靠渐变半径的差值。比如你设定`circle`半径从20%到80%变化,鼠标在左上角时,光斑是小的;往右下角拖,光斑变大。感觉像光在“呼吸”。但我测试时发现,纯靠坐标变化,光斑在元素边缘会突然截断,特别生硬。解法是给伪元素加`padding: 20px`,再设`background-clip: content-box`——等于把光效的“画布”往外扩了一圈,边缘的渐变就能自然衰减,不会撞墙。
再提一个反直觉的点:光效的“速度感”不是靠光斑移动快,是靠光斑和鼠标位置之间的“延迟”。我试过让光斑完全同步鼠标,结果像贴纸,一点不炫。后来给`transition: background 0.15s ease-out`——不对,background不能直接过渡渐变。得换思路:把光斑做成一个独立的绝对定位小圆点,用`transform: translate()`跟鼠标,再给这个圆点的`transition: transform 0.1s linear`。圆点跑得比鼠标慢半拍,光效就带尾巴了。那光效的“伸缩”呢?用圆点的`scale`,鼠标移入时从0.8到1.1,移出时缩回0.5,配上`opacity`淡出,跟光斑的渐变叠加,效果立刻有层次。
实际项目里,我踩过一个坑:鼠标快速划过整个div时,光效会“断”。因为mousemove事件不是每像素都触发,浏览器会合并高频触发。解决办法是别监听div本身,监听它的父容器,用`e.clientX - rect.left`算相对位置,同时给div设`pointer-events: none`,让事件穿透到父层。这样鼠标划多快都不丢坐标,光效始终连贯。
最后说点主观的。这种效果网上教程一堆,但真正能用的没几个。很多demo在Chrome里跑得欢,一到Firefox或者移动端就露馅——因为`background-clip: content-box`对伪元素的兼容性在旧版Safari有bug。我的建议是:别追求完美,给不支持的环境直接降级成渐变底色,光效没了但不影响功能。毕竟用户不会拿放大镜看你的光效,但会拿鼠标划你的按钮。光效是点缀,不是主角。