fix: starfield visibility on load and WebGL fail-soft guard
- ParticleBuildText: self-scheduling rAF loop (previous one-shot tick left the canvas blank until scroll), deterministic sampleUntilBuilt warm-up, ambient star layer spread across the whole card with twinkle, random drift and a birth/death lifecycle (4-10s TTL with 800ms fades and respawn) - ControlRoomGlobe: fail-soft WebGL guard (webgl2/webgl check + try/catch around createGlobe) so an unavailable or poisoned context renders the fallback instead of crashing the hero tree
This commit is contained in:
1 parent
b791b7de9e
commit
957c58b69a
2 files changed
+177
-58
No files matched your search
@@ -125,26 +125,50 @@ export function ControlRoomGlobe({
|
||||
observer.disconnect();
|
||||
};
|
||||
}
|
||||
const globe = createGlobe(canvas, {
|
||||
...config,
|
||||
width: width * 2,
|
||||
height: width * 2,
|
||||
onRender: (state) => {
|
||||
if (!isVisibleRef.current || document.hidden) return;
|
||||
if (autoRotate && !pointerInteracting.current) {
|
||||
phiRef.current += rotationSpeed;
|
||||
thetaPhaseRef.current += rotationSpeed * 0.9;
|
||||
}
|
||||
state.phi = phiRef.current + rs.get();
|
||||
// Rotação livre em todos os eixos: spin (phi) + guinada senoidal (theta) + inclinação por arraste vertical
|
||||
state.theta =
|
||||
(config.theta ?? TROPIC_OF_CAPRICORN_RAD) +
|
||||
Math.sin(thetaPhaseRef.current) * 0.35 +
|
||||
thetaDragRef.current;
|
||||
state.width = width * 2;
|
||||
state.height = width * 2;
|
||||
},
|
||||
});
|
||||
// Falha suave: sem WebGL (ou canvas já vinculado a outro tipo de contexto) o phenomenon
|
||||
// lança "reading 'enable' of null" e derrubaria a árvore React inteira — card ficaria preto.
|
||||
let globe: ReturnType<typeof createGlobe> | null = null;
|
||||
try {
|
||||
const supportsWebGL =
|
||||
canvas.getContext("webgl2") ?? canvas.getContext("webgl");
|
||||
if (supportsWebGL) {
|
||||
globe = createGlobe(canvas, {
|
||||
...config,
|
||||
width: width * 2,
|
||||
height: width * 2,
|
||||
onRender: (state) => {
|
||||
if (!isVisibleRef.current || document.hidden) return;
|
||||
if (autoRotate && !pointerInteracting.current) {
|
||||
phiRef.current += rotationSpeed;
|
||||
thetaPhaseRef.current += rotationSpeed * 0.9;
|
||||
}
|
||||
state.phi = phiRef.current + rs.get();
|
||||
// Rotação livre em todos os eixos: spin (phi) + guinada senoidal (theta) + inclinação por arraste vertical
|
||||
state.theta =
|
||||
(config.theta ?? TROPIC_OF_CAPRICORN_RAD) +
|
||||
Math.sin(thetaPhaseRef.current) * 0.35 +
|
||||
thetaDragRef.current;
|
||||
state.width = width * 2;
|
||||
state.height = width * 2;
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
globe = null;
|
||||
}
|
||||
|
||||
if (!globe) {
|
||||
// Sem globo: fallback visível (gradiente do card permanece), página viva.
|
||||
canvas.style.opacity = "1";
|
||||
return () => {
|
||||
window.removeEventListener("resize", onResize);
|
||||
document.removeEventListener(
|
||||
"visibilitychange",
|
||||
handleVisibilityChange,
|
||||
);
|
||||
observer.disconnect();
|
||||
};
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (canvasRef.current) {
|
||||
|
||||
Reference in new issue
Block a user