first commit
This commit is contained in:
Executable
+315
@@ -0,0 +1,315 @@
|
||||
/* =========================
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user