hecq-et-lesort/assets/js/characters/pupilTracking.js
isUnknown 967afa28c3 characters : refacto clean code, découpage en modules ES
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 <noreply@anthropic.com>
2026-05-28 08:11:47 +02:00

58 lines
2.4 KiB
JavaScript

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);
}