Cards avec yeux qui suivent le curseur + tilt 3D interactif. Effet immersif type hero section.
"use client";
import { useCallback, useRef } from "react";
export default function EyeTrackingTilt() {
const sectionRef = useRef<HTMLElement>(null);
const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
const eyeRefs = useRef<(HTMLDivElement | null)[][]>([]);
const handleMouseMove = useCallback((e: React.MouseEvent) => {
const section = sectionRef.current;
if (!section) return;
const sRect = section.getBoundingClientRect();
const mouseX = e.clientX - sRect.left;
const mouseY = e.clientY - sRect.top;
itemRefs.current.forEach((el, i) => {
if (!el) return;
const rect = el.getBoundingClientRect();
const elCX = rect.left + rect.width / 2 - sRect.left;
const elCY = rect.top + rect.height / 2 - sRect.top;
const amp = 30;
const rotateY = ((mouseX - elCX) / (sRect.width / 2)) * amp;
const rotateX = ((elCY - mouseY) / (sRect.height / 2)) * amp;
const tx = ((mouseX - elCX) / sRect.width) * amp * 3;
const ty = ((mouseY - elCY) / sRect.height) * amp * 3;
el.style.transform = `perspective(800px) translate(${tx}px, ${ty}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
// Pupilles suivent le curseur
eyeRefs.current[i]?.forEach((pupil) => {
if (!pupil) return;
const eyeRect = pupil.parentElement!.getBoundingClientRect();
const dx = e.clientX - (eyeRect.left + eyeRect.width / 2);
const dy = e.clientY - (eyeRect.top + eyeRect.height / 2);
const angle = Math.atan2(dy, dx);
const dist = Math.min(Math.sqrt(dx * dx + dy * dy), 20) / 4;
pupil.style.transform = `translate(${Math.cos(angle) * dist}px, ${Math.sin(angle) * dist}px)`;
});
});
}, []);
return (
<section ref={sectionRef} onMouseMove={handleMouseMove}>
{/* Cards: background colore, borderRadius 16, boxShadow */}
{/* Yeux: div 18x18 blanc rounded-full */}
{/* Pupille: div 8x8 #1a1a2e rounded-full + glint 3x3 blanc */}
</section>
);
}Chaque card a une amplitude differente pour varier l'effet. Les yeux sont des div blanches avec une pupille sombre. Le glint (point blanc) donne du realisme. Utilisez transition: transform 0.08s sur les pupilles et 0.12s sur les cards.