// 还原 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 ( <>
) }