hecq-et-lesort/assets/js/characters/morphEyes.js
isUnknown 2ec9354266
All checks were successful
Deploy / Deploy to Production (push) Successful in 6s
improve view buttons and home animation
2026-06-24 16:02:13 +02:00

144 lines
4.2 KiB
JavaScript

export function initMorphEyes() {
const svg = document.querySelector(".identity svg");
if (!svg) return;
gsap.registerPlugin(MorphSVGPlugin);
const DURATION = 0.7;
const EASE_IN = "elastic.out(1.3, 0.35)";
const EASE_OUT = "elastic.out(1.3, 0.35)";
// lc-eye-l : composé de plusieurs éléments superposés
const lcEyeLIris = svg.querySelector("#lc-eye-l-iris");
const lcEyeLBase = svg.querySelector("#lc-eye-l-base");
const lcEyeLOverlays = svg.querySelectorAll(
"#lc-eye-l-sclera, #lc-eye-l-shadow, #lc-eye-l-sparkle"
);
// Les trois autres yeux : un seul compound path chacun
const lcEyeRIris = svg.querySelector("#lc-eye-r-iris");
const rcEyeLIris = svg.querySelector("#rc-eye-l-iris");
const rcEyeRIris = svg.querySelector("#rc-eye-r-iris");
// Groupes d'yeux pour le blink
const eyeGroupLcL = svg.querySelector("#lc-eye-l");
const eyeGroupLcR = svg.querySelector("#lc-eye-r");
const eyeGroupRcL = svg.querySelector("#rc-eye-l");
const eyeGroupRcR = svg.querySelector("#rc-eye-r");
const eyeGroups = [eyeGroupLcL, eyeGroupLcR, eyeGroupRcL, eyeGroupRcR];
// Paupières, sourcils, nez : révélés en fondu lors du passage en no-outfit
const faceOverlays = svg.querySelector("#face-overlays");
if (!lcEyeLIris || !lcEyeRIris || !rcEyeLIris || !rcEyeRIris) return;
const origins = {
lcEyeLIris: lcEyeLIris.getAttribute("d"),
lcEyeLBase: lcEyeLBase.getAttribute("d"),
lcEyeRIris: lcEyeRIris.getAttribute("d"),
rcEyeLIris: rcEyeLIris.getAttribute("d"),
rcEyeRIris: rcEyeRIris.getAttribute("d"),
};
let blinkTl = null;
function blink() {
blinkTl = gsap.timeline({ delay: DURATION + 0.1 });
blinkTl
.to(eyeGroups, { scaleY: 0, duration: 0.07, ease: "power2.in", transformOrigin: "center center" })
.to(eyeGroups, { scaleY: 1, duration: 0.1, ease: "power2.out" })
.to(eyeGroups, { scaleY: 0, duration: 0.07, ease: "power2.in", delay: 0.06 })
.to(eyeGroups, { scaleY: 1, duration: 0.1, ease: "power2.out" })
.to(eyeGroups, { scaleY: 0, duration: 0.07, ease: "power2.in", delay: 0.1 })
.to(eyeGroups, { scaleY: 1, duration: 0.12, ease: "power2.out" });
}
function morphToNoOutfit() {
if (blinkTl) blinkTl.kill();
gsap.set(eyeGroups, { scaleY: 1 });
gsap.to(lcEyeLIris, {
morphSVG: "#lc-eye-l-target",
duration: DURATION,
ease: EASE_IN,
});
// base + overlays disparaissent vite pour ne pas masquer le morph de l'iris
gsap.to([lcEyeLBase, ...lcEyeLOverlays], {
opacity: 0,
duration: 0.08,
ease: "power1.in",
});
gsap.to(lcEyeRIris, {
morphSVG: "#lc-eye-r-target",
duration: DURATION,
delay: 0.04,
ease: EASE_IN,
});
gsap.to(rcEyeLIris, {
morphSVG: "#rc-eye-l-target",
duration: DURATION,
delay: 0.02,
ease: EASE_IN,
});
gsap.to(rcEyeRIris, {
morphSVG: "#rc-eye-r-target",
duration: DURATION,
delay: 0.06,
ease: EASE_IN,
});
gsap.to(faceOverlays, {
opacity: 1,
duration: DURATION * 0.8,
delay: DURATION * 0.25,
ease: "power1.out",
});
}
function morphToKawai() {
gsap.to(lcEyeLIris, {
morphSVG: origins.lcEyeLIris,
duration: DURATION,
ease: EASE_OUT,
});
// base + overlays réapparaissent seulement après que l'iris ait fini de morphier
gsap.to([lcEyeLBase, ...lcEyeLOverlays], {
opacity: 1,
duration: 0.1,
delay: DURATION,
ease: "power1.out",
});
gsap.to(lcEyeRIris, {
morphSVG: origins.lcEyeRIris,
duration: DURATION,
delay: 0.04,
ease: EASE_OUT,
});
gsap.to(rcEyeLIris, {
morphSVG: origins.rcEyeLIris,
duration: DURATION,
delay: 0.02,
ease: EASE_OUT,
});
gsap.to(rcEyeRIris, {
morphSVG: origins.rcEyeRIris,
duration: DURATION,
delay: 0.06,
ease: EASE_OUT,
});
gsap.to(faceOverlays, {
opacity: 0,
duration: DURATION * 0.35,
ease: "power2.in",
});
blink();
}
const identity = document.querySelector(".identity");
identity.addEventListener("mouseenter", morphToNoOutfit);
identity.addEventListener("mouseleave", morphToKawai);
}