1
- 'use client' ;
1
+ "use client" ;
2
+ import React , { useEffect , useState , useRef } from 'react' ;
3
+ import { usePathname } from 'next/navigation' ;
2
4
3
- import React , { useEffect , useState } from 'react' ;
5
+ function StarryBackground ( { starCount = 240 } ) {
6
+ const pathname = usePathname ( ) ;
7
+ // 在 docs 路由下不渲染背景
8
+ if ( pathname && pathname . startsWith ( '/docs' ) ) {
9
+ return null ;
10
+ }
4
11
5
- const StarryBackground = ( { starCount = 240 } ) => {
6
12
const [ stars , setStars ] = useState ( [ ] ) ;
13
+ const [ mousePosition , setMousePosition ] = useState ( { x : 0 , y : 0 } ) ;
7
14
const [ isMobile , setIsMobile ] = useState ( false ) ;
15
+ const containerRef = useRef ( null ) ;
8
16
9
17
// 检测是否为移动端
10
18
useEffect ( ( ) => {
@@ -28,7 +36,7 @@ const StarryBackground = ({ starCount = 240 }) => {
28
36
newStars . push ( {
29
37
id : i ,
30
38
x : Math . random ( ) * 100 , // 0-100%
31
- y : Math . pow ( Math . random ( ) , 2 ) * 100 , // Skew distribution towards the top
39
+ y : Math . random ( ) * 100 , // 0-100%
32
40
layer : Math . floor ( Math . random ( ) * 3 ) + 1 , // 1, 2, 3 三层
33
41
} ) ;
34
42
}
@@ -39,6 +47,42 @@ const StarryBackground = ({ starCount = 240 }) => {
39
47
generateStars ( ) ;
40
48
} , [ starCount , isMobile ] ) ; // 添加isMobile作为依赖项
41
49
50
+ // 监听鼠标移动
51
+ useEffect ( ( ) => {
52
+ const handleMouseMove = ( e ) => {
53
+ if ( containerRef . current ) {
54
+ const rect = containerRef . current . getBoundingClientRect ( ) ;
55
+ const centerX = rect . width / 2 ;
56
+ const centerY = rect . height / 2 ;
57
+
58
+ // 计算鼠标相对于容器中心的位置
59
+ const x = ( e . clientX - rect . left - centerX ) / centerX ;
60
+ const y = ( e . clientY - rect . top - centerY ) / centerY ;
61
+
62
+ setMousePosition ( { x, y } ) ;
63
+ }
64
+ } ;
65
+
66
+ window . addEventListener ( 'mousemove' , handleMouseMove ) ;
67
+ return ( ) => window . removeEventListener ( 'mousemove' , handleMouseMove ) ;
68
+ } , [ ] ) ;
69
+
70
+ // 计算星星的位移
71
+ const getStarTransform = ( star ) => {
72
+ // 不同层级的鼠标位移系数(与鼠标移动方向相反)
73
+ const mouseLayerMultipliers = {
74
+ 1 : 1 , // 最上层,位移最小
75
+ 2 : 2 , // 中间层
76
+ 3 : 4 , // 最下层,位移最大
77
+ } ;
78
+ const mouseMultiplier = mouseLayerMultipliers [ star . layer ] ?? 1 ;
79
+
80
+ const offsetX = - mousePosition . x * mouseMultiplier ;
81
+ const offsetY = - mousePosition . y * mouseMultiplier ;
82
+
83
+ return `translate(${ offsetX } px, ${ offsetY } px)` ;
84
+ } ;
85
+
42
86
// 根据层级获取星星的透明度和大小
43
87
const getStarStyle = ( star ) => {
44
88
const layerStyles = {
@@ -47,21 +91,21 @@ const StarryBackground = ({ starCount = 240 }) => {
47
91
3 : { opacity : 1 , size : '4px' } , // 最下层,最亮
48
92
} ;
49
93
50
- const style = layerStyles [ star . layer ] ;
94
+ const style = layerStyles [ star . layer ] ?? layerStyles [ 1 ] ;
51
95
52
96
return {
53
97
opacity : style . opacity ,
54
98
width : style . size ,
55
99
height : style . size ,
56
- // 保持静止:不使用 transform/transition
57
- transform : 'none' ,
58
- transition : 'none' ,
100
+ transform : getStarTransform ( star ) ,
101
+ transition : 'transform 0.1s ease-out' ,
59
102
} ;
60
103
} ;
61
104
62
105
return (
63
106
< div
64
- className = "absolute top-0 left-0 w-full h-full z-0 overflow-hidden bg-[#191E1B]"
107
+ ref = { containerRef }
108
+ className = "fixed top-0 left-0 w-full h-full z-0 pointer-events-none overflow-hidden bg-[#191E1B]"
65
109
>
66
110
{ stars . map ( ( star ) => (
67
111
< div
@@ -76,6 +120,6 @@ const StarryBackground = ({ starCount = 240 }) => {
76
120
) ) }
77
121
</ div >
78
122
) ;
79
- } ;
123
+ }
80
124
81
125
export default StarryBackground ;
0 commit comments