Blog

自定义光标和悬停反馈

cssuxanimation

自定义光标和悬停反馈

这个站的光标设计参考了 Unseen Studio 的磁性光标方案——一个大圆环跟随鼠标,靠近交互元素时会略微"吸"向按钮中心并放大。

弹性跟随

普通的 transform: translate(x, y) 加线性插值虽然能用,但手感偏"黏"。试了几组参数后选了一个弹簧模型:

vel += (target - pos) * stiffness
vel *= damping
pos += vel

stiffness = 0.18damping = 0.78 这个组合差不多合适——跟手速度适中,停下来的时候不会过度回弹。如果 stiffness 再高一点会显得太硬,低一点又像拖着块泥巴。

磁性检测

磁性效果的实现是对所有 buttona[role="button"] 元素做距离检测。为避免在 useEffect 里频繁 querySelectorAll,用 MutationObserver 监听 DOM 变化,只在新增或移除元素时重新收集目标列表。

距离计算取的是鼠标到元素矩形中心的距离,减去元素半径。缩到阈值 120px 以内时,鼠标位置会向元素中心偏移,偏移量随距离线性递减。

点击涟漪

点击涟漪是一个轻量的 CSS 动画方案。点击时在点击位置创建一个绝对定位的 div,用 CSS 动画让它从 0 放大到 60px,同时透明度降到 0,动画结束后移除 DOM 节点。

用 requestAnimationFrame 驱动而不是 CSS transition,是为了方便在动画过程中取消或调整参数。

悬停音效

每个可交互元素悬停时触发一个简短的合成音效——由噪声脉冲加两个正弦振荡器组成。噪声脉冲用 createBuffer 预先算好衰减曲线,避免每次悬停都去生成。

这里踩过一个坑:音效用的 AudioContext 和音乐播放器的是同一个,导致播放音乐时悬停音效偶尔会出现爆音。后来把 UI 音效和音乐播放器分到两个独立的 AudioContext,世界清净了。

关于隐藏原生光标

cursor: none 这个 CSS 属性虽然能隐藏原生鼠标指针,但在输入框等需要文本光标的元素上也会生效,需要单独处理。目前对这部分的处理是在 CSS 里对 inputtextarea 重置为 cursor: text,但仍然不够优雅——如果页面加了富文本编辑器之类的东西,可能又有遗漏。