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>`
).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 ---
addDots(tw, 1000, 4000);

View File

@@ -3,6 +3,8 @@ import { Canvas } from "@react-three/fiber";
import VoidTypewriter from "./typewriter";
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 = `
.void-glitch-subtle {
animation: void-glitch-subtle 2s ease-in-out infinite;
@@ -13,6 +15,15 @@ const GLITCH_CSS = `
.void-glitch-dissolve {
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 {
0%, 100% { transform: none; filter: none; }
@@ -29,6 +40,15 @@ const GLITCH_CSS = `
85% { transform: translateX(-1px) skewY(0.1deg); }
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 {
0%, 100% { transform: none; filter: none; }
@@ -48,6 +68,21 @@ const GLITCH_CSS = `
85% { transform: skewX(-1deg) translateX(2px) translateY(-1px); filter: saturate(5); }
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 {
0% { transform: none; filter: none; opacity: 1; }
@@ -65,6 +100,18 @@ const GLITCH_CSS = `
80% { transform: translateX(-2px); opacity: 0.1; }
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 {
@@ -73,7 +120,7 @@ function getCorruption(segment: number): number {
if (segment === 9) return 0.08;
if (segment === 10) return 0.1;
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 === 14) return 0.6;
if (segment === 15) return 0.75;
@@ -81,13 +128,20 @@ function getCorruption(segment: number): number {
return 1.0;
}
function getGlitchClass(segment: number, dissolving: boolean): string {
function getCanvasGlitch(segment: number, dissolving: boolean): string {
if (dissolving) return "void-glitch-dissolve";
if (segment < 8) return "";
if (segment <= 14) return "void-glitch-subtle";
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 {
token: string;
}
@@ -97,16 +151,28 @@ export default function VoidExperience({ token }: VoidExperienceProps) {
const [visitCount, setVisitCount] = useState<number | null>(null);
const [dissolving, setDissolving] = useState(false);
// Inject CSS once
// Inject CSS + hide cursor + force fullscreen feel on mobile
useEffect(() => {
const style = document.createElement("style");
style.textContent = GLITCH_CSS;
document.head.appendChild(style);
// Hide cursor during void experience
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 () => {
style.remove();
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 glitchClass = getGlitchClass(activeSegment, dissolving);
return (
<div className="fixed inset-0 bg-black">
{/* 3D Canvas — filter applied directly to this element, not a parent */}
<div className={`fixed inset-0 z-[10] ${glitchClass}`}>
<div className="fixed inset-0 bg-black" style={{ height: "100dvh" }}>
{/* 3D Canvas — full glitch (transforms + filters) */}
<div className={`fixed inset-0 z-[10] ${getCanvasGlitch(activeSegment, dissolving)}`}>
<Canvas
camera={{ position: [0, 0, 8], fov: 60 }}
dpr={[1, 1.5]}
@@ -160,9 +225,9 @@ export default function VoidExperience({ token }: VoidExperienceProps) {
</Canvas>
</div>
{/* Typewriter — same glitch class applied directly */}
{/* Typewriter — filter-only glitch (no transform shift) */}
{visitCount !== null && (
<div className={glitchClass}>
<div className={getTextGlitch(activeSegment, dissolving)}>
<VoidTypewriter
startSegment={0}
onPhaseComplete={handlePhaseComplete}