@@ -3,6 +3,7 @@ import * as THREE from 'three';
33
44export function ForgeVisualization ( ) {
55 const mountRef = useRef < HTMLDivElement > ( null ) ;
6+ const pointerRef = useRef ( { x : 0 , y : 0 } ) ;
67
78 useEffect ( ( ) => {
89 const mount = mountRef . current ;
@@ -91,6 +92,7 @@ export function ForgeVisualization() {
9192 let lastForge = 0 ;
9293 const forgeIntervalMs = 2200 ;
9394 const clock = new THREE . Clock ( ) ;
95+ let userPulse = 0 ;
9496
9597 const triggerForge = ( ) => {
9698 fireLight . intensity = 3.7 ;
@@ -108,6 +110,21 @@ export function ForgeVisualization() {
108110 sparkGeo . attributes . position . needsUpdate = true ;
109111 } ;
110112
113+ const onPointerMove = ( event : PointerEvent ) => {
114+ if ( ! mount ) return ;
115+ const rect = mount . getBoundingClientRect ( ) ;
116+ const x = ( ( event . clientX - rect . left ) / rect . width ) * 2 - 1 ;
117+ const y = - ( ( ( event . clientY - rect . top ) / rect . height ) * 2 - 1 ) ;
118+ pointerRef . current . x = x ;
119+ pointerRef . current . y = y ;
120+ userPulse = 0.7 ;
121+ } ;
122+
123+ const onPointerDown = ( ) => {
124+ triggerForge ( ) ;
125+ userPulse = 1 ;
126+ } ;
127+
111128 const animate = ( ) => {
112129 const elapsed = clock . getElapsedTime ( ) ;
113130 const delta = Math . min ( clock . getDelta ( ) , 0.03 ) ;
@@ -148,8 +165,15 @@ export function ForgeVisualization() {
148165 }
149166 sparkGeo . attributes . position . needsUpdate = true ;
150167
151- camera . position . x = Math . sin ( elapsed * 0.24 ) * 0.85 ;
152- camera . lookAt ( 0 , 0.35 , 0 ) ;
168+ const targetX = Math . sin ( elapsed * 0.24 ) * 0.85 + pointerRef . current . x * 0.45 ;
169+ const targetY = 1.6 + pointerRef . current . y * 0.28 ;
170+ camera . position . x += ( targetX - camera . position . x ) * 0.06 ;
171+ camera . position . y += ( targetY - camera . position . y ) * 0.06 ;
172+ camera . lookAt ( pointerRef . current . x * 0.2 , 0.35 + pointerRef . current . y * 0.1 , 0 ) ;
173+
174+ userPulse = Math . max ( 0 , userPulse - delta * 1.8 ) ;
175+ sparks . material . opacity = 0.72 + userPulse * 0.2 ;
176+ fireLight . color . setHSL ( 0.06 + userPulse * 0.04 , 1 , 0.5 ) ;
153177
154178 renderer . render ( scene , camera ) ;
155179 frame = requestAnimationFrame ( animate ) ;
@@ -164,10 +188,14 @@ export function ForgeVisualization() {
164188 renderer . setSize ( mount . clientWidth , mount . clientHeight ) ;
165189 } ;
166190 window . addEventListener ( 'resize' , onResize ) ;
191+ mount . addEventListener ( 'pointermove' , onPointerMove ) ;
192+ mount . addEventListener ( 'pointerdown' , onPointerDown ) ;
167193
168194 return ( ) => {
169195 cancelAnimationFrame ( frame ) ;
170196 window . removeEventListener ( 'resize' , onResize ) ;
197+ mount . removeEventListener ( 'pointermove' , onPointerMove ) ;
198+ mount . removeEventListener ( 'pointerdown' , onPointerDown ) ;
171199 renderer . dispose ( ) ;
172200 sparkGeo . dispose ( ) ;
173201 mount . removeChild ( renderer . domElement ) ;
0 commit comments