316 lines
8.3 KiB
JavaScript
Executable File
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);
|
|
});
|