This commit is contained in:
+118
-26
@@ -4,45 +4,137 @@
|
||||
|
||||
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
const sequence = [
|
||||
["projects", 180], ["articles", 320], ["blog", 460],
|
||||
["github", 820], ["gitea", 930], ["linkedin", 1040],
|
||||
["twitch", 1150], ["youtube", 1260], ["kofi", 1370],
|
||||
{ name: "projects", travel: 620, character: 62 },
|
||||
{ name: "articles", travel: 600, character: 58 },
|
||||
{ name: "blog", travel: 560, character: 72 },
|
||||
{ name: "github", travel: 380, character: 46 },
|
||||
{ name: "gitea", travel: 360, character: 48 },
|
||||
{ name: "linkedin", travel: 360, character: 42 },
|
||||
{ name: "twitch", travel: 340, character: 46 },
|
||||
{ name: "youtube", travel: 360, character: 42 },
|
||||
{ name: "kofi", travel: 320, character: 50 },
|
||||
];
|
||||
let run = 0;
|
||||
let timers = [];
|
||||
|
||||
const clearTimers = () => {
|
||||
timers.forEach(window.clearTimeout);
|
||||
timers = [];
|
||||
let runToken = 0;
|
||||
const sleep = (milliseconds) => new Promise((resolve) => window.setTimeout(resolve, milliseconds));
|
||||
|
||||
const reset = () => {
|
||||
hero.classList.remove("is-glitching");
|
||||
hero.querySelectorAll(".circuit-node").forEach((node) => {
|
||||
node.classList.remove("is-revealing", "is-ready");
|
||||
node.querySelector("[data-route-output]").textContent = "";
|
||||
const cursor = node.querySelector("i");
|
||||
if (cursor) cursor.hidden = false;
|
||||
});
|
||||
hero.querySelectorAll(".circuit-live path").forEach((path) => {
|
||||
path.style.opacity = "0";
|
||||
path.style.strokeDashoffset = "0";
|
||||
});
|
||||
};
|
||||
|
||||
const revealAll = () => {
|
||||
hero.querySelectorAll(".circuit-node").forEach((node) => node.classList.add("is-ready"));
|
||||
hero.querySelectorAll(".circuit-live path").forEach((path) => path.classList.add("is-live"));
|
||||
hero.querySelectorAll(".circuit-node").forEach((node) => {
|
||||
node.querySelector("[data-route-output]").textContent = node.dataset.routeText;
|
||||
const cursor = node.querySelector("i");
|
||||
if (cursor) cursor.hidden = true;
|
||||
node.classList.add("is-revealing", "is-ready");
|
||||
});
|
||||
hero.querySelectorAll(".circuit-live path").forEach((path) => {
|
||||
path.style.opacity = ".34";
|
||||
});
|
||||
};
|
||||
|
||||
const start = () => {
|
||||
run += 1;
|
||||
const token = run;
|
||||
clearTimers();
|
||||
hero.classList.remove("is-glitching");
|
||||
hero.querySelectorAll(".circuit-node").forEach((node) => node.classList.remove("is-ready"));
|
||||
hero.querySelectorAll(".circuit-live path").forEach((path) => path.classList.remove("is-live"));
|
||||
const animatePath = (path, duration, token) => {
|
||||
const length = path.getTotalLength();
|
||||
path.style.strokeDasharray = `30 ${Math.max(40, length)}`;
|
||||
path.style.strokeDashoffset = "30";
|
||||
path.style.opacity = "1";
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const started = performance.now();
|
||||
const frame = (now) => {
|
||||
if (token !== runToken) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const progress = Math.min(1, (now - started) / duration);
|
||||
const eased = 1 - Math.pow(1 - progress, 3);
|
||||
path.style.strokeDashoffset = String(30 - (length + 55) * eased);
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(frame);
|
||||
} else {
|
||||
path.style.opacity = ".34";
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
requestAnimationFrame(frame);
|
||||
});
|
||||
};
|
||||
|
||||
const typeRoute = async (node, characterDelay, token) => {
|
||||
const output = node.querySelector("[data-route-output]");
|
||||
node.classList.add("is-revealing");
|
||||
for (const character of node.dataset.routeText) {
|
||||
if (token !== runToken) return;
|
||||
output.textContent += character;
|
||||
await sleep(characterDelay);
|
||||
}
|
||||
const cursor = node.querySelector("i");
|
||||
if (cursor) cursor.hidden = true;
|
||||
node.classList.add("is-ready");
|
||||
};
|
||||
|
||||
const animateAmbientPackets = () => {
|
||||
if (reducedMotion) return;
|
||||
hero.querySelectorAll(".circuit-ambient-packets path").forEach((packet, index) => {
|
||||
const length = packet.getTotalLength();
|
||||
packet.style.strokeDasharray = `16 ${Math.max(30, length)}`;
|
||||
packet.animate(
|
||||
[
|
||||
{ strokeDashoffset: "20", opacity: .06 },
|
||||
{ opacity: .2, offset: .18 },
|
||||
{ opacity: .2, offset: .82 },
|
||||
{ strokeDashoffset: String(-(length + 25)), opacity: .05 },
|
||||
],
|
||||
{ duration: 3300 + (index % 5) * 620, delay: index * 210, iterations: Infinity, easing: "linear" },
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const runSequence = async () => {
|
||||
runToken += 1;
|
||||
const token = runToken;
|
||||
|
||||
if (reducedMotion) {
|
||||
reset();
|
||||
revealAll();
|
||||
return;
|
||||
}
|
||||
|
||||
sequence.forEach(([name, delay]) => {
|
||||
timers.push(window.setTimeout(() => {
|
||||
if (token !== run) return;
|
||||
hero.querySelector(`[data-route-node="${name}"]`)?.classList.add("is-ready");
|
||||
hero.querySelector(`#route-${name}`)?.classList.add("is-live");
|
||||
}, delay));
|
||||
});
|
||||
while (token === runToken) {
|
||||
reset();
|
||||
await sleep(280);
|
||||
|
||||
for (const item of sequence) {
|
||||
if (token !== runToken) return;
|
||||
const node = hero.querySelector(`[data-route-node="${item.name}"]`);
|
||||
const path = hero.querySelector(`#route-${item.name}`);
|
||||
await animatePath(path, item.travel, token);
|
||||
await typeRoute(node, item.character, token);
|
||||
await sleep(55);
|
||||
}
|
||||
|
||||
await sleep(3000);
|
||||
if (token !== runToken) return;
|
||||
hero.classList.add("is-glitching");
|
||||
await sleep(430);
|
||||
if (token !== runToken) return;
|
||||
reset();
|
||||
await sleep(180);
|
||||
}
|
||||
};
|
||||
|
||||
hero.querySelector("[data-circuit-replay]")?.addEventListener("click", start);
|
||||
start();
|
||||
hero.querySelector("[data-circuit-replay]")?.addEventListener("click", runSequence);
|
||||
animateAmbientPackets();
|
||||
runSequence();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user