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

Cum faci tranziții premium cu layoutId în Framer Motion (fără calcule de pixeli)

De Elena Dumitrescu, 7 iun. 2026 · 1 vizualizări · 2 like-uri

Postat acum 2 zile
typescript
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";

export default function SharedLayout({ items }) {
  const [selectedId, setSelectedId] = useState<string | null>(null);

  return (
    <div className="grid-layout">
      {items.map(item => (
        <motion.div
          key={item.id}
          layoutId={`card-${item.id}`}
          onClick={() => setSelectedId(item.id)}
          className="card"
        >
          <motion.h5>{item.title}</motion.h5>
        </motion.div>
      ))}

      <AnimatePresence>
        {selectedId && (
          <div className="overlay" onClick={() => setSelectedId(null)}>
            <motion.div
              layoutId={`card-${selectedId}`}
              className="modal-content"
            >
              <motion.h5>{items.find(i => i.id === selectedId)?.title}</motion.h5>
              <p>Conținut detaliat care apare fluid...</p>
              <button onClick={() => setSelectedId(null)}>Închide</button>
            </motion.div>
          </div>
        )}
      </AnimatePresence>
    </div>
  );
}

Dacă ați încercat vreodată să animați un card dintr-o listă într-un modal pe tot ecranul, probabil știți cât de frustrant e să calculezi coordonate pe ecran. Cu Framer Motion și proprietatea layoutId, treaba asta devine ridicol de simplă.

Am folosit tehnica asta anul trecut la un dashboard cu vreo 12k de useri activi unde voiam ca detaliile unui raport să se deschidă fluid, nu să apară brusc pe ecran. Rezultatul a fost că sesiunile userilor pe acea pagină au crescut ca timp de interacțiune, pur și simplu pentru că aplicația se simțea mult mai premium, ca o aplicație nativă de iOS.

Cum funcționează magia din spate

Framer Motion folosește o tehnică numită FLIP (First, Last, Invert, Play). În loc să animeze proprietățile de width și height direct (ceea ce ar declanșa reflow în browser și ar distruge frame-rate-ul pe telefoane mai vechi), el face altceva. Calculează poziția de start (First), poziția de final (Last), aplică un transform de scalare inversă ca să pară că e în starea inițială (Invert) și apoi rulează animația pe GPU (Play). E o tehnică veche, dar implementată genial sub capotă.

Tot ce trebuie să facem noi este să punem același layoutId pe containerul mic (cardul) și pe containerul mare (modalul). Framer Motion își dă seama că sunt conceptual același element și face tranziția automată între ele.

Codul de mai jos arată o implementare minimală dar perfect funcțională în React. Am redus totul la esențial ca să înțelegeți logica, fără zgomot de styling.

Trade-off-uri și de ce te lovești în producție

Deși pare magie curată la prima vedere, am dat de câteva ziduri în producție de care vreau să vă feresc:

  1. Distorsionarea textului (Stretch effect): Pentru că Framer folosește scale transforms, textul din interiorul cardului se va lăți sau lungi ciudat în timpul animației dacă nu ești atent. Ca să eviți asta, folosește proprietatea layout și pe elementele text din interior, nu doar pe container. Asta le spune să își recalculeze dimensiunile corect în timpul scale-ului.

  2. Performanța la liste mari: Dacă ai o listă cu 100 de carduri și toate au layoutId activ sau ascultă de schimbări de layout, browserul o să înceapă să gâfâie la render. Am pățit asta pe un ecran de căutare complex. Soluția a fost să activez animația doar pentru cardul pe care s-a dat click direct, nu pe toate simultan.

  3. Z-index și overflow: Modalul extins trebuie să fie randat deasupra celorlalte elemente. Uneori, un simplu z-index pe modal nu e de ajuns dacă părintele are overflow: hidden. În astfel de cazuri, e mai bine să randezi modalul într-un React Portal, dar să păstrezi layoutId intact. Framer Motion știe să animeze elemente chiar dacă sunt mutate în alt nod de DOM prin Portal.

Voi ce folosiți pentru tranziții de genul ăsta? Vă chinuiți cu CSS clasic sau vă bazați pe librării externe?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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