Versiunea video a acestui blog

„Framer Motion” este, fără îndoială, una dintre cele mai puternice biblioteci pentru animația UI cu React. Recent, am avut șansa să mă joc în profunzime cu această bibliotecă pentru unele dintre lucrurile la care lucrez la Razorpay. În această postare video pe blog, intenționez să împărtășesc unele dintre învățările mele prin construirea unei componente de etichetă extensibilă al cărei conținut se modifică atunci când utilizatorul trece cu mouse-ul peste ele. Voi discuta despre diferite cazuri de margine într-o componentă ca aceasta și, de asemenea, despre modul în care Framer Motion face ca animațiile complexe de aspect să fie ușor!

Modificarea conținutului unui element atunci când utilizatorul trece cu mouse-ul peste el

Stilul legat de trecerea cursorului este foarte simplu cu CSS datorită pseudo-selectorului :hover, dar nu este foarte clar cum poate fi modificat conținutul unui element la trecerea cursorului. Din fericire, JavaScript expune două evenimente - onMouseEnter și onMouseLeave care sunt declanșate atunci când mouse-ul intră (pasează) pe un element sau, respectiv, părăsește elementul. Aceste evenimente pot fi folosite pentru a actualiza o variabilă de stare în React, care este apoi folosită pentru a reda condiționat conținutul elementului.

function ExpandableTag({ unhoveredText, hoveredText }) {
  const [isHovering, setIsHovering] = useState(false);
  return (
    <div
      className="inline-block rounded-lg relative text-gray-300 bg-gray-900 ring-1 ring-gray-800 px-4 py-1.5 tracking-wider text-sm font-medium whitespace-nowrap"
      onMouseEnter={() => setIsHovering(true)} 
      onMouseLeave={() => setIsHovering(false)} 
    >
      {isHovering ? hoveredText : unhoveredText}
    </div>
  );
}

Avem gata o implementare de bază a componentei, dar există două probleme:

  • Nu există animație. Conținutul se schimbă instantaneu, ceea ce ar putea să nu fie un UX bun în acest scenariu și unele animații subtile pot șlefui și mai mult această componentă.
  • Temutul pâlpâire a pieirii. Acesta este unul greu de surprins, dar în cazul în care textul plasat cu mouse-ul este mai mic decât textul suspendat, utilizatorul poate face ca componenta să intre într-o stare de pâlpâire în care conținutul a componentei se modifică continuu.

Adăugarea de animație de intrare/ieșire la conținut

Pentru a rezolva prima problemă, să începem prin adăugarea de fade-in și fade-out simplă la conținutul în schimbare din etichetă. Când utilizatorul trece cu mouse-ul peste etichetă, textul care nu este trecut cu mouse-ul se estompează, iar textul trecut cu mouse-ul se estompează. Acest lucru este foarte ușor de făcut în Framer Motion cu componenta motion și componenta AnimatePresence. Componenta AnimatePresence trebuie utilizată ori de câte ori există o animație de ieșire definită pentru componenta motion. În cazul nostru, aceasta ar fi animația de atenuare când conținutul ieșește din interfața de utilizare.

function ExpandableTag({ unhoveredText, hoveredText }) {
  const [isHovering, setIsHovering] = useState(false);
  return (
    <div
      className="inline-block rounded-lg relative text-gray-300 bg-gray-900 ring-1 ring-gray-800 px-4 py-1.5 tracking-wider text-sm font-medium whitespace-nowrap"
      onMouseEnter={() => setIsHovering(true)}
      onMouseLeave={() => setIsHovering(false)}
    >
      <AnimatePresence>
        <motion.span initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
          {isHovering ? hoveredText : unhoveredText}
        </motion.span>
      </AnimatePresence>
    </div>
  );
}

Numai acest lucru nu rezolvă problema, Framer Motion nu știe când ar trebui să ruleze animațiile dvs., așa că, implicit, rulează doar când componenta este montată pentru prima dată. Vrem ca animația să ruleze atunci când utilizatorul trece și deplasează elementul (ca atunci când se schimbă conținutul), iar acest lucru este deja urmărit de variabila de stare isHovering. Pentru a-i spune lui Framer Motion să ruleze animațiile, putem pur și simplu să setăm prop key astfel încât să se schimbe atunci când vrem să ruleze animațiile. În cazul nostru, aceasta ar fi setarea key la ceva bazat pe variabila de stare isHovering.

De asemenea, deoarece nu dorim ca animațiile de intrare să ruleze atunci când componenta este montată pentru prima dată, putem renunța la acest comportament setând prop initial din componenta AnimatePresence la false

function ExpandableTag({ unhoveredText, hoveredText }) {
  const [isHovering, setIsHovering] = useState(false);
  return (
    <div
      className="inline-block rounded-lg relative text-gray-300 bg-gray-900 ring-1 ring-gray-800 px-4 py-1.5 tracking-wider text-sm font-medium whitespace-nowrap"
      onMouseEnter={() => setIsHovering(true)}
      onMouseLeave={() => setIsHovering(false)}
    >
      <AnimatePresence
        initial={false} 
      >
        <motion.span
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          key={isHovering ? 'hovering' : 'unhovering'} 
        >
          {isHovering ? hoveredText : unhoveredText}
        </motion.span>
      </AnimatePresence>
    </div>
  );
}