|
3 | 3 | --- |
4 | 4 |
|
5 | 5 | <div class="cosmic-background"> |
6 | | - <canvas class="galaxy-canvas"></canvas> |
7 | 6 | <div class="nebula-glow"></div> |
8 | 7 | <div class="galaxy-dust"></div> |
9 | 8 | <div class="star-clusters"></div> |
|
28 | 27 | ); |
29 | 28 | } |
30 | 29 |
|
31 | | - .galaxy-canvas { |
32 | | - position: absolute; |
33 | | - top: 0; |
34 | | - left: 0; |
35 | | - width: 100%; |
36 | | - height: 100%; |
37 | | - opacity: 1; |
38 | | - z-index: 10; /* Higher z-index but below content */ |
39 | | - pointer-events: all; /* Ensure mouse events are captured */ |
40 | | - } |
41 | 30 |
|
42 | 31 | .nebula-glow { |
43 | 32 | position: absolute; |
|
52 | 41 | radial-gradient(ellipse at 25% 75%, rgba(55, 218, 214, 0.1) 0%, transparent 60%), |
53 | 42 | radial-gradient(ellipse at 50% 50%, rgba(255, 255, 255, 0.05) 0%, transparent 100%); |
54 | 43 | filter: blur(20px); |
55 | | - animation: nebula-pulse 25s infinite alternate; |
56 | 44 | mix-blend-mode: screen; |
57 | 45 | z-index: 2; |
58 | 46 | } |
|
66 | 54 | background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 800 800'%3E%3Cg fill='none' stroke='%23FFFFFF' stroke-width='1'%3E%3Cpath d='M769 229L1037 260.9M927 880L731 737 520 660 309 538 40 599 295 764 126.5 879.5 40 599-197 493 102 382-31 229 126.5 79.5-69-63'/%3E%3Cpath d='M-31 229L237 261 390 382 603 493 308.5 537.5 101.5 381.5M370 905L295 764'/%3E%3Cpath d='M520 660L578 842 731 737 840 599 603 493 520 660 295 764 309 538 390 382 539 269 769 229 577.5 41.5 370 105 295 -36 126.5 79.5 237 261 102 382 40 599 -69 737 127 880'/%3E%3Cpath d='M520-140L578.5 42.5 731-63M603 493L539 269 237 261 370 105M902 382L539 269M390 382L102 382'/%3E%3Cpath d='M-222 42L126.5 79.5 370 105 539 269 577.5 41.5 927 80 769 229 902 382 603 493 731 737M295-36L577.5 41.5M578 842L295 764M40-201L127 80M102 382L-261 269'/%3E%3C/g%3E%3Cg fill='%23FFFFFF'%3E%3Ccircle cx='769' cy='229' r='1'/%3E%3Ccircle cx='539' cy='269' r='1'/%3E%3Ccircle cx='603' cy='493' r='1'/%3E%3Ccircle cx='731' cy='737' r='1'/%3E%3Ccircle cx='520' cy='660' r='1'/%3E%3Ccircle cx='309' cy='538' r='1'/%3E%3Ccircle cx='295' cy='764' r='1'/%3E%3Ccircle cx='40' cy='599' r='1'/%3E%3Ccircle cx='102' cy='382' r='1'/%3E%3Ccircle cx='127' cy='80' r='1'/%3E%3Ccircle cx='370' cy='105' r='1'/%3E%3Ccircle cx='578' cy='42' r='1'/%3E%3Ccircle cx='237' cy='261' r='1'/%3E%3Ccircle cx='390' cy='382' r='1'/%3E%3C/g%3E%3C/svg%3E"); |
67 | 55 | background-size: 150% 150%; |
68 | 56 | opacity: 0.05; |
69 | | - animation: dust-drift 120s linear infinite; |
70 | 57 | z-index: 3; |
71 | 58 | } |
72 | 59 |
|
|
96 | 83 | radial-gradient(1.5px 1.5px at 80% 90%, rgba(135, 206, 250, 0.8) 1%, transparent 2%), |
97 | 84 | radial-gradient(1px 1px at 90% 10%, rgba(255, 255, 255, 0.7) 1%, transparent 2%); |
98 | 85 | background-size: 500% 500%; |
99 | | - animation: |
100 | | - stars-twinkle 8s infinite alternate, |
101 | | - stars-move 150s linear infinite; |
102 | 86 | } |
103 | 87 |
|
104 | 88 | .star-clusters::after { |
|
118 | 102 | radial-gradient(1.5px 1.5px at 95% 95%, rgba(135, 206, 250, 0.8) 1%, transparent 2%); |
119 | 103 | background-size: 300% 300%; |
120 | 104 | transform: rotate(30deg); |
121 | | - animation: |
122 | | - stars-twinkle 10s infinite alternate-reverse, |
123 | | - stars-move 200s linear infinite reverse; |
124 | 105 | } |
125 | 106 |
|
126 | | - @keyframes nebula-pulse { |
127 | | - 0% { |
128 | | - opacity: 0.1; |
129 | | - filter: blur(15px); |
130 | | - } |
131 | | - 50% { |
132 | | - opacity: 0.15; |
133 | | - filter: blur(20px); |
134 | | - } |
135 | | - 100% { |
136 | | - opacity: 0.1; |
137 | | - filter: blur(15px); |
138 | | - } |
139 | | - } |
140 | 107 |
|
141 | | - @keyframes dust-drift { |
142 | | - 0% { |
143 | | - background-position: 0% 0%; |
144 | | - opacity: 0.05; |
145 | | - } |
146 | | - 50% { |
147 | | - opacity: 0.08; |
148 | | - } |
149 | | - 100% { |
150 | | - background-position: 100% 100%; |
151 | | - opacity: 0.05; |
152 | | - } |
153 | | - } |
154 | | - |
155 | | - @keyframes stars-twinkle { |
156 | | - 0% { |
157 | | - opacity: 0.4; |
158 | | - } |
159 | | - 50% { |
160 | | - opacity: 0.7; |
161 | | - } |
162 | | - 100% { |
163 | | - opacity: 0.6; |
164 | | - } |
165 | | - } |
166 | | - |
167 | | - @keyframes stars-move { |
168 | | - 0% { |
169 | | - background-position: 0% 0%; |
170 | | - } |
171 | | - 100% { |
172 | | - background-position: 100% 100%; |
173 | | - } |
174 | | - } |
175 | | - |
176 | | - @keyframes spin { |
177 | | - 0% { |
178 | | - transform: rotate(0deg); |
179 | | - } |
180 | | - 100% { |
181 | | - transform: rotate(360deg); |
182 | | - } |
183 | | - } |
184 | | - |
185 | | - @keyframes orbit { |
186 | | - 0% { |
187 | | - transform: rotate(0deg) translateX(var(--orbit-distance)) rotate(0deg); |
188 | | - } |
189 | | - 100% { |
190 | | - transform: rotate(360deg) translateX(var(--orbit-distance)) rotate(-360deg); |
191 | | - } |
192 | | - } |
193 | | - |
194 | | - @media (prefers-reduced-motion: reduce) { |
195 | | - .nebula-glow, |
196 | | - .galaxy-dust, |
197 | | - .star-clusters, |
198 | | - .galaxy-canvas { |
199 | | - animation: none !important; |
200 | | - transition: none !important; |
201 | | - } |
202 | | - .star-clusters::before, |
203 | | - .star-clusters::after { |
204 | | - animation: none !important; |
205 | | - } |
206 | | - .galaxy-canvas { |
207 | | - display: none !important; |
208 | | - } |
209 | | - } |
210 | 108 |
|
211 | 109 | /* Dark mode adjustments */ |
212 | 110 | :root[data-theme="dark"] .cosmic-background { |
|
248 | 146 | return; |
249 | 147 | } |
250 | 148 |
|
251 | | - // Skip animation in Firefox or if reduced motion is preferred |
| 149 | + // Skip animation in Firefox, if reduced motion is preferred, or on low-end devices |
| 150 | + const isLowEnd = navigator.hardwareConcurrency <= 2 || navigator.deviceMemory <= 4; |
252 | 151 | if ( |
253 | 152 | navigator.userAgent.match(/firefox/i) || |
254 | | - window.matchMedia("(prefers-reduced-motion: reduce)").matches |
| 153 | + window.matchMedia("(prefers-reduced-motion: reduce)").matches || |
| 154 | + isLowEnd |
255 | 155 | ) { |
256 | | - // Animation disabled due to browser type or reduced motion preference |
| 156 | + // Animation disabled due to browser type, reduced motion preference, or low-end device |
257 | 157 | canvas.style.display = "none"; |
258 | 158 | return; |
259 | 159 | } |
|
304 | 204 |
|
305 | 205 | // Create stars with positions and properties |
306 | 206 | function createStars() { |
307 | | - // Significantly reduce star count for better performance |
| 207 | + // Drastically reduce star count for better performance |
308 | 208 | const isMobile = window.innerWidth < 640; |
309 | 209 | const starCount = isMobile |
310 | | - ? Math.min(300, Math.floor((width * height) / 8000)) |
311 | | - : Math.min(800, Math.floor((width * height) / 5000)); |
| 210 | + ? Math.min(150, Math.floor((width * height) / 15000)) |
| 211 | + : Math.min(300, Math.floor((width * height) / 10000)); |
312 | 212 | stars = []; |
313 | 213 |
|
314 | 214 | const galaxyRadius = Math.min(width, height) * 0.4; |
|
428 | 328 |
|
429 | 329 | // Create dust particles |
430 | 330 | function createDust() { |
431 | | - // Significantly reduce dust count for better performance |
| 331 | + // Drastically reduce dust count for better performance |
432 | 332 | const isMobile = window.innerWidth < 640; |
433 | 333 | const dustCount = isMobile |
434 | | - ? Math.floor((width * height) / 40000) |
435 | | - : Math.floor((width * height) / 25000); |
| 334 | + ? Math.floor((width * height) / 80000) |
| 335 | + : Math.floor((width * height) / 60000); |
436 | 336 | dust = []; |
437 | 337 |
|
438 | 338 | const galaxyRadius = Math.min(width, height) * 0.4; |
|
890 | 790 | } |
891 | 791 | } |
892 | 792 |
|
| 793 | + let lastAnimationTime = 0; |
| 794 | + const ANIMATION_INTERVAL = 66; // ~15 FPS max for better performance |
| 795 | + |
893 | 796 | // Animation loop |
894 | | - function animate() { |
| 797 | + function animate(currentTime) { |
895 | 798 | // Only render when visible and not hidden |
896 | 799 | if (isVisible && !document.hidden) { |
897 | | - // Throttle animation based on device capability |
898 | | - const isMobile = window.innerWidth < 640; |
899 | | - |
900 | | - if (isMobile) { |
901 | | - // On mobile, throttle even more for better performance |
902 | | - if (Math.random() < 0.2) { |
903 | | - // Only update ~20% of frames |
904 | | - updateStars(); |
905 | | - drawGalaxy(); |
906 | | - } |
907 | | - } else { |
908 | | - // On desktop, use requestAnimationFrame throttling for better performance |
909 | | - if (Date.now() % 3 === 0) { |
910 | | - // Only render every third frame |
| 800 | + // Throttle animation to ~15 FPS for better performance |
| 801 | + if (currentTime - lastAnimationTime >= ANIMATION_INTERVAL) { |
| 802 | + const isMobile = window.innerWidth < 640; |
| 803 | + |
| 804 | + if (isMobile) { |
| 805 | + // On mobile, update even less frequently |
| 806 | + if (Math.random() < 0.4) { |
| 807 | + updateStars(); |
| 808 | + drawGalaxy(); |
| 809 | + } |
| 810 | + } else { |
911 | 811 | updateStars(); |
912 | 812 | drawGalaxy(); |
913 | 813 | } |
| 814 | + lastAnimationTime = currentTime; |
914 | 815 | } |
915 | 816 | } |
916 | 817 |
|
|
988 | 889 | let isTouching = false; |
989 | 890 | let touchTimeout = null; |
990 | 891 |
|
991 | | - // Handle touch events for mobile |
| 892 | + // Handle touch events for mobile - simplified for better performance |
992 | 893 | document.addEventListener("touchstart", function (e) { |
993 | 894 | if (!cursor) return; |
| 895 | + |
| 896 | + // Disable complex effects on low-end devices |
| 897 | + if (isLowEnd) return; |
994 | 898 |
|
995 | 899 | // Reset transition to initial state for new touch |
996 | 900 | cursor.style.transition = |
|
0 commit comments