@@ -8,175 +8,174 @@ export default function Hero3D() {
88 const container = containerRef . current ;
99 if ( ! container ) return ;
1010
11- // Scene setup
12- const scene = new THREE . Scene ( ) ;
13- const camera = new THREE . PerspectiveCamera (
14- 60 ,
15- container . clientWidth / container . clientHeight ,
16- 0.1 ,
17- 1000
18- ) ;
19- camera . position . z = 35 ;
20-
21- const renderer = new THREE . WebGLRenderer ( { antialias : true , alpha : true } ) ;
22- renderer . setSize ( container . clientWidth , container . clientHeight ) ;
23- renderer . setPixelRatio ( Math . min ( window . devicePixelRatio , 2 ) ) ;
24- container . appendChild ( renderer . domElement ) ;
25-
26- // Group for entire interactive network
27- const networkGroup = new THREE . Group ( ) ;
28- scene . add ( networkGroup ) ;
29-
30- // Create central security node (Icosahedron wireframe + core)
31- const coreGeometry = new THREE . IcosahedronGeometry ( 6.5 , 1 ) ;
32- const coreMaterial = new THREE . MeshBasicMaterial ( {
33- color : 0x0ea5e9 ,
34- wireframe : true ,
35- transparent : true ,
36- opacity : 0.35 ,
37- } ) ;
38- const coreMesh = new THREE . Mesh ( coreGeometry , coreMaterial ) ;
39- networkGroup . add ( coreMesh ) ;
40-
41- // Inner glowing sphere
42- const innerGeo = new THREE . SphereGeometry ( 3.5 , 16 , 16 ) ;
43- const innerMat = new THREE . MeshBasicMaterial ( {
44- color : 0x38bdf8 ,
45- wireframe : false ,
46- transparent : true ,
47- opacity : 0.25 ,
48- } ) ;
49- const innerMesh = new THREE . Mesh ( innerGeo , innerMat ) ;
50- networkGroup . add ( innerMesh ) ;
51-
52- // Outer orbiting nodes / particles
53- const particleCount = 75 ;
54- const nodePositions = [ ] ;
55- const nodeGeometry = new THREE . BufferGeometry ( ) ;
56- const positions = new Float32Array ( particleCount * 3 ) ;
57- const colors = new Float32Array ( particleCount * 3 ) ;
58-
59- const color1 = new THREE . Color ( 0x38bdf8 ) ; // Sky blue
60- const color2 = new THREE . Color ( 0x818cf8 ) ; // Indigo
61- const color3 = new THREE . Color ( 0x2dd4bf ) ; // Teal
62-
63- for ( let i = 0 ; i < particleCount ; i ++ ) {
64- const radius = 12 + Math . random ( ) * 18 ;
65- const theta = Math . random ( ) * Math . PI * 2 ;
66- const phi = Math . acos ( Math . random ( ) * 2 - 1 ) ;
67-
68- const x = radius * Math . sin ( phi ) * Math . cos ( theta ) ;
69- const y = radius * Math . sin ( phi ) * Math . sin ( theta ) ;
70- const z = radius * Math . cos ( phi ) ;
71-
72- positions [ i * 3 ] = x ;
73- positions [ i * 3 + 1 ] = y ;
74- positions [ i * 3 + 2 ] = z ;
75-
76- nodePositions . push ( new THREE . Vector3 ( x , y , z ) ) ;
77-
78- const mixedColor = i % 3 === 0 ? color1 : i % 3 === 1 ? color2 : color3 ;
79- colors [ i * 3 ] = mixedColor . r ;
80- colors [ i * 3 + 1 ] = mixedColor . g ;
81- colors [ i * 3 + 2 ] = mixedColor . b ;
82- }
11+ let renderer , scene , camera , animationFrameId ;
12+ let clock = new THREE . Clock ( ) ;
8313
84- nodeGeometry . setAttribute ( 'position' , new THREE . BufferAttribute ( positions , 3 ) ) ;
85- nodeGeometry . setAttribute ( 'color' , new THREE . BufferAttribute ( colors , 3 ) ) ;
86-
87- const particleMaterial = new THREE . PointsMaterial ( {
88- size : 0.9 ,
89- vertexColors : true ,
90- transparent : true ,
91- opacity : 0.85 ,
92- } ) ;
93-
94- const particles = new THREE . Points ( nodeGeometry , particleMaterial ) ;
95- networkGroup . add ( particles ) ;
96-
97- // Connecting lines between nearby nodes
98- const lineMaterial = new THREE . LineBasicMaterial ( {
99- color : 0x38bdf8 ,
100- transparent : true ,
101- opacity : 0.15 ,
102- } ) ;
103-
104- const linePositions = [ ] ;
105- for ( let i = 0 ; i < nodePositions . length ; i ++ ) {
106- for ( let j = i + 1 ; j < nodePositions . length ; j ++ ) {
107- const dist = nodePositions [ i ] . distanceTo ( nodePositions [ j ] ) ;
108- if ( dist < 10 ) {
109- linePositions . push (
110- nodePositions [ i ] . x , nodePositions [ i ] . y , nodePositions [ i ] . z ,
111- nodePositions [ j ] . x , nodePositions [ j ] . y , nodePositions [ j ] . z
112- ) ;
14+ try {
15+ const width = container . clientWidth || window . innerWidth || 800 ;
16+ const height = container . clientHeight || window . innerHeight || 600 ;
17+
18+ scene = new THREE . Scene ( ) ;
19+ camera = new THREE . PerspectiveCamera ( 60 , width / height , 0.1 , 1000 ) ;
20+ camera . position . z = 35 ;
21+
22+ renderer = new THREE . WebGLRenderer ( { antialias : true , alpha : true } ) ;
23+ renderer . setSize ( width , height ) ;
24+ renderer . setPixelRatio ( Math . min ( window . devicePixelRatio || 1 , 2 ) ) ;
25+ container . appendChild ( renderer . domElement ) ;
26+
27+ const networkGroup = new THREE . Group ( ) ;
28+ scene . add ( networkGroup ) ;
29+
30+ // Core wireframe
31+ const coreGeometry = new THREE . IcosahedronGeometry ( 6.5 , 1 ) ;
32+ const coreMaterial = new THREE . MeshBasicMaterial ( {
33+ color : 0x0ea5e9 ,
34+ wireframe : true ,
35+ transparent : true ,
36+ opacity : 0.35 ,
37+ } ) ;
38+ const coreMesh = new THREE . Mesh ( coreGeometry , coreMaterial ) ;
39+ networkGroup . add ( coreMesh ) ;
40+
41+ // Inner glowing sphere
42+ const innerGeo = new THREE . SphereGeometry ( 3.5 , 16 , 16 ) ;
43+ const innerMat = new THREE . MeshBasicMaterial ( {
44+ color : 0x38bdf8 ,
45+ wireframe : false ,
46+ transparent : true ,
47+ opacity : 0.25 ,
48+ } ) ;
49+ const innerMesh = new THREE . Mesh ( innerGeo , innerMat ) ;
50+ networkGroup . add ( innerMesh ) ;
51+
52+ // Particles
53+ const particleCount = 65 ;
54+ const nodePositions = [ ] ;
55+ const nodeGeometry = new THREE . BufferGeometry ( ) ;
56+ const positions = new Float32Array ( particleCount * 3 ) ;
57+ const colors = new Float32Array ( particleCount * 3 ) ;
58+
59+ const color1 = new THREE . Color ( 0x38bdf8 ) ;
60+ const color2 = new THREE . Color ( 0x818cf8 ) ;
61+ const color3 = new THREE . Color ( 0x2dd4bf ) ;
62+
63+ for ( let i = 0 ; i < particleCount ; i ++ ) {
64+ const radius = 12 + Math . random ( ) * 16 ;
65+ const theta = Math . random ( ) * Math . PI * 2 ;
66+ const phi = Math . acos ( Math . random ( ) * 2 - 1 ) ;
67+
68+ const x = radius * Math . sin ( phi ) * Math . cos ( theta ) ;
69+ const y = radius * Math . sin ( phi ) * Math . sin ( theta ) ;
70+ const z = radius * Math . cos ( phi ) ;
71+
72+ positions [ i * 3 ] = x ;
73+ positions [ i * 3 + 1 ] = y ;
74+ positions [ i * 3 + 2 ] = z ;
75+
76+ nodePositions . push ( new THREE . Vector3 ( x , y , z ) ) ;
77+
78+ const mixedColor = i % 3 === 0 ? color1 : i % 3 === 1 ? color2 : color3 ;
79+ colors [ i * 3 ] = mixedColor . r ;
80+ colors [ i * 3 + 1 ] = mixedColor . g ;
81+ colors [ i * 3 + 2 ] = mixedColor . b ;
82+ }
83+
84+ nodeGeometry . setAttribute ( 'position' , new THREE . BufferAttribute ( positions , 3 ) ) ;
85+ nodeGeometry . setAttribute ( 'color' , new THREE . BufferAttribute ( colors , 3 ) ) ;
86+
87+ const particleMaterial = new THREE . PointsMaterial ( {
88+ size : 0.9 ,
89+ vertexColors : true ,
90+ transparent : true ,
91+ opacity : 0.85 ,
92+ } ) ;
93+
94+ const particles = new THREE . Points ( nodeGeometry , particleMaterial ) ;
95+ networkGroup . add ( particles ) ;
96+
97+ // Connecting lines
98+ const linePositions = [ ] ;
99+ for ( let i = 0 ; i < nodePositions . length ; i ++ ) {
100+ for ( let j = i + 1 ; j < nodePositions . length ; j ++ ) {
101+ const dist = nodePositions [ i ] . distanceTo ( nodePositions [ j ] ) ;
102+ if ( dist < 10 ) {
103+ linePositions . push (
104+ nodePositions [ i ] . x , nodePositions [ i ] . y , nodePositions [ i ] . z ,
105+ nodePositions [ j ] . x , nodePositions [ j ] . y , nodePositions [ j ] . z
106+ ) ;
107+ }
113108 }
114109 }
115- }
116110
117- const lineGeometry = new THREE . BufferGeometry ( ) ;
118- lineGeometry . setAttribute ( 'position' , new THREE . Float32BufferAttribute ( linePositions , 3 ) ) ;
119- const lines = new THREE . LineSegments ( lineGeometry , lineMaterial ) ;
120- networkGroup . add ( lines ) ;
121-
122- // Mouse parallax
123- let mouseX = 0 ;
124- let mouseY = 0 ;
125- let targetX = 0 ;
126- let targetY = 0 ;
127-
128- const handleMouseMove = ( e ) => {
129- const rect = container . getBoundingClientRect ( ) ;
130- mouseX = ( ( e . clientX - rect . left ) / rect . width ) * 2 - 1 ;
131- mouseY = - ( ( ( e . clientY - rect . top ) / rect . height ) * 2 - 1 ) ;
132- } ;
133-
134- window . addEventListener ( 'mousemove' , handleMouseMove ) ;
135-
136- // Responsive resize handler
137- const handleResize = ( ) => {
138- if ( ! container ) return ;
139- camera . aspect = container . clientWidth / container . clientHeight ;
140- camera . updateProjectionMatrix ( ) ;
141- renderer . setSize ( container . clientWidth , container . clientHeight ) ;
142- } ;
143-
144- window . addEventListener ( 'resize' , handleResize ) ;
145-
146- // Animation loop
147- let animationFrameId ;
148- let clock = new THREE . Clock ( ) ;
111+ const lineGeometry = new THREE . BufferGeometry ( ) ;
112+ lineGeometry . setAttribute ( 'position' , new THREE . Float32BufferAttribute ( linePositions , 3 ) ) ;
113+ const lineMaterial = new THREE . LineBasicMaterial ( {
114+ color : 0x38bdf8 ,
115+ transparent : true ,
116+ opacity : 0.15 ,
117+ } ) ;
118+ const lines = new THREE . LineSegments ( lineGeometry , lineMaterial ) ;
119+ networkGroup . add ( lines ) ;
120+
121+ // Mouse follow
122+ let mouseX = 0 , mouseY = 0 , targetX = 0 , targetY = 0 ;
123+ const handleMouseMove = ( e ) => {
124+ const rect = container . getBoundingClientRect ( ) ;
125+ if ( rect . width > 0 && rect . height > 0 ) {
126+ mouseX = ( ( e . clientX - rect . left ) / rect . width ) * 2 - 1 ;
127+ mouseY = - ( ( ( e . clientY - rect . top ) / rect . height ) * 2 - 1 ) ;
128+ }
129+ } ;
130+
131+ window . addEventListener ( 'mousemove' , handleMouseMove ) ;
132+
133+ const handleResize = ( ) => {
134+ if ( ! container || ! renderer || ! camera ) return ;
135+ const w = container . clientWidth || window . innerWidth ;
136+ const h = container . clientHeight || window . innerHeight ;
137+ if ( w > 0 && h > 0 ) {
138+ camera . aspect = w / h ;
139+ camera . updateProjectionMatrix ( ) ;
140+ renderer . setSize ( w , h ) ;
141+ }
142+ } ;
149143
150- const animate = ( ) => {
151- animationFrameId = requestAnimationFrame ( animate ) ;
152- const elapsedTime = clock . getElapsedTime ( ) ;
153-
154- // Gentle continuous rotation
155- networkGroup . rotation . y = elapsedTime * 0.08 ;
156- networkGroup . rotation . x = Math . sin ( elapsedTime * 0.05 ) * 0.15 ;
157- coreMesh . rotation . y = - elapsedTime * 0.15 ;
158- coreMesh . rotation . z = elapsedTime * 0.05 ;
159-
160- // Smooth mouse follow
161- targetX += ( mouseX * 0.5 - targetX ) * 0.05 ;
162- targetY += ( mouseY * 0.5 - targetY ) * 0.05 ;
163- networkGroup . rotation . y += targetX * 0.2 ;
164- networkGroup . rotation . x -= targetY * 0.2 ;
165-
166- renderer . render ( scene , camera ) ;
167- } ;
168-
169- animate ( ) ;
170-
171- return ( ) => {
172- window . removeEventListener ( 'mousemove' , handleMouseMove ) ;
173- window . removeEventListener ( 'resize' , handleResize ) ;
174- cancelAnimationFrame ( animationFrameId ) ;
175- if ( container && renderer . domElement ) {
176- container . removeChild ( renderer . domElement ) ;
177- }
178- renderer . dispose ( ) ;
179- } ;
144+ window . addEventListener ( 'resize' , handleResize ) ;
145+
146+ const animate = ( ) => {
147+ animationFrameId = requestAnimationFrame ( animate ) ;
148+ const elapsedTime = clock . getElapsedTime ( ) ;
149+
150+ networkGroup . rotation . y = elapsedTime * 0.08 ;
151+ networkGroup . rotation . x = Math . sin ( elapsedTime * 0.05 ) * 0.15 ;
152+ coreMesh . rotation . y = - elapsedTime * 0.15 ;
153+ coreMesh . rotation . z = elapsedTime * 0.05 ;
154+
155+ targetX += ( mouseX * 0.5 - targetX ) * 0.05 ;
156+ targetY += ( mouseY * 0.5 - targetY ) * 0.05 ;
157+ networkGroup . rotation . y += targetX * 0.2 ;
158+ networkGroup . rotation . x -= targetY * 0.2 ;
159+
160+ renderer . render ( scene , camera ) ;
161+ } ;
162+
163+ animate ( ) ;
164+
165+ return ( ) => {
166+ window . removeEventListener ( 'mousemove' , handleMouseMove ) ;
167+ window . removeEventListener ( 'resize' , handleResize ) ;
168+ if ( animationFrameId ) cancelAnimationFrame ( animationFrameId ) ;
169+ if ( container && renderer ?. domElement ) {
170+ try {
171+ container . removeChild ( renderer . domElement ) ;
172+ } catch ( e ) { }
173+ }
174+ renderer ?. dispose ( ) ;
175+ } ;
176+ } catch ( err ) {
177+ console . warn ( 'Hero3D WebGL initialization skipped:' , err ) ;
178+ }
180179 } , [ ] ) ;
181180
182181 return (
0 commit comments