hero bugfixes/improvements

This commit is contained in:
2026-04-08 16:29:13 -07:00
parent f6873546df
commit 87d3b3bfa6
2 changed files with 75 additions and 20 deletions

View File

@@ -370,16 +370,6 @@ function addComeback(tw: TypewriterInstance, onRetire: () => void, completions:
`<span class="text-yellow">until they're the only way out</span>` `<span class="text-yellow">until they're the only way out</span>`
).pauseFor(4000).deleteAll(); ).pauseFor(4000).deleteAll();
// --- Visitor count ---
if (completions !== null && completions > 0) {
tw.typeString(
`<span>You're visitor </span>` +
`<span class="text-yellow">#${completions.toLocaleString()}</span>${BR}` +
`<span class="text-aqua">to make it this far</span>`
).pauseFor(5000).deleteAll();
}
// --- Done for real --- // --- Done for real ---
addDots(tw, 1000, 4000); addDots(tw, 1000, 4000);

View File

@@ -3,6 +3,8 @@ import { Canvas } from "@react-three/fiber";
import VoidTypewriter from "./typewriter"; import VoidTypewriter from "./typewriter";
import VoidWater from "./scenes/void-water"; import VoidWater from "./scenes/void-water";
// Canvas glitch: transforms + filters (physical shake + color corruption)
// Text glitch: filters only (color corruption, no position shift)
const GLITCH_CSS = ` const GLITCH_CSS = `
.void-glitch-subtle { .void-glitch-subtle {
animation: void-glitch-subtle 2s ease-in-out infinite; animation: void-glitch-subtle 2s ease-in-out infinite;
@@ -13,6 +15,15 @@ const GLITCH_CSS = `
.void-glitch-dissolve { .void-glitch-dissolve {
animation: void-glitch-dissolve 2s ease-in forwards; animation: void-glitch-dissolve 2s ease-in forwards;
} }
.void-text-glitch-subtle {
animation: void-text-glitch-subtle 2s ease-in-out infinite;
}
.void-text-glitch-intense {
animation: void-text-glitch-intense 1.2s ease-in-out infinite;
}
.void-text-glitch-dissolve {
animation: void-text-glitch-dissolve 2s ease-in forwards;
}
@keyframes void-glitch-subtle { @keyframes void-glitch-subtle {
0%, 100% { transform: none; filter: none; } 0%, 100% { transform: none; filter: none; }
@@ -29,6 +40,15 @@ const GLITCH_CSS = `
85% { transform: translateX(-1px) skewY(0.1deg); } 85% { transform: translateX(-1px) skewY(0.1deg); }
87% { transform: none; } 87% { transform: none; }
} }
@keyframes void-text-glitch-subtle {
0%, 100% { filter: none; }
3% { filter: hue-rotate(15deg); }
6% { filter: none; }
30% { filter: saturate(1.5); }
32% { filter: none; }
70% { filter: hue-rotate(-10deg); }
72% { filter: none; }
}
@keyframes void-glitch-intense { @keyframes void-glitch-intense {
0%, 100% { transform: none; filter: none; } 0%, 100% { transform: none; filter: none; }
@@ -48,6 +68,21 @@ const GLITCH_CSS = `
85% { transform: skewX(-1deg) translateX(2px) translateY(-1px); filter: saturate(5); } 85% { transform: skewX(-1deg) translateX(2px) translateY(-1px); filter: saturate(5); }
88% { transform: none; filter: none; } 88% { transform: none; filter: none; }
} }
@keyframes void-text-glitch-intense {
0%, 100% { filter: none; }
2% { filter: hue-rotate(60deg) saturate(3); }
5% { filter: none; }
12% { filter: hue-rotate(-90deg); }
15% { filter: none; }
25% { filter: saturate(4); }
28% { filter: none; }
40% { filter: hue-rotate(120deg) saturate(2); }
42% { filter: none; }
70% { filter: hue-rotate(-45deg) saturate(3); }
73% { filter: none; }
85% { filter: saturate(5); }
88% { filter: none; }
}
@keyframes void-glitch-dissolve { @keyframes void-glitch-dissolve {
0% { transform: none; filter: none; opacity: 1; } 0% { transform: none; filter: none; opacity: 1; }
@@ -65,6 +100,18 @@ const GLITCH_CSS = `
80% { transform: translateX(-2px); opacity: 0.1; } 80% { transform: translateX(-2px); opacity: 0.1; }
100% { transform: none; filter: none; opacity: 0; } 100% { transform: none; filter: none; opacity: 0; }
} }
@keyframes void-text-glitch-dissolve {
0% { filter: none; opacity: 1; }
3% { filter: hue-rotate(90deg) saturate(4); }
10% { filter: hue-rotate(-120deg) saturate(3); opacity: 0.9; }
20% { filter: hue-rotate(180deg) saturate(5); opacity: 0.8; }
30% { filter: saturate(6) hue-rotate(60deg); opacity: 0.7; }
40% { filter: hue-rotate(-90deg); opacity: 0.55; }
50% { filter: saturate(3); opacity: 0.4; }
60% { filter: hue-rotate(150deg); opacity: 0.3; }
80% { filter: none; opacity: 0.1; }
100% { filter: none; opacity: 0; }
}
`; `;
function getCorruption(segment: number): number { function getCorruption(segment: number): number {
@@ -73,7 +120,7 @@ function getCorruption(segment: number): number {
if (segment === 9) return 0.08; if (segment === 9) return 0.08;
if (segment === 10) return 0.1; if (segment === 10) return 0.1;
if (segment === 11) return 0.13; if (segment === 11) return 0.13;
if (segment === 12) return 0.1; // "anyway" — dips if (segment === 12) return 0.1;
if (segment === 13) return 0.3; if (segment === 13) return 0.3;
if (segment === 14) return 0.6; if (segment === 14) return 0.6;
if (segment === 15) return 0.75; if (segment === 15) return 0.75;
@@ -81,13 +128,20 @@ function getCorruption(segment: number): number {
return 1.0; return 1.0;
} }
function getGlitchClass(segment: number, dissolving: boolean): string { function getCanvasGlitch(segment: number, dissolving: boolean): string {
if (dissolving) return "void-glitch-dissolve"; if (dissolving) return "void-glitch-dissolve";
if (segment < 8) return ""; if (segment < 8) return "";
if (segment <= 14) return "void-glitch-subtle"; if (segment <= 14) return "void-glitch-subtle";
return "void-glitch-intense"; return "void-glitch-intense";
} }
function getTextGlitch(segment: number, dissolving: boolean): string {
if (dissolving) return "void-text-glitch-dissolve";
if (segment < 8) return "";
if (segment <= 14) return "void-text-glitch-subtle";
return "void-text-glitch-intense";
}
interface VoidExperienceProps { interface VoidExperienceProps {
token: string; token: string;
} }
@@ -97,16 +151,28 @@ export default function VoidExperience({ token }: VoidExperienceProps) {
const [visitCount, setVisitCount] = useState<number | null>(null); const [visitCount, setVisitCount] = useState<number | null>(null);
const [dissolving, setDissolving] = useState(false); const [dissolving, setDissolving] = useState(false);
// Inject CSS once // Inject CSS + hide cursor + force fullscreen feel on mobile
useEffect(() => { useEffect(() => {
const style = document.createElement("style"); const style = document.createElement("style");
style.textContent = GLITCH_CSS; style.textContent = GLITCH_CSS;
document.head.appendChild(style); document.head.appendChild(style);
// Hide cursor during void experience
document.body.style.cursor = "none"; document.body.style.cursor = "none";
// Push mobile tab bar off-screen by making the page taller than viewport
const meta = document.querySelector('meta[name="viewport"]');
const origContent = meta?.getAttribute("content") || "";
meta?.setAttribute("content", "width=device-width, initial-scale=1, interactive-widget=resizes-content");
document.documentElement.style.overflow = "hidden";
document.body.style.overflow = "hidden";
// Scroll down slightly to trigger mobile browsers hiding the tab bar
window.scrollTo(0, 1);
return () => { return () => {
style.remove(); style.remove();
document.body.style.cursor = ""; document.body.style.cursor = "";
document.documentElement.style.overflow = "";
document.body.style.overflow = "";
if (meta) meta.setAttribute("content", origContent);
}; };
}, []); }, []);
@@ -144,12 +210,11 @@ export default function VoidExperience({ token }: VoidExperienceProps) {
}, []); }, []);
const corruption = getCorruption(activeSegment); const corruption = getCorruption(activeSegment);
const glitchClass = getGlitchClass(activeSegment, dissolving);
return ( return (
<div className="fixed inset-0 bg-black"> <div className="fixed inset-0 bg-black" style={{ height: "100dvh" }}>
{/* 3D Canvas — filter applied directly to this element, not a parent */} {/* 3D Canvas — full glitch (transforms + filters) */}
<div className={`fixed inset-0 z-[10] ${glitchClass}`}> <div className={`fixed inset-0 z-[10] ${getCanvasGlitch(activeSegment, dissolving)}`}>
<Canvas <Canvas
camera={{ position: [0, 0, 8], fov: 60 }} camera={{ position: [0, 0, 8], fov: 60 }}
dpr={[1, 1.5]} dpr={[1, 1.5]}
@@ -160,9 +225,9 @@ export default function VoidExperience({ token }: VoidExperienceProps) {
</Canvas> </Canvas>
</div> </div>
{/* Typewriter — same glitch class applied directly */} {/* Typewriter — filter-only glitch (no transform shift) */}
{visitCount !== null && ( {visitCount !== null && (
<div className={glitchClass}> <div className={getTextGlitch(activeSegment, dissolving)}>
<VoidTypewriter <VoidTypewriter
startSegment={0} startSegment={0}
onPhaseComplete={handlePhaseComplete} onPhaseComplete={handlePhaseComplete}