@@ -5,10 +5,29 @@ class Mouse {
55 } ) {
66 this . cursor = document . createElement ( 'div' ) ;
77 this . cursor . className = 'custom-cursor' ;
8+
9+ // 设置初始样式和硬件加速
10+ this . cursor . style . cssText = `
11+ position: fixed;
12+ top: 0;
13+ left: 0;
14+ width: 48px;
15+ height: 48px;
16+ background-size: contain;
17+ background-repeat: no-repeat;
18+ background-position: center;
19+ pointer-events: none;
20+ z-index: 10000;
21+ transform-origin: center;
22+ will-change: transform;
23+ transition: none;
24+ ` ;
25+
826 document . body . appendChild ( this . cursor ) ;
927
1028 this . defaultCursor = defaultCursor ;
1129 this . clickCursor = clickCursor ;
30+ this . currentCursor = null ; // 缓存当前光标状态
1231
1332 this . visible = true ;
1433
@@ -18,52 +37,119 @@ class Mouse {
1837 init ( ) {
1938 this . setCursorImage ( this . defaultCursor ) ;
2039
21- // Move
22- document . addEventListener ( 'mousemove' , ( e ) => {
23- this . showCursor ( ) ;
24- const isLink = e . target . tagName === 'A' || e . target . tagName === 'BUTTON' ;
25- const cursorImg = isLink ? this . clickCursor : this . defaultCursor ;
26- const transform = isLink ? 'scale(1.5)' : 'scale(1.5) rotate(15deg)' ;
40+ // 使用 requestAnimationFrame 优化鼠标移动
41+ let animationId ;
42+ const updateCursor = ( e ) => {
43+ if ( animationId ) {
44+ cancelAnimationFrame ( animationId ) ;
45+ }
2746
28- this . setCursorImage ( cursorImg ) ;
29- this . cursor . style . transform = transform ;
30- this . cursor . style . left = `${ e . clientX } px` ;
31- this . cursor . style . top = `${ e . clientY } px` ;
32- } ) ;
47+ animationId = requestAnimationFrame ( ( ) => {
48+ this . showCursor ( ) ;
49+
50+ // 检查是否为可交互元素
51+ const isInteractive = this . isInteractiveElement ( e . target ) ;
52+ const cursorImg = isInteractive ? this . clickCursor : this . defaultCursor ;
53+
54+ // 只在需要时更新光标图片
55+ this . setCursorImage ( cursorImg ) ;
56+
57+ // 使用 translate3d 进行硬件加速的位置更新
58+ const x = e . clientX - 24 ; // 居中偏移
59+ const y = e . clientY - 24 ;
60+ const scale = isInteractive ? 1.2 : 1.0 ;
61+ const rotation = isInteractive ? 0 : 15 ;
62+
63+ this . cursor . style . transform = `translate3d(${ x } px, ${ y } px, 0) scale(${ scale } ) rotate(${ rotation } deg)` ;
64+ } ) ;
65+ } ;
66+
67+ // Move
68+ document . addEventListener ( 'mousemove' , updateCursor , { passive : true } ) ;
3369
3470 // Down
3571 document . addEventListener ( 'mousedown' , ( ) => {
3672 this . setCursorImage ( this . clickCursor ) ;
37- this . cursor . style . transform = 'scale(1.5)' ;
73+ // 保持当前位置,只改变缩放
74+ const currentTransform = this . cursor . style . transform ;
75+ const scaleMatch = currentTransform . match ( / s c a l e \( [ \d . ] + \) / ) ;
76+ const rotateMatch = currentTransform . match ( / r o t a t e \( [ \d . - ] + d e g \) / ) ;
77+ const translateMatch = currentTransform . match ( / t r a n s l a t e 3 d \( [ ^ ) ] + \) / ) ;
78+
79+ if ( translateMatch ) {
80+ this . cursor . style . transform = `${ translateMatch [ 0 ] } scale(1.1) rotate(0deg)` ;
81+ }
3882 } ) ;
3983
4084 // Up
41- document . addEventListener ( 'mouseup' , ( ) => {
42- this . setCursorImage ( this . defaultCursor ) ;
43- this . cursor . style . transform = 'scale(1.5) rotate(15deg)' ;
85+ document . addEventListener ( 'mouseup' , ( e ) => {
86+ const isInteractive = this . isInteractiveElement ( e . target ) ;
87+ const cursorImg = isInteractive ? this . clickCursor : this . defaultCursor ;
88+ const scale = isInteractive ? 1.2 : 1.0 ;
89+ const rotation = isInteractive ? 0 : 15 ;
90+
91+ this . setCursorImage ( cursorImg ) ;
92+
93+ const currentTransform = this . cursor . style . transform ;
94+ const translateMatch = currentTransform . match ( / t r a n s l a t e 3 d \( [ ^ ) ] + \) / ) ;
95+
96+ if ( translateMatch ) {
97+ this . cursor . style . transform = `${ translateMatch [ 0 ] } scale(${ scale } ) rotate(${ rotation } deg)` ;
98+ }
4499 } ) ;
45100
46101 // Hide native cursor
47102 document . body . style . cursor = 'none' ;
48103
49- // Handle leave/enter
104+ // Handle leave/enter with proper visibility
50105 document . addEventListener ( 'mouseleave' , ( ) => this . hideCursor ( ) ) ;
51106 document . addEventListener ( 'mouseenter' , ( ) => this . showCursor ( ) ) ;
52107 }
53108
109+ // 检查元素是否可交互
110+ isInteractiveElement ( element ) {
111+ if ( ! element ) return false ;
112+
113+ // 检查标签类型
114+ const interactiveTags = [ 'A' , 'BUTTON' , 'INPUT' , 'SELECT' , 'TEXTAREA' ] ;
115+ if ( interactiveTags . includes ( element . tagName ) ) {
116+ return true ;
117+ }
118+
119+ // 检查是否有点击事件或cursor样式
120+ const styles = window . getComputedStyle ( element ) ;
121+ if ( styles . cursor === 'pointer' ) {
122+ return true ;
123+ }
124+
125+ // 检查是否有特定的类名
126+ const interactiveClasses = [ 'option' , 'start-btn' , 'submit' , 'analysis' , 'continue-btn' , 'next' , 'prev' ] ;
127+ if ( interactiveClasses . some ( cls => element . classList . contains ( cls ) ) ) {
128+ return true ;
129+ }
130+
131+ return false ;
132+ }
133+
54134 setCursorImage ( src ) {
55- if ( this . cursor . style . backgroundImage !== `url("${ src } ")` ) {
135+ // 只在光标图片真正需要改变时更新
136+ if ( this . currentCursor !== src ) {
56137 this . cursor . style . backgroundImage = `url("${ src } ")` ;
138+ this . currentCursor = src ;
57139 }
58140 }
59141
60142 hideCursor ( ) {
61143 this . visible = false ;
144+ this . cursor . style . opacity = '0' ;
145+ this . cursor . style . pointerEvents = 'none' ;
62146 }
63147
64148 showCursor ( ) {
65149 if ( ! this . visible ) {
66150 this . visible = true ;
151+ this . cursor . style . opacity = '1' ;
152+ this . cursor . style . pointerEvents = 'none' ;
67153 }
68154 }
69155}
0 commit comments