diff --git a/personal-blog/backend/uploads/20260820_191841_art1.png b/personal-blog/backend/uploads/20260820_191841_art1.png new file mode 100644 index 0000000..6e79af5 Binary files /dev/null and b/personal-blog/backend/uploads/20260820_191841_art1.png differ diff --git a/personal-blog/backend/uploads/thumbnails/thumb_20260820_191841_art1.png b/personal-blog/backend/uploads/thumbnails/thumb_20260820_191841_art1.png new file mode 100644 index 0000000..433c73c Binary files /dev/null and b/personal-blog/backend/uploads/thumbnails/thumb_20260820_191841_art1.png differ diff --git a/personal-blog/frontend/src/components/CursorEffect.jsx b/personal-blog/frontend/src/components/CursorEffect.jsx new file mode 100644 index 0000000..f2e7a78 --- /dev/null +++ b/personal-blog/frontend/src/components/CursorEffect.jsx @@ -0,0 +1,61 @@ +// 还原 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 ( + <> + +
+ + ) +} diff --git a/personal-blog/frontend/src/pages/Home.jsx b/personal-blog/frontend/src/pages/Home.jsx index ab8fcd2..57ac664 100644 --- a/personal-blog/frontend/src/pages/Home.jsx +++ b/personal-blog/frontend/src/pages/Home.jsx @@ -2,6 +2,7 @@ import { useState, useEffect, useRef, useCallback } from 'react' import { Link } from 'react-router-dom' import { API_BASE_URL } from '../api/config' +import { CursorEffect } from '../components/CursorEffect' const POSTS_PER_PAGE = 10 // 每页加载数量 @@ -227,6 +228,8 @@ export function Home() { return (
+ {/* DeepSeek 同款 cursor blend 圆饼效果(仅首页) */} + {/* 顶部导航 */}