decarbone/assets/js/script.js
2026-02-13 15:40:11 +01:00

363 lines
10 KiB
JavaScript

document.addEventListener("DOMContentLoaded", () => {
// init
const introduction = document.querySelector("#introduction");
if (introduction) playIntroduction();
initNav();
initNotes();
styleCallouts();
linkLayersScrolls();
let currentUrl = location.href;
window.onpopstate = function () {
let target = null;
const currentLayerId = document.querySelector(".current").id;
if (currentLayerId === "first-level") {
window.location.href = window.location.href;
} else if (currentLayerId === "second-level") {
history.pushState(null, document.title, currentUrl);
target = document.querySelector("#first-level .layer-btn");
const event = {
preventDefault: () => {},
stopPropagation: () => {},
target: target,
};
go(event);
} else if (currentLayerId === "third-level") {
history.pushState(null, document.title, currentUrl);
target = document.querySelector("#second-level .layer-btn");
const event = {
preventDefault: () => {},
stopPropagation: () => {},
target: target,
};
go(event);
}
};
const collapsableSectionBtns = document.querySelectorAll(
".collapsable__button"
);
collapsableSectionBtns.forEach((btn) => {
btn.addEventListener("click", () => {
const section = btn.parentNode;
section.classList.toggle("open");
});
});
animateCursor();
// functions
let ticking = false;
function linkLayersScrolls() {
const thirdLayer = document.querySelector("#third-level");
let secondLayer = document.querySelector("#second-level");
thirdLayer.addEventListener("scroll", () => {
if (!ticking) {
window.requestAnimationFrame(() => {
scrollLayer(secondLayer, thirdLayer.scrollTop);
ticking = false;
});
ticking = true;
}
});
}
function scrollLayer(secondLayer, scrollTop) {
if (window.innerWidth > 650) return;
const parallaxFactor = 0.5;
if (secondLayer.scrollTop <= 40) {
let newScrollTop = scrollTop * parallaxFactor;
newScrollTop = newScrollTop > 40 ? 40 : newScrollTop;
secondLayer.scrollTop = newScrollTop;
}
}
function playIntroduction() {
const parts = introduction.querySelectorAll("span");
const step = 800;
parts.forEach((part, index) => {
setTimeout(() => {
part.classList.remove("hide");
}, step + step * index);
});
setTimeout(() => {
introduction.classList.add("hide");
}, parts.length + 5 * step);
}
function formatSize(size) {
if (size < 1_000) {
return {
size: size,
unit: " o",
};
} else if (size < 1_000_000) {
return {
size: size / 1_000,
unit: " Ko",
};
} else if (size < 1_000_000_000) {
return {
size: size / 1_000_000,
unit: " Mo",
};
} else {
return {
size: size / 1_000_000_000,
unit: " Go",
};
}
}
function initNotes() {
const notes = document.querySelectorAll(".note");
notes.forEach((note) => {
note.addEventListener("click", () => {
note.classList.toggle("open");
note.querySelector(".details").classList.toggle("hide");
});
});
}
function animateCursor() {
const cursor = document.getElementById("cursor");
let animationFrameId;
document.addEventListener("mousemove", (e) => {
if (window.innerWidth < 640) return;
const hoveredLayer = e.target.closest(".layer");
if (hoveredLayer.id === "first-level") {
cursor.classList.add("light");
} else if (!e.target.closest(".collapsable__button")) {
cursor.classList.remove("light");
}
isCursorVisible = true;
cursor.style.display = "flex";
document.body.style.cursor = "none";
const x = e.clientX - 16;
const y = e.clientY - 16 + window.scrollY;
cancelAnimationFrame(animationFrameId);
animationFrameId = requestAnimationFrame(() => {
cursor.style.left = x + "px";
cursor.style.top = y + "px";
});
});
}
function initNav() {
listenLinks();
showTargetPageSize();
listenCollapsableSections();
}
function listenLinks() {
const internalLinks = document.querySelectorAll("a.internal-link");
internalLinks.forEach((btn) => {
btn.addEventListener("click", go);
});
const imageLinks = document.querySelectorAll("figure a");
imageLinks.forEach((link) => {
const cursorText = document.querySelector("#cursor #text");
link.addEventListener("mouseenter", () => {
const href = new URL(link.href);
cursorText.style.display = "flex";
cursorText.textContent = "ouvrir " + href.host;
});
link.addEventListener("mouseleave", () => {
cursorText.style.display = "";
cursorText.textContent = "";
});
});
}
function removeListeners() {
const internalLinks = document.querySelectorAll("a.internal-link");
internalLinks.forEach((btn) => {
btn.removeEventListener("click", go);
});
}
function removeTargetPageListeners() {
const internalLinks = document.querySelectorAll("a.internal-link");
internalLinks.forEach((link) => {
link.removeEventListener("mouseenter", handleMouseEnter);
link.removeEventListener("mouseleave", handleMouseLeave);
});
}
function handleMouseEnter() {
const cursorText = document.querySelector("#cursor #text");
if (this.href === window.location.href) return;
cursorText.style.display = "flex";
cursorText.textContent = this.dataset.size;
}
function handleMouseLeave() {
const cursorText = document.querySelector("#cursor #text");
cursorText.style.display = "";
cursorText.textContent = "";
}
function toggleVisibility(element, condition) {
element.classList.remove("unvisible", condition);
}
function moveBackward(wrapper, page) {
page.content
.querySelector(".current .layer-btn")
.classList.remove("unvisible");
wrapper.innerHTML = page.content.innerHTML;
wrapper.classList.add("current");
setTimeout(() => {
wrapper.querySelector(".current .layer-btn").classList.add("unvisible");
}, 50);
}
function moveForward(originWrapper, page) {
const isThirdLevel = originWrapper.id === "third-level";
const targetWrapper = isThirdLevel
? document.querySelector("#second-level")
: originWrapper.nextElementSibling;
originWrapper.classList.remove("current");
targetWrapper.classList.add("current");
const emptys = page.content.querySelectorAll(".empty");
const current = document.querySelector(".current");
if (current.id === "second-level" && emptys.length === 3) {
const firstEmpty = page.content.querySelector(".empty");
firstEmpty.parentNode.removeChild(firstEmpty);
}
targetWrapper.innerHTML = page.content.innerHTML;
targetWrapper.classList.remove("out-screen");
}
function go(event) {
event.stopPropagation();
event.preventDefault();
const btn = event.target.closest("a");
const wrapper = btn.closest(".layer");
const targetUrl = btn.href;
if (targetUrl === window.location.href) return;
removeListeners();
removeTargetPageListeners();
fetch(targetUrl)
.then((res) => res.text())
.then((htmlString) => {
const page = getPage(htmlString);
history.pushState(null, page.title, targetUrl);
let timer = 0;
const currentLevel = document.querySelector(".current");
if (currentLevel.scrollTop > 0) {
document.querySelector(".current").scrollTo({
top: 0,
behavior: "smooth",
});
timer = 500;
}
setTimeout(() => {
if (wrapper.classList.contains("current")) {
moveForward(wrapper, page);
} else {
moveBackward(wrapper, page);
}
document.querySelector("title").textContent = `${page.title}`;
document.querySelectorAll(`.current ~ .layer`).forEach((layer) => {
layer.classList.add("out-screen");
});
const layerBtnsToShow = document.querySelectorAll(
".layer-btn:not(.current .layer-btn)"
);
layerBtnsToShow.forEach((layerBtn) => {
toggleVisibility(layerBtn, window.innerWidth < 640);
});
listenLinks();
showTargetPageSize();
styleCallouts();
initNotes();
linkLayersScrolls();
currentUrl = window.location.href;
}, timer);
})
.catch((err) => {
console.error("Fetch Error:", err);
});
}
function showTargetPageSize() {
const internalLinks = document.querySelectorAll("a.internal-link");
internalLinks.forEach((link) => {
link.addEventListener("mouseenter", handleMouseEnter);
link.addEventListener("mouseleave", handleMouseLeave);
});
}
function listenCollapsableSections() {
const collapsableSections = document.querySelectorAll(
".collapsable__button"
);
const cursorText = document.querySelector("#cursor #text");
collapsableSections.forEach((section) => {
section.addEventListener("mouseenter", () => {
cursorText.style.display = "flex";
cursorText.textContent = "ouvrir/fermer";
});
section.addEventListener("mouseleave", () => {
cursorText.style.display = "";
cursorText.textContent = "";
});
});
}
});
function getPage(htmlString) {
const parser = new DOMParser();
const fullPage = parser.parseFromString(htmlString, "text/html");
const title = fullPage.querySelector("title").textContent;
const content = fullPage.querySelector(".layer.current");
return {
title: title,
content: content,
};
}
function getUrlLastPart(url) {
const parts = url.split("/");
const lastPart = parts[parts.length - 1];
return lastPart;
}
function styleCallouts() {
const callouts = document.querySelectorAll(".callout");
callouts.forEach((callout) => {
if (!callout.closest("p")) return;
callout.closest("p").classList.add("callout");
});
}