From 967afa28c34f337d19d2fbd633f31a840b64feeb Mon Sep 17 00:00:00 2001 From: isUnknown Date: Thu, 28 May 2026 08:11:47 +0200 Subject: [PATCH] =?UTF-8?q?characters=20:=20refacto=20clean=20code,=20d?= =?UTF-8?q?=C3=A9coupage=20en=20modules=20ES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noms de variables et fonctions explicites (sans abréviation), fonctions unitaires, paths SVG construits via template literals avec constantes nommées. Découpage en assets/js/characters/jump.js et pupilTracking.js, importés dans script.js. Clamp de la rotation des pieds pour éviter le bounce à l'atterrissage. Co-Authored-By: Claude Sonnet 4.6 --- assets/js/characters.js | 217 -------------------------- assets/js/characters/jump.js | 192 +++++++++++++++++++++++ assets/js/characters/pupilTracking.js | 58 +++++++ assets/js/script.js | 50 +++--- site/snippets/head.php | 3 +- 5 files changed, 277 insertions(+), 243 deletions(-) delete mode 100644 assets/js/characters.js create mode 100644 assets/js/characters/jump.js create mode 100644 assets/js/characters/pupilTracking.js diff --git a/assets/js/characters.js b/assets/js/characters.js deleted file mode 100644 index 4f6577a..0000000 --- a/assets/js/characters.js +++ /dev/null @@ -1,217 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const svg = document.querySelector(".characters svg"); - initCharacterJump(); - initPupilTracking(svg); -}); - -function initCharacterJump() { - // Gravité constante — contrairement à un ressort, la force est uniforme : - // la chute accélère jusqu'au sol au lieu de ralentir en apesanteur - const GRAVITY = 900; // accélération vers le bas (unités SVG/s²) - const LAUNCH_V = 225; // impulsion vers le haut (unités SVG/s) - const RESTITUTION = 0.15; // restitution du mini-rebond à l'atterrissage - const CYCLE = 1200; // ms entre deux lancers - const SQUAT_MS = 220; // durée du squat avant lancer - const EXTEND_MS = 100; // extension des jambes juste après le lancer - const FOOT_ROT = 35; // rotation max des pieds (degrés) - // Distance dont le top des jambes descend à compression max (173.32 → 197) - const BODY_SQUAT = 197 - 173.32; - - const rightChar = document.getElementById("right-character"); - const rightBody = document.getElementById("right-character-body"); - const rightLeg = document.getElementById("right-leg1"); - const leftLeg = document.getElementById("left-leg1"); - const rightFoot = document.getElementById("right-foot1"); - const leftFoot = document.getElementById("left-foot1"); - - const LEG_R_REST = "M209.01,216.53C221.33,194.34 209.01,173.32 209.01,173.32"; - const LEG_L_REST = "M189.01,216.53C176.69,194.34 189.01,173.32 189.01,173.32"; - const LEG_R_COMP = "M209.01,216.53C231.01,207 209.01,197 209.01,197"; - const LEG_L_COMP = "M189.01,216.53C167.01,207 189.01,197 189.01,197"; - - function lerpPath(p1, p2, t) { - const re = /-?\d+\.?\d*/g; - const n1 = p1.match(re).map(Number); - const n2 = p2.match(re).map(Number); - let i = 0; - return p1.replace(re, () => { - const v = n1[i] + (n2[i] - n1[i]) * t; - i++; - return v.toFixed(2); - }); - } - - const easeInOut = t => t < 0.5 ? 2*t*t : 1 - (-2*t+2)**2/2; - const easeIn = t => t*t; - - // Crée un ressort secondaire indépendant (fermeture sur pos/vel) - function makeSpring(k, c) { - let pos = 0, vel = 0; - return (target, dt) => { - const acc = -k * (pos - target) - c * vel; - vel += acc * dt; - pos += vel * dt; - return pos; - }; - } - - // Cinématique verticale — pos > 0 = en l'air - let yPos = 0, yVel = 0, landingCompress = 0; - - // Ressorts secondaires : chaque partie suit sa cible avec son propre délai - // Corps/tête : k élevé → réactif, léger overshoot sur le relâché - // Jambes : mêmes paramètres que le corps pour rester visuellement connectés - // Pieds : k bas → floppy, ils traînent naturellement - const bodySpring = makeSpring(300, 18); - const legSpring = makeSpring(300, 18); - const rFootSpring = makeSpring(150, 10); - const lFootSpring = makeSpring(150, 10); - - let hovered = false; - let inCycle = false; - let cycleStart = null; - let launched = false; - let lastTime = null; - - const wrapper = document.querySelector(".characters"); - wrapper.addEventListener("mouseenter", () => { hovered = true; }); - wrapper.addEventListener("mouseleave", () => { hovered = false; }); - - function tick(time) { - if (!lastTime) lastTime = time; - const dt = Math.min((time - lastTime) / 1000, 0.05); - lastTime = time; - - if (hovered && !inCycle) { - cycleStart = time; - inCycle = true; - launched = false; - } - - let elapsed = inCycle ? time - cycleStart : 0; - if (inCycle && elapsed >= CYCLE) { - if (hovered) { - cycleStart = time; - elapsed = 0; - launched = false; - } else { - inCycle = false; - elapsed = 0; - } - } - - // Squat + lancer seulement pendant un cycle actif - let squatCompress = 0; - if (inCycle) { - if (elapsed < SQUAT_MS) { - squatCompress = easeInOut(elapsed / SQUAT_MS); - } else if (elapsed < SQUAT_MS + EXTEND_MS) { - squatCompress = 1 - easeIn((elapsed - SQUAT_MS) / EXTEND_MS); - } - - if (!launched && elapsed >= SQUAT_MS) { - yVel += LAUNCH_V; - launched = true; - } - } - - // Cinématique verticale : gravité constante, contrainte sol - if (yPos > 0 || yVel > 0) { - yVel -= GRAVITY * dt; - } - yPos += yVel * dt; - - if (yPos < 0) { - if (yVel < 0) { - const impactV = -yVel; - if (impactV > 50) { - landingCompress = Math.min(0.6, impactV / LAUNCH_V * 0.8); - } - yVel = impactV > 15 ? impactV * RESTITUTION : 0; - } - yPos = 0; - } - landingCompress = Math.max(0, landingCompress - 4 * dt); - - const ty = -yPos; - - const compressTarget = Math.max(squatCompress, landingCompress); - - // Rotation cible des pieds proportionnelle à la hauteur - const footRotTarget = yPos > 3 - ? Math.min(1, (yPos - 3) / 12) - : 0; - - // Ressorts secondaires : calcul des valeurs avec inertie propre - const bodyDisp = bodySpring(compressTarget * BODY_SQUAT, dt); - const legMorph = Math.max(0, Math.min(1, legSpring(compressTarget, dt))); - const rFootAngle = rFootSpring(footRotTarget * FOOT_ROT, dt); - const lFootAngle = lFootSpring(-footRotTarget * FOOT_ROT, dt); - - rightChar.setAttribute("transform", `translate(0,${ty.toFixed(2)})`); - rightBody.setAttribute("transform", `translate(0,${bodyDisp.toFixed(2)})`); - rightLeg.setAttribute("d", lerpPath(LEG_R_REST, LEG_R_COMP, legMorph)); - leftLeg.setAttribute("d", lerpPath(LEG_L_REST, LEG_L_COMP, legMorph)); - rightFoot.setAttribute("transform", `rotate(${rFootAngle.toFixed(1)},209.01,217.6)`); - leftFoot.setAttribute("transform", `rotate(${lFootAngle.toFixed(1)},189.01,217.6)`); - - requestAnimationFrame(tick); - } - - requestAnimationFrame(tick); -} - -function initPupilTracking(svg) { - const VB_W = 304; - const VB_H = 259; - const S = 1.267471; - const BASE_TX = S * 52.5; - const BASE_TY = S * 87.5; - const MAX_R = 3.5; - const LERP = 0.12; - - // Centre de chaque œil — personnage gauche uniquement - const eyes = [ - { id: "left-pupil", cx: 54.03, cy: 86.79 }, - { id: "right-pupil", cx: 103.55, cy: 86.79 }, - ]; - - const cur = eyes.map(e => ({ x: e.cx, y: e.cy })); - const tgt = eyes.map(e => ({ x: e.cx, y: e.cy })); - - document.addEventListener("mousemove", (e) => { - const rect = svg.getBoundingClientRect(); - const mx = (e.clientX - rect.left) * (VB_W / rect.width); - const my = (e.clientY - rect.top) * (VB_H / rect.height); - - eyes.forEach((eye, i) => { - const dx = mx - eye.cx; - const dy = my - eye.cy; - const dist = Math.hypot(dx, dy); - const ratio = dist > 0 ? Math.min(MAX_R, dist) / dist : 0; - tgt[i].x = eye.cx + dx * ratio; - tgt[i].y = eye.cy + dy * ratio; - }); - }); - - document.addEventListener("mouseleave", () => { - eyes.forEach((eye, i) => { - tgt[i].x = eye.cx; - tgt[i].y = eye.cy; - }); - }); - - function tick() { - eyes.forEach((eye, i) => { - cur[i].x += (tgt[i].x - cur[i].x) * LERP; - cur[i].y += (tgt[i].y - cur[i].y) * LERP; - document.getElementById(eye.id)?.setAttribute( - "transform", - `matrix(${S},0,0,${S},${cur[i].x - BASE_TX},${cur[i].y - BASE_TY})` - ); - }); - requestAnimationFrame(tick); - } - - requestAnimationFrame(tick); -} diff --git a/assets/js/characters/jump.js b/assets/js/characters/jump.js new file mode 100644 index 0000000..095a480 --- /dev/null +++ b/assets/js/characters/jump.js @@ -0,0 +1,192 @@ +const RIGHT_LEG_AXIS_X = 209.01; +const LEFT_LEG_AXIS_X = 189.01; +const ANKLE_Y = 216.53; +const HIP_Y_STANDING = 173.32; +const HIP_Y_SQUATTING = 197; + +// Point de contrôle de Bézier qui détermine la courbure latérale de la jambe +const rightLegBowControlStanding = { x: 221.33, y: 194.34 }; +const rightLegBowControlSquatting = { x: 231.01, y: 207 }; +const leftLegBowControlStanding = { x: 176.69, y: 194.34 }; +const leftLegBowControlSquatting = { x: 167.01, y: 207 }; + +function buildLegSvgPath(legAxisX, bowControl, hipY) { + return `M${legAxisX},${ANKLE_Y}C${bowControl.x},${bowControl.y} ${legAxisX},${hipY} ${legAxisX},${hipY}`; +} + +const RIGHT_LEG_REST_PATH = buildLegSvgPath(RIGHT_LEG_AXIS_X, rightLegBowControlStanding, HIP_Y_STANDING); +const LEFT_LEG_REST_PATH = buildLegSvgPath(LEFT_LEG_AXIS_X, leftLegBowControlStanding, HIP_Y_STANDING); +const RIGHT_LEG_COMPRESSED_PATH = buildLegSvgPath(RIGHT_LEG_AXIS_X, rightLegBowControlSquatting, HIP_Y_SQUATTING); +const LEFT_LEG_COMPRESSED_PATH = buildLegSvgPath(LEFT_LEG_AXIS_X, leftLegBowControlSquatting, HIP_Y_SQUATTING); + +export function initCharacterJump() { + // Gravité constante — contrairement à un ressort, la force est uniforme : + // la chute accélère jusqu'au sol au lieu de ralentir en apesanteur + const GRAVITY = 900; // accélération vers le bas (unités SVG/s²) + const LAUNCH_VELOCITY = 225; // impulsion vers le haut (unités SVG/s) + const RESTITUTION = 0.15; // restitution du mini-rebond à l'atterrissage + const JUMP_CYCLE_DURATION_MS = 1200; // ms entre deux lancers + const SQUAT_DURATION_MS = 220; // durée du squat avant lancer + const LEG_EXTEND_DURATION_MS = 100; // extension des jambes juste après le lancer + const FOOT_MAX_ROTATION_DEG = 45; // rotation max des pieds (degrés) + const BODY_SQUAT_AMPLITUDE = HIP_Y_SQUATTING - HIP_Y_STANDING; + + const rightCharacter = document.getElementById("right-character"); + const rightCharacterBody = document.getElementById("right-character-body"); + const rightLeg = document.getElementById("right-leg1"); + const leftLeg = document.getElementById("left-leg1"); + const rightFoot = document.getElementById("right-foot1"); + const leftFoot = document.getElementById("left-foot1"); + + function interpolateSvgPath(startPath, endPath, interpolationFactor) { + const numberPattern = /-?\d+\.?\d*/g; + const startNumbers = startPath.match(numberPattern).map(Number); + const endNumbers = endPath.match(numberPattern).map(Number); + let numberIndex = 0; + return startPath.replace(numberPattern, () => { + const interpolatedValue = + startNumbers[numberIndex] + + (endNumbers[numberIndex] - startNumbers[numberIndex]) * interpolationFactor; + numberIndex++; + return interpolatedValue.toFixed(2); + }); + } + + const easeInOutQuadratic = (t) => (t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2); + const easeInQuadratic = (t) => t * t; + + // Crée un ressort secondaire indépendant (fermeture sur position/vitesse) + function createSpringSimulator(stiffness, damping) { + let position = 0; + let velocity = 0; + return (target, deltaTime) => { + const acceleration = -stiffness * (position - target) - damping * velocity; + velocity += acceleration * deltaTime; + position += velocity * deltaTime; + return position; + }; + } + + // Cinématique verticale — position > 0 = en l'air + let verticalPosition = 0; + let verticalVelocity = 0; + let landingCompressionRatio = 0; + + // Ressorts secondaires : chaque partie suit sa cible avec son propre délai + // Corps/tête : k élevé → réactif, léger overshoot sur le relâché + // Jambes : mêmes paramètres que le corps pour rester visuellement connectés + // Pieds : k bas → floppy, ils traînent naturellement + const bodySpring = createSpringSimulator(300, 18); + const legSpring = createSpringSimulator(300, 18); + const rightFootSpring = createSpringSimulator(150, 10); + const leftFootSpring = createSpringSimulator(150, 10); + + let isHovered = false; + let isInJumpCycle = false; + let jumpCycleStartTime = null; + let hasLaunched = false; + let previousFrameTime = null; + + const wrapper = document.querySelector(".characters"); + wrapper.addEventListener("mouseenter", () => { isHovered = true; }); + wrapper.addEventListener("mouseleave", () => { isHovered = false; }); + + function computeSquatCompressionRatio(elapsedCycleTime) { + if (elapsedCycleTime < SQUAT_DURATION_MS) { + return easeInOutQuadratic(elapsedCycleTime / SQUAT_DURATION_MS); + } + if (elapsedCycleTime < SQUAT_DURATION_MS + LEG_EXTEND_DURATION_MS) { + return 1 - easeInQuadratic((elapsedCycleTime - SQUAT_DURATION_MS) / LEG_EXTEND_DURATION_MS); + } + return 0; + } + + function applyLandingBounce(impactVelocity) { + if (impactVelocity > 50) { + landingCompressionRatio = Math.min(0.6, (impactVelocity / LAUNCH_VELOCITY) * 0.8); + } + verticalVelocity = impactVelocity > 15 ? impactVelocity * RESTITUTION : 0; + } + + function updateVerticalPhysics(deltaTime) { + if (verticalPosition > 0 || verticalVelocity > 0) { + verticalVelocity -= GRAVITY * deltaTime; + } + verticalPosition += verticalVelocity * deltaTime; + + if (verticalPosition < 0) { + if (verticalVelocity < 0) { + applyLandingBounce(-verticalVelocity); + } + verticalPosition = 0; + } + + landingCompressionRatio = Math.max(0, landingCompressionRatio - 4 * deltaTime); + } + + function updateJumpCycle(currentTime) { + if (isHovered && !isInJumpCycle) { + jumpCycleStartTime = currentTime; + isInJumpCycle = true; + hasLaunched = false; + } + + let elapsedCycleTime = isInJumpCycle ? currentTime - jumpCycleStartTime : 0; + + if (isInJumpCycle && elapsedCycleTime >= JUMP_CYCLE_DURATION_MS) { + if (isHovered) { + jumpCycleStartTime = currentTime; + elapsedCycleTime = 0; + hasLaunched = false; + } else { + isInJumpCycle = false; + elapsedCycleTime = 0; + } + } + + let squatCompressionRatio = 0; + if (isInJumpCycle) { + squatCompressionRatio = computeSquatCompressionRatio(elapsedCycleTime); + + if (!hasLaunched && elapsedCycleTime >= SQUAT_DURATION_MS) { + verticalVelocity += LAUNCH_VELOCITY; + hasLaunched = true; + } + } + + return squatCompressionRatio; + } + + function applyTransforms(compressionTarget, deltaTime) { + const verticalTranslation = -verticalPosition; + const footRotationTarget = verticalPosition > 3 ? Math.min(1, (verticalPosition - 3) / 12) : 0; + + const bodyVerticalDisplacement = bodySpring(compressionTarget * BODY_SQUAT_AMPLITUDE, deltaTime); + const legCompressionRatio = Math.max(0, Math.min(1, legSpring(compressionTarget, deltaTime))); + const rightFootRotationAngle = Math.max(0, rightFootSpring(footRotationTarget * FOOT_MAX_ROTATION_DEG, deltaTime)); + const leftFootRotationAngle = Math.min(0, leftFootSpring(-footRotationTarget * FOOT_MAX_ROTATION_DEG, deltaTime)); + + rightCharacter.setAttribute("transform", `translate(0,${verticalTranslation.toFixed(2)})`); + rightCharacterBody.setAttribute("transform", `translate(0,${bodyVerticalDisplacement.toFixed(2)})`); + rightLeg.setAttribute("d", interpolateSvgPath(RIGHT_LEG_REST_PATH, RIGHT_LEG_COMPRESSED_PATH, legCompressionRatio)); + leftLeg.setAttribute("d", interpolateSvgPath(LEFT_LEG_REST_PATH, LEFT_LEG_COMPRESSED_PATH, legCompressionRatio)); + rightFoot.setAttribute("transform", `rotate(${rightFootRotationAngle.toFixed(1)},${RIGHT_LEG_AXIS_X},217.6)`); + leftFoot.setAttribute("transform", `rotate(${leftFootRotationAngle.toFixed(1)},${LEFT_LEG_AXIS_X},217.6)`); + } + + function animateJump(currentTime) { + if (!previousFrameTime) previousFrameTime = currentTime; + const deltaTime = Math.min((currentTime - previousFrameTime) / 1000, 0.05); + previousFrameTime = currentTime; + + const squatCompressionRatio = updateJumpCycle(currentTime); + updateVerticalPhysics(deltaTime); + + const compressionTarget = Math.max(squatCompressionRatio, landingCompressionRatio); + applyTransforms(compressionTarget, deltaTime); + + requestAnimationFrame(animateJump); + } + + requestAnimationFrame(animateJump); +} diff --git a/assets/js/characters/pupilTracking.js b/assets/js/characters/pupilTracking.js new file mode 100644 index 0000000..94974b1 --- /dev/null +++ b/assets/js/characters/pupilTracking.js @@ -0,0 +1,58 @@ +export function initPupilTracking(svg) { + const VIEWBOX_WIDTH = 304; + const VIEWBOX_HEIGHT = 259; + const PUPIL_SCALE = 1.267471; + const PUPIL_BASE_TRANSLATE_X = PUPIL_SCALE * 52.5; + const PUPIL_BASE_TRANSLATE_Y = PUPIL_SCALE * 87.5; + const PUPIL_MAX_OFFSET_RADIUS = 3.5; + const PUPIL_LERP_FACTOR = 0.12; + + // Centre de chaque œil — personnage gauche uniquement + const eyes = [ + { id: "left-pupil", centerX: 54.03, centerY: 86.79 }, + { id: "right-pupil", centerX: 103.55, centerY: 86.79 }, + ]; + + const currentPupilPositions = eyes.map((eye) => ({ x: eye.centerX, y: eye.centerY })); + const targetPupilPositions = eyes.map((eye) => ({ x: eye.centerX, y: eye.centerY })); + + document.addEventListener("mousemove", (event) => { + const svgBoundingRect = svg.getBoundingClientRect(); + const mouseViewportX = (event.clientX - svgBoundingRect.left) * (VIEWBOX_WIDTH / svgBoundingRect.width); + const mouseViewportY = (event.clientY - svgBoundingRect.top) * (VIEWBOX_HEIGHT / svgBoundingRect.height); + + eyes.forEach((eye, eyeIndex) => { + const deltaX = mouseViewportX - eye.centerX; + const deltaY = mouseViewportY - eye.centerY; + const distanceToMouse = Math.hypot(deltaX, deltaY); + const clampRatio = distanceToMouse > 0 ? Math.min(PUPIL_MAX_OFFSET_RADIUS, distanceToMouse) / distanceToMouse : 0; + targetPupilPositions[eyeIndex].x = eye.centerX + deltaX * clampRatio; + targetPupilPositions[eyeIndex].y = eye.centerY + deltaY * clampRatio; + }); + }); + + document.addEventListener("mouseleave", () => { + eyes.forEach((eye, eyeIndex) => { + targetPupilPositions[eyeIndex].x = eye.centerX; + targetPupilPositions[eyeIndex].y = eye.centerY; + }); + }); + + function animatePupils() { + eyes.forEach((eye, eyeIndex) => { + currentPupilPositions[eyeIndex].x += + (targetPupilPositions[eyeIndex].x - currentPupilPositions[eyeIndex].x) * PUPIL_LERP_FACTOR; + currentPupilPositions[eyeIndex].y += + (targetPupilPositions[eyeIndex].y - currentPupilPositions[eyeIndex].y) * PUPIL_LERP_FACTOR; + document + .getElementById(eye.id) + ?.setAttribute( + "transform", + `matrix(${PUPIL_SCALE},0,0,${PUPIL_SCALE},${currentPupilPositions[eyeIndex].x - PUPIL_BASE_TRANSLATE_X},${currentPupilPositions[eyeIndex].y - PUPIL_BASE_TRANSLATE_Y})` + ); + }); + requestAnimationFrame(animatePupils); + } + + requestAnimationFrame(animatePupils); +} diff --git a/assets/js/script.js b/assets/js/script.js index e751723..a3c4453 100644 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -1,21 +1,22 @@ -document.addEventListener("DOMContentLoaded", () => { - const header = document.querySelector(".main-header"); - const charactersWrapper = document.querySelector(".characters"); - const logo = document.querySelector(".logo"); - const svg = charactersWrapper.querySelector("svg"); +import { initCharacterJump } from "./characters/jump.js"; +import { initPupilTracking } from "./characters/pupilTracking.js"; - const CHAR_SPEED = 0.6; - const LOGO_SPEED = 1.2; +document.addEventListener("DOMContentLoaded", () => { + const header = document.querySelector(".main-header"); + const charactersWrapper = document.querySelector(".characters"); + const logo = document.querySelector(".logo"); + const svg = charactersWrapper.querySelector("svg"); + + const CHAR_SPEED = 0.6; + const LOGO_SPEED = 1.2; const CHAR_WIDTH_MAX = 304; const CHAR_WIDTH_MIN = 150; // Valeurs initiales lues depuis l'attribut style HTML - const charMax = parseFloat( - charactersWrapper.style.getPropertyValue("--offset") - ); + const charMax = parseFloat(charactersWrapper.style.getPropertyValue("--offset")); const logoMax = logo.offsetHeight + 100; - let menuOpen = false; + let menuOpen = false; let menuTransitioning = false; function onScroll() { @@ -33,10 +34,8 @@ document.addEventListener("DOMContentLoaded", () => { function scaleCharacters(scrollY) { const charOffset = Math.max(0, charMax - (scrollY / 16) * CHAR_SPEED); - const charWidth = - CHAR_WIDTH_MIN + - (charOffset / charMax) * (CHAR_WIDTH_MAX - CHAR_WIDTH_MIN); - svg.setAttribute("width", charWidth + "px"); + const charWidth = CHAR_WIDTH_MIN + (charOffset / charMax) * (CHAR_WIDTH_MAX - CHAR_WIDTH_MIN); + svg.setAttribute("width", charWidth + "px"); svg.setAttribute("height", charWidth + "px"); } @@ -50,23 +49,23 @@ document.addEventListener("DOMContentLoaded", () => { { const nav = { toggleBtn: document.querySelector(".toggle-nav"), - strip: document.querySelector(".strip"), + strip: document.querySelector(".strip"), }; function openMenu() { menuOpen = true; header.classList.add("menu-transitioning", "big"); nav.strip.classList.add("open"); - nav.toggleBtn.textContent = "fermer"; + nav.toggleBtn.textContent = "fermer"; document.body.style.overflow = "hidden"; } function closeMenu() { - menuOpen = false; + menuOpen = false; menuTransitioning = true; header.classList.remove("big"); nav.strip.classList.remove("open"); - nav.toggleBtn.textContent = "menu"; + nav.toggleBtn.textContent = "menu"; document.body.style.overflow = ""; setTimeout(() => { header.classList.remove("menu-transitioning"); @@ -78,14 +77,17 @@ document.addEventListener("DOMContentLoaded", () => { nav.strip.classList.contains("open") ? closeMenu() : openMenu(); }); - document.addEventListener("keydown", (e) => { - if (e.key === "Escape" && menuOpen) closeMenu(); + document.addEventListener("keydown", (event) => { + if (event.key === "Escape" && menuOpen) closeMenu(); }); - document.addEventListener("click", (e) => { + document.addEventListener("click", (event) => { if (!menuOpen) return; - if (e.target.closest(".toggle-nav")) return; - if (!e.target.closest(".strip")) closeMenu(); + if (event.target.closest(".toggle-nav")) return; + if (!event.target.closest(".strip")) closeMenu(); }); } + + initCharacterJump(); + initPupilTracking(svg); }); diff --git a/site/snippets/head.php b/site/snippets/head.php index fd0ff2c..fd10360 100644 --- a/site/snippets/head.php +++ b/site/snippets/head.php @@ -7,6 +7,5 @@ - - + \ No newline at end of file