Files
nanocode/personal-blog/frontend/src/components/CursorEffect.jsx
T
2026-08-21 16:00:01 +08:00

62 lines
2.2 KiB
React

// 还原 DeepSeek 首页的 cursor blend 圆饼效果
// 原理:fixed 白色圆环 + mix-blend-mode:difference,悬停 [data-cursor="blend"] 元素时放大为 64px 半透明圆饼,移开缩小消失
import { useEffect, useRef } from 'react'
const CURSOR_CSS = `
.ds-cursor-ring{position:fixed;top:0;left:0;pointer-events:none;z-index:9999;will-change:transform,opacity;border-radius:50%;opacity:0;width:0;height:0;mix-blend-mode:difference;transition:width .3s cubic-bezier(.16,1,.3,1),height .3s cubic-bezier(.16,1,.3,1),margin .3s cubic-bezier(.16,1,.3,1),background-color .3s ease,border-color .3s ease,opacity .3s ease}
.ds-cursor-ring.is-blend{width:64px;height:64px;margin-left:-32px;margin-top:-32px;border-color:transparent;background:#fff}
@media (hover:none),(pointer:coarse){.ds-cursor-ring{display:none}}
`
export function CursorEffect() {
const ref = useRef(null)
useEffect(() => {
const ring = ref.current
if (!ring) return
let raf
let tx = 0, ty = 0, x = 0, y = 0
// 弹性跟随:距离 < 50px 跟得紧(0.7),远了跟得松(0.4)
const loop = () => {
const dx = tx - x, dy = ty - y
const ease = Math.sqrt(dx * dx + dy * dy) < 50 ? 0.7 : 0.4
x += dx * ease
y += dy * ease
ring.style.transform = `translate3d(${x}px, ${y}px, 0)`
raf = requestAnimationFrame(loop)
}
raf = requestAnimationFrame(loop)
const onMove = (e) => {
tx = e.clientX
ty = e.clientY
ring.style.opacity = '1'
}
const onOver = (e) => {
const blendEl = e.target.closest('[data-cursor="blend"]')
ring.classList.toggle('is-blend', !!blendEl)
}
const onLeave = () => { ring.style.opacity = '0' }
document.addEventListener('mousemove', onMove)
document.addEventListener('mouseover', onOver)
document.documentElement.addEventListener('mouseleave', onLeave)
return () => {
cancelAnimationFrame(raf)
document.removeEventListener('mousemove', onMove)
document.removeEventListener('mouseover', onOver)
document.documentElement.removeEventListener('mouseleave', onLeave)
}
}, [])
return (
<>
<style>{CURSOR_CSS}</style>
<div ref={ref} className="ds-cursor-ring" />
</>
)
}