11import { useEffect , useRef , useState } from 'react' ;
2+ import PropTypes from 'prop-types' ;
23import * as THREE from 'three' ;
34
45const Starfield = ( { onSkyboxLoaded = ( ) => { } , uiVisible = false } ) => {
@@ -10,10 +11,17 @@ const Starfield = ({ onSkyboxLoaded = () => { }, uiVisible = false }) => {
1011 const animationFrameRef = useRef ( null ) ;
1112
1213 // Component state
13- const [ skyboxLoaded , setSkyboxLoaded ] = useState ( false ) ;
1414 const [ starsVisible , setStarsVisible ] = useState ( false ) ;
1515 const [ starsAnimating , setStarsAnimating ] = useState ( false ) ;
1616
17+ // Use ref for callback to keep it stable and prevent useEffect re-runs
18+ const onSkyboxLoadedRef = useRef ( onSkyboxLoaded ) ;
19+
20+ // Update ref when callback changes, but don't trigger useEffect
21+ useEffect ( ( ) => {
22+ onSkyboxLoadedRef . current = onSkyboxLoaded ;
23+ } , [ onSkyboxLoaded ] ) ;
24+
1725 // Animation and scroll tracking references
1826 const scrollProgressRef = useRef ( 0 ) ;
1927 const scrollVelocityRef = useRef ( 0 ) ;
@@ -75,8 +83,8 @@ const Starfield = ({ onSkyboxLoaded = () => { }, uiVisible = false }) => {
7583
7684 // Configuration constants (increased minimum sizes to prevent twinkling)
7785 const STAR_LAYER_CONFIG = {
78- VERY_DISTANT : { count : 3000 , distance : 800 , size : 0.6 , opacity : 0.4 } ,
79- FAR : { count : 2500 , distance : 600 , size : 0.7 , opacity : 0.5 } ,
86+ // VERY_DISTANT: { count: 3000, distance: 800, size: 0.6, opacity: 0.4 },
87+ // FAR: { count: 2500, distance: 600, size: 0.7, opacity: 0.5 },
8088 DISTANT : { count : 2000 , distance : 450 , size : 0.8 , opacity : 0.6 } ,
8189 MEDIUM_FAR : { count : 1500 , distance : 350 , size : 0.9 , opacity : 0.7 } ,
8290 MEDIUM : { count : 1000 , distance : 250 , size : 1.0 , opacity : 0.8 }
@@ -212,11 +220,16 @@ const Starfield = ({ onSkyboxLoaded = () => { }, uiVisible = false }) => {
212220
213221 // Create and add skybox mesh
214222 skyboxMesh = new THREE . Mesh ( sphereGeometry , skyboxMaterial ) ;
223+
224+ // Initial skybox positioning
225+ skyboxMesh . rotation . x = Math . PI * - 0.1 ;
226+ skyboxMesh . rotation . y = Math . PI * 0 ;
227+ skyboxMesh . rotation . z = Math . PI * 0 ;
228+
215229 scene . add ( skyboxMesh ) ;
216230
217231 // Initialize stars and notify completion
218- setSkyboxLoaded ( true ) ;
219- onSkyboxLoaded ( ) ;
232+ onSkyboxLoadedRef . current ( ) ;
220233 setStarsVisible ( true ) ;
221234
222235 // Start the animation loop
@@ -226,8 +239,7 @@ const Starfield = ({ onSkyboxLoaded = () => { }, uiVisible = false }) => {
226239 ( error ) => {
227240 console . error ( 'Failed to load galaxy texture:' , error ) ;
228241 // No fallback - just proceed with black background and stars
229- setSkyboxLoaded ( true ) ;
230- onSkyboxLoaded ( ) ;
242+ onSkyboxLoadedRef . current ( ) ;
231243 setStarsVisible ( true ) ;
232244 animate ( ) ;
233245 }
@@ -236,8 +248,8 @@ const Starfield = ({ onSkyboxLoaded = () => { }, uiVisible = false }) => {
236248 // Create star layers with fade-in effect
237249 const starLayers = [ ] ;
238250 const layerConfigs = [
239- STAR_LAYER_CONFIG . VERY_DISTANT ,
240- STAR_LAYER_CONFIG . FAR ,
251+ // STAR_LAYER_CONFIG.VERY_DISTANT,
252+ // STAR_LAYER_CONFIG.FAR,
241253 STAR_LAYER_CONFIG . DISTANT ,
242254 STAR_LAYER_CONFIG . MEDIUM_FAR ,
243255 STAR_LAYER_CONFIG . MEDIUM
@@ -366,17 +378,14 @@ const Starfield = ({ onSkyboxLoaded = () => { }, uiVisible = false }) => {
366378 skyboxMesh . material . opacity += ANIMATION_CONFIG . skyboxFadeSpeed ;
367379 }
368380
369- // Calculate scroll-based motion
370- const scrollProgress = scrollProgressRef . current ;
371-
372381 // Decay scroll velocity when not actively scrolling
373382 scrollVelocityRef . current *= ANIMATION_CONFIG . velocityDecay ;
374383
375384 // Convert scroll velocity to speed multiplier (restore original values)
376385 const velocityFactor = Math . min ( scrollVelocityRef . current / ANIMATION_CONFIG . maxVelocity , 1.0 ) ;
377386 const scrollAmplification = 2.5 + velocityFactor * 40 ;
378387
379- starLayersRef . current . forEach ( ( starLayer , index ) => {
388+ starLayersRef . current . forEach ( ( starLayer ) => {
380389 if ( starLayer && starLayer . userData ) {
381390 const distance = starLayer . userData . distance ;
382391
@@ -446,7 +455,7 @@ const Starfield = ({ onSkyboxLoaded = () => { }, uiVisible = false }) => {
446455
447456 // Return cleanup function
448457 return cleanup ;
449- } , [ ] ) ;
458+ } , [ ] ) ; // Empty dependency array - only run once
450459
451460 return (
452461 < div
@@ -464,4 +473,10 @@ const Starfield = ({ onSkyboxLoaded = () => { }, uiVisible = false }) => {
464473 ) ;
465474} ;
466475
476+ // PropTypes validation
477+ Starfield . propTypes = {
478+ onSkyboxLoaded : PropTypes . func ,
479+ uiVisible : PropTypes . bool
480+ } ;
481+
467482export default Starfield ;
0 commit comments