Files
2026-05-16 11:10:19 +02:00

316 lines
8.3 KiB
JavaScript
Executable File

/* =========================
CONFIG
========================= */
const MIN_TIME = 10000;
const startTime = performance.now();
/* =========================
HELPERS
========================= */
function getTimeStamp() {
return new Date().toLocaleTimeString("fr-FR", {
hour: "2-digit",
minute: "2-digit",
second: "2-digit"
});
}
/* =========================
SEQUENTIAL TEXT + FROZEN TIME
========================= */
const steps = document.querySelectorAll(".step");
steps.forEach((el, i) => {
setTimeout(() => {
const timeEl = el.querySelector(".time");
if (timeEl) timeEl.textContent = getTimeStamp();
el.classList.add("visible");
}, i * 600);
});
/* =========================
RANDOM GRID / PIXEL ART
========================= */
const grid = document.getElementById("grid");
let gridSquares = []; // Track for cleanup
let pixelIntervals = []; // Track pixel intervals for cleanup
if (grid) {
const squareCount = Math.floor(Math.random() * 6) + 8;
const occupiedCells = new Set(); // Track occupied grid positions
for (let i = 0; i < squareCount; i++) {
const square = document.createElement("div");
square.className = "square";
// Find an unoccupied position
let col, row, posKey;
let attempts = 0;
do {
col = Math.floor(Math.random() * 5) + 1;
row = Math.floor(Math.random() * 4) + 1;
posKey = `${col}-${row}`;
attempts++;
} while (occupiedCells.has(posKey) && attempts < 50);
// Mark position as occupied
occupiedCells.add(posKey);
square.style.gridColumn = col;
square.style.gridRow = row;
// Make squares visible immediately without animation
square.style.opacity = "1";
square.style.transform = "none";
// Draw pixels on hover (can be triggered multiple times)
const drawHandler = () => {
const canvas = square.querySelector("canvas");
if (!canvas || !square.hasPixels) {
drawPixels(square);
if (!square.pixelInterval) {
startPixelCycle(square); // Start the 5-second cycle for this square
}
}
};
square.addEventListener("pointerenter", drawHandler);
gridSquares.push(square);
grid.appendChild(square);
}
}
function drawPixels(el) {
const existingCanvas = el.querySelector("canvas");
if (existingCanvas) {
existingCanvas.remove();
}
const canvas = document.createElement("canvas");
canvas.width = el.clientWidth;
canvas.height = el.clientHeight;
canvas.style.position = "absolute";
canvas.style.top = "0";
canvas.style.left = "0";
el.appendChild(canvas);
const ctx = canvas.getContext("2d");
const colors = ["#ff4fd8", "#9333ea", "#ef4444"];
const px = 6;
// Fill background
ctx.fillStyle = "#0a0a0c";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Collect all pixel positions that will be drawn
const pixels = [];
for (let x = 0; x < canvas.width; x += px) {
for (let y = 0; y < canvas.height; y += px) {
if (Math.random() > 0.6) {
pixels.push({
x: x,
y: y,
color: colors[Math.floor(Math.random() * colors.length)]
});
}
}
}
// Mark that this square has pixels
el.hasPixels = true;
// Create an offscreen canvas to draw all pixels at once
const offscreenCanvas = document.createElement("canvas");
offscreenCanvas.width = canvas.width;
offscreenCanvas.height = canvas.height;
const offscreenCtx = offscreenCanvas.getContext("2d");
// Draw all pixels on offscreen canvas
offscreenCtx.fillStyle = "#0a0a0c";
offscreenCtx.fillRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
pixels.forEach(pixel => {
offscreenCtx.fillStyle = pixel.color;
offscreenCtx.fillRect(pixel.x, pixel.y, px, px);
});
// Animate the entire pixel pattern sliding in from left
const duration = 400; // ms for animation
const startTime = performance.now();
const startX = -canvas.width; // Start completely off-screen to the left
const endX = 0;
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Clear canvas
ctx.fillStyle = "#0a0a0c";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Calculate current X position
const currentX = startX + (endX - startX) * progress;
// Draw the entire offscreen canvas at the current position
ctx.drawImage(offscreenCanvas, currentX, 0);
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
}
function clearPixels(el) {
const canvas = el.querySelector("canvas");
if (!canvas) return;
const ctx = canvas.getContext("2d");
// Create an offscreen canvas with current pixel state
const offscreenCanvas = document.createElement("canvas");
offscreenCanvas.width = canvas.width;
offscreenCanvas.height = canvas.height;
const offscreenCtx = offscreenCanvas.getContext("2d");
// Copy current canvas to offscreen
offscreenCtx.drawImage(canvas, 0, 0);
// Animate the entire pixel pattern sliding out to the right
const duration = 400; // ms for animation
const startTime = performance.now();
const startX = 0;
const endX = canvas.width; // Slide completely off-screen to the right
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Clear canvas
ctx.fillStyle = "#0a0a0c";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Calculate current X position
const currentX = startX + (endX - startX) * progress;
// Draw the entire offscreen canvas at the current position
ctx.drawImage(offscreenCanvas, currentX, 0);
if (progress < 1) {
requestAnimationFrame(animate);
} else {
// Final clear
ctx.fillStyle = "#0a0a0c";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
}
requestAnimationFrame(animate);
}
function startPixelCycle(square) {
const interval = setInterval(() => {
clearPixels(square);
square.hasPixels = false; // Mark that pixels are cleared
}, 5000);
pixelIntervals.push(interval);
// Store interval on the element for later cleanup
square.pixelInterval = interval;
}
/* =========================
LOADER CLEANUP
========================= */
function cleanupLoader() {
// Clear all intervals
pixelIntervals.forEach(interval => clearInterval(interval));
pixelIntervals = [];
// Remove grid squares
gridSquares.forEach(sq => sq.remove());
gridSquares = [];
// Remove loader
const loader = document.getElementById("loader");
if (loader) {
loader.style.opacity = "0";
loader.style.pointerEvents = "none";
setTimeout(() => {
loader.remove();
}, 800);
}
}
/* =========================
LOADER HANDOFF
========================= */
function hideLoader() {
const elapsed = performance.now() - startTime;
const remaining = Math.max(0, MIN_TIME - elapsed);
setTimeout(() => {
document.body.classList.remove("loading");
cleanupLoader(); // Clean up properly
}, remaining);
}
window.addEventListener("load", hideLoader);
/* =========================
LOADING BAR - OPTIMIZED
========================= */
const loadingBar = document.getElementById('loading-bar');
let currentLoadingInterval = null;
function showLoadingBar() {
// Clear any existing interval first
if (currentLoadingInterval) {
clearInterval(currentLoadingInterval);
}
loadingBar.style.width = '0%';
loadingBar.style.display = 'block';
setTimeout(() => loadingBar.style.width = '30%', 50);
let progress = 30;
currentLoadingInterval = setInterval(() => {
progress += Math.random() * 20;
if (progress < 90) {
loadingBar.style.width = progress + '%';
} else {
clearInterval(currentLoadingInterval);
currentLoadingInterval = null;
}
}, 300);
return currentLoadingInterval;
}
function completeLoadingBar(interval) {
if (interval) {
clearInterval(interval);
if (interval === currentLoadingInterval) {
currentLoadingInterval = null;
}
}
loadingBar.style.width = '100%';
setTimeout(() => {
loadingBar.style.width = '0%';
}, 400);
}
// Initial page load
let pageLoadInterval;
if (document.readyState === 'loading') {
pageLoadInterval = showLoadingBar();
}
window.addEventListener('load', () => {
completeLoadingBar(pageLoadInterval);
});