22 import { onMount , onDestroy } from ' svelte' ;
33 import { browser } from ' $app/environment' ;
44 import ThreeBackgroundFallback from ' ./ThreeBackgroundFallback.svelte' ;
5+ import LoadingSpinner from ' ./LoadingSpinner.svelte' ;
6+ import { getDeviceInfo , getPerformanceRecommendations } from ' ../utils/deviceDetection.js' ;
57
68 let canvas;
79 let scene, camera, renderer;
1113 let mouseX = 0 , mouseY = 0 ;
1214 let threeFailed = false ;
1315 let showFallback = false ;
16+ let isLoading = false ;
17+ let isReady = false ;
18+ let performanceSettings = {
19+ particles: 1000 ,
20+ quality: ' high' ,
21+ enableAnimations: true
22+ };
1423
1524 onMount (async () => {
1625 if (! browser) return ;
2332 }
2433
2534 try {
35+ isLoading = true ;
36+
37+ // Get device info and performance recommendations
38+ const deviceInfo = getDeviceInfo ();
39+ const recommendations = getPerformanceRecommendations (deviceInfo);
40+ performanceSettings = recommendations;
41+
42+ // Skip 3D on very low-end devices
43+ if (deviceInfo .isLowEnd && ! deviceInfo .supportsWebGL ) {
44+ throw new Error (' Device not suitable for 3D' );
45+ }
46+
2647 // Check for WebGL support
27- if (! window .WebGLRenderingContext ) {
48+ if (! window .WebGLRenderingContext || ! deviceInfo . supportsWebGL ) {
2849 throw new Error (' WebGL not supported' );
2950 }
3051
31- const canvas = document .createElement (' canvas' );
32- const gl = canvas .getContext (' webgl' ) || canvas .getContext (' experimental-webgl' );
52+ const testCanvas = document .createElement (' canvas' );
53+ const gl = testCanvas .getContext (' webgl' ) || testCanvas .getContext (' experimental-webgl' );
3354 if (! gl) {
3455 throw new Error (' WebGL context not available' );
3556 }
3960 animate ();
4061 window .addEventListener (' resize' , handleResize);
4162 window .addEventListener (' mousemove' , handleMouseMove);
63+
64+ // Add a small delay to ensure smooth loading
65+ setTimeout (() => {
66+ isLoading = false ;
67+ isReady = true ;
68+ }, 500 );
4269 } catch (error) {
4370 console .warn (' 3D background failed to initialize, using fallback:' , error);
71+ isLoading = false ;
4472 threeFailed = true ;
4573 showFallback = true ;
4674 }
89117 mesh = new THREE.Mesh (geometry, material);
90118 scene .add (mesh);
91119
92- // Add particles
93- particleGeometry = new THREE.BufferGeometry ();
94- const particleCount = 1000 ;
95- const positions = new Float32Array (particleCount * 3 );
96-
97- for (let i = 0 ; i < particleCount * 3 ; i += 3 ) {
98- positions[i] = (Math .random () - 0.5 ) * 10 ;
99- positions[i + 1 ] = (Math .random () - 0.5 ) * 10 ;
100- positions[i + 2 ] = (Math .random () - 0.5 ) * 10 ;
120+ // Add particles based on performance settings
121+ if (performanceSettings .enableAnimations ) {
122+ particleGeometry = new THREE.BufferGeometry ();
123+ const particleCount = performanceSettings .particles || 1000 ;
124+ const positions = new Float32Array (particleCount * 3 );
125+
126+ for (let i = 0 ; i < particleCount * 3 ; i += 3 ) {
127+ positions[i] = (Math .random () - 0.5 ) * 10 ;
128+ positions[i + 1 ] = (Math .random () - 0.5 ) * 10 ;
129+ positions[i + 2 ] = (Math .random () - 0.5 ) * 10 ;
130+ }
131+
132+ particleGeometry .setAttribute (' position' , new THREE.BufferAttribute (positions, 3 ));
133+
134+ particleMaterial = new THREE.PointsMaterial ({
135+ color: 0x3b82f6 ,
136+ size: performanceSettings .quality === ' high' ? 0.02 : 0.01 ,
137+ transparent: true ,
138+ opacity: performanceSettings .quality === ' low' ? 0.3 : 0.6 ,
139+ blending: THREE .AdditiveBlending
140+ });
141+
142+ particles = new THREE.Points (particleGeometry, particleMaterial);
143+ scene .add (particles);
101144 }
102-
103- particleGeometry .setAttribute (' position' , new THREE.BufferAttribute (positions, 3 ));
104-
105- particleMaterial = new THREE.PointsMaterial ({
106- color: 0x3b82f6 ,
107- size: 0.02 ,
108- transparent: true ,
109- opacity: 0.6 ,
110- blending: THREE .AdditiveBlending
111- });
112-
113- particles = new THREE.Points (particleGeometry, particleMaterial);
114- scene .add (particles);
115145
116146 // Add lights
117147 const ambientLight = new THREE.AmbientLight (0xffffff , 0.5 );
159189
160190{#if showFallback }
161191 <ThreeBackgroundFallback />
192+ {:else if isLoading }
193+ <div class =" three-loading" >
194+ <LoadingSpinner size =" medium" message =" Initializing 3D background..." />
195+ </div >
162196{:else }
163- <canvas bind:this ={canvas } class =" three-canvas" aria-hidden =" true" ></canvas >
197+ <canvas bind:this ={canvas } class ="three-canvas" aria-hidden ="true" class:ready ={ isReady } ></canvas >
164198{/if }
165199
166200<style >
172206 height : 100% ;
173207 pointer-events : none ;
174208 z-index : 0 ;
209+ opacity : 0 ;
210+ transition : opacity 0.5s ease ;
211+ }
212+
213+ .three-canvas.ready {
214+ opacity : 1 ;
215+ }
216+
217+ .three-loading {
218+ position : fixed ;
219+ top : 0 ;
220+ left : 0 ;
221+ width : 100% ;
222+ height : 100% ;
223+ display : flex ;
224+ align-items : center ;
225+ justify-content : center ;
226+ pointer-events : none ;
227+ z-index : 0 ;
228+ background : rgba (0 , 0 , 0 , 0.02 );
229+ backdrop-filter : blur (1px );
230+ }
231+
232+ :global(html .dark ) .three-loading {
233+ background : rgba (0 , 0 , 0 , 0.1 );
175234 }
176235 </style >
0 commit comments