eduardweb.
Animații (Framer Motion)Intermediar#react#framer-motion#frontend#animations

Tranziții fluide cu layoutId în Framer Motion: De la card la modal fără calcule matematice

De Elisabeta Stan, 26 apr. 2026 · 1 vizualizări · 2 like-uri

Postat acum 1 zi
javascript
function Gallery() {
  const [selectedId, setSelectedId] = useState(null);

  return (
    <>
      {items.map(item => (
        <motion.div 
          layoutId={item.id} 
          onClick={() => setSelectedId(item.id)}
          className="card"
        >
          <motion.img layoutId={`img-${item.id}`} src={item.url} />
          <motion.h2 layoutId={`title-${item.id}`}>{item.title}</motion.h2>
        </motion.div>
      ))}

      <AnimatePresence>
        {selectedId && (
          <motion.div layoutId={selectedId} className="modal">
            <motion.img layoutId={`img-${selectedId}`} src={items.find(i => i.id === selectedId).url} />
            <motion.h2 layoutId={`title-${selectedId}`}>{items.find(i => i.id === selectedId).title}</motion.h2>
            <button onClick={() => setSelectedId(null)}>Close</button>
          </motion.div>
        )}
      </AnimatePresence>
    </>
  );
}

Dacă ai încercat vreodată să animezi manual un element care se transformă dintr-un card mic într-un modal pe tot ecranul, probabil ai ajuns să urăști getBoundingClientRect. Am pățit-o acum vreo doi ani la un proiect de fintech unde clientul voia „feeling de iOS” în browser. M-am chinuit cu calcule de coordonate și scale până am dat de layoutId din Framer Motion. Mi-a tăiat timpul de dezvoltare pe partea de UI cu cel puțin 40% și codul a devenit mult mai ușor de urmărit.

Faza e că Framer Motion face toată magia în spate. Tu doar îi spui: „Elementul ăsta de aici și elementul ăla de acolo sunt, de fapt, aceeași entitate vizuală”. Când starea se schimbă, librăria calculează delta dintre poziții și dimensiuni și aplică un transform fluid. E incredibil de satisfăcător să vezi cum un thumbnail „zboară” și devine header de pagină fără să scrii tu nicio linie de logică de interpolare.

Cum am abordat implementarea

La un proiect recent cu vreo 15 carduri de produs, am folosit un selectedId în state-ul componentei părinte. Când userul dă click pe un card, setăm ID-ul respectiv. Cardul din listă are layoutId={item.id}, iar modalul care apare peste are exact același layoutId.

Secretul pe care l-am învățat pe pielea mea: ordinea în DOM contează pentru z-index, dar pentru Framer Motion contează doar ca ID-urile să se potrivească. Am avut un bug unde animația părea că „se rupe” la mijloc. M-am prins după o oră de debug că uitasem să pun layoutId și pe elementele interne, cum ar fi imaginea sau titlul. Dacă vrei o tranziție perfectă, și sub-elementele trebuie să aibă propriile lor ID-uri unice corelate.

Trade-off-uri și bătăi de cap

Nu totul e lapte și miere. Un trade-off sincer? Performanța pe ecrane foarte mari cu multe elemente complexe. Dacă ai un card plin de grafice SVG și încerci să-l expandezi cu layoutId, s-ar putea să vezi un mic drop de frame-uri pe dispozitive mai slabe. Framer încearcă să animeze layout-ul, dar re-randarea conținutului greu în timpul transformării poate fi costisitoare.

O altă problemă e legată de text. Textul nu se „scalează” frumos prin natura lui în CSS. Dacă expandezi un container, textul din interior va părea că se întinde ciudat (aspect ratio distortion) până când se termină animația. Soluția mea? Folosesc un delay mic pentru opacitatea textului sau îl scot complet din calculul de layout și îl aduc cu un fade-in separat după ce containerul a ajuns la dimensiunea finală.

Când să-l folosești și când nu

Am economisit cam 2 zile de muncă la ultimul proiect folosind tehnica asta pentru un viewer de poze. E perfect pentru galerii, dashboard-uri cu detalii expandabile sau chiar meniuri de navigație mobile-first.

Totuși, dacă ai nevoie de ceva extrem de custom unde vrei să controlezi fiecare curbă Bezier pe axele X și Y separat, s-ar putea ca layoutId să fie prea „opinat” pentru tine. Uneori te lupți cu librăria ca să nu mai facă ea ce crede că e bine. Pentru 90% din cazurile de shared element transitions, e exact ce trebuie.

Voi cum gestionați tranzițiile astea complexe? Mergeți pe mâna librăriilor de genul Framer sau preferați să scrieți propriul engine de tranziții cu CSS variables și Web Animations API?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.