added loading screen
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
.loader {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
overflow: hidden;
|
||||
padding: clamp(24px, 3.5vw, 56px);
|
||||
color: #f5f3ee;
|
||||
background: #24231e;
|
||||
transition:
|
||||
opacity 650ms cubic-bezier(0.76, 0, 0.24, 1),
|
||||
clip-path 650ms cubic-bezier(0.76, 0, 0.24, 1);
|
||||
clip-path: inset(0);
|
||||
}
|
||||
.loader--closing {
|
||||
opacity: 0;
|
||||
clip-path: inset(0 0 100% 0);
|
||||
pointer-events: none;
|
||||
}
|
||||
.loader__canvas,
|
||||
.loader__veil {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.loader__canvas {
|
||||
display: block;
|
||||
opacity: 0;
|
||||
transition: opacity 180ms ease-out;
|
||||
}
|
||||
.loader__canvas[data-ready="true"] {
|
||||
opacity: 1;
|
||||
}
|
||||
.loader__veil {
|
||||
background: linear-gradient(90deg, #17171238, transparent 45%, #17171238);
|
||||
}
|
||||
.loader__topline,
|
||||
.loader__footer,
|
||||
.loader__center {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.loader__topline,
|
||||
.loader__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.loader__center {
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
text-align: center;
|
||||
}
|
||||
.loader__eyebrow {
|
||||
margin: 0 0 18px;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.loader__title {
|
||||
margin: 0;
|
||||
font-family: var(--serif);
|
||||
font-size: clamp(58px, 8.5vw, 132px);
|
||||
font-weight: 400;
|
||||
line-height: 0.84;
|
||||
letter-spacing: -0.055em;
|
||||
}
|
||||
.loader__title em {
|
||||
font-weight: 400;
|
||||
}
|
||||
.loader__footer {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
}
|
||||
.loader__track {
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
background: #ffffff38;
|
||||
}
|
||||
.loader__progress {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f5f3ee;
|
||||
transform: translateX(-100%);
|
||||
animation: loader-progress 1.9s linear forwards;
|
||||
}
|
||||
.loader__progress::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border: 1px solid #f5f3ee;
|
||||
border-radius: 50%;
|
||||
background: #29271f;
|
||||
content: "";
|
||||
transform: translate(50%, -50%);
|
||||
}
|
||||
.loader__announcement {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
@keyframes loader-progress {
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.loader__title {
|
||||
font-size: clamp(54px, 19vw, 86px);
|
||||
}
|
||||
.loader__topline span:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.loader {
|
||||
transition: none;
|
||||
}
|
||||
.loader__progress {
|
||||
animation: none;
|
||||
}
|
||||
.loader__progress {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import weaveShader from "../shaders/loader.wgsl?raw";
|
||||
import "./Loader.css";
|
||||
|
||||
export default function Loader() {
|
||||
const canvasRef = useRef(null);
|
||||
const [phase, setPhase] = useState("active");
|
||||
|
||||
useEffect(() => {
|
||||
const reducedMotion = window.matchMedia(
|
||||
"(prefers-reduced-motion: reduce)",
|
||||
).matches;
|
||||
const closeTimer = window.setTimeout(
|
||||
() => setPhase("closing"),
|
||||
reducedMotion ? 0 : 1900,
|
||||
);
|
||||
const removeTimer = window.setTimeout(
|
||||
() => setPhase("hidden"),
|
||||
reducedMotion ? 50 : 2550,
|
||||
);
|
||||
return () => {
|
||||
window.clearTimeout(closeTimer);
|
||||
window.clearTimeout(removeTimer);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (phase !== "active") return undefined;
|
||||
let active = true;
|
||||
let gpu;
|
||||
let loop;
|
||||
let unsubscribeResize;
|
||||
|
||||
async function renderWeave() {
|
||||
try {
|
||||
const { clock, effect, frameLoop, init, surface } = await import(
|
||||
"vgpu"
|
||||
);
|
||||
gpu = await init({ powerPreference: "low-power" });
|
||||
if (!active) return gpu.dispose();
|
||||
|
||||
const target = surface(gpu, canvasRef.current, { dpr: [1, 1.5] });
|
||||
const weave = effect(gpu, weaveShader, {
|
||||
set: { params: { time: 0, progress: 0, aspect: 1, padding: 0 } },
|
||||
});
|
||||
const timer = clock(gpu);
|
||||
unsubscribeResize = target.onResize(({ width, height }) => {
|
||||
weave.set({ params: { aspect: width / height } });
|
||||
});
|
||||
let presented = false;
|
||||
loop = frameLoop(
|
||||
gpu,
|
||||
(frame) => {
|
||||
weave.set({
|
||||
params: {
|
||||
time: timer.time,
|
||||
progress: Math.min(1.2, timer.time / 1.8),
|
||||
},
|
||||
});
|
||||
frame.pass(target, weave);
|
||||
if (!presented) {
|
||||
presented = true;
|
||||
requestAnimationFrame(() => {
|
||||
if (active) canvasRef.current.dataset.ready = "true";
|
||||
});
|
||||
}
|
||||
},
|
||||
{ fps: 30 },
|
||||
);
|
||||
} catch {
|
||||
// A flat background remains when WebGPU is unavailable.
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches)
|
||||
renderWeave();
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
loop?.stop();
|
||||
unsubscribeResize?.();
|
||||
gpu?.dispose();
|
||||
};
|
||||
}, [phase]);
|
||||
|
||||
if (phase === "hidden") return null;
|
||||
|
||||
return (
|
||||
<div className={`loader loader--${phase}`} aria-live="polite">
|
||||
<canvas className="loader__canvas" ref={canvasRef} aria-hidden="true" />
|
||||
<div className="loader__veil" aria-hidden="true" />
|
||||
<div className="loader__topline">
|
||||
<span>RRS / 1987</span>
|
||||
<span>Surat, India</span>
|
||||
</div>
|
||||
<div className="loader__center">
|
||||
<p className="loader__eyebrow">Preparing the loom</p>
|
||||
<p className="loader__title">
|
||||
From yarn
|
||||
<br />
|
||||
<em>to structure.</em>
|
||||
</p>
|
||||
</div>
|
||||
<div className="loader__footer">
|
||||
<span>Warp</span>
|
||||
<span className="loader__track" aria-hidden="true">
|
||||
<span className="loader__progress" />
|
||||
</span>
|
||||
<span>Weft</span>
|
||||
</div>
|
||||
<span className="loader__announcement">
|
||||
Loading Radhe Radhe Synthetics
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user