62 lines
2.2 KiB
React
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" />
|
|
</>
|
|
)
|
|
}
|