Hopp til innhold

Framer motion i Jøkul

Leiv Fredrik Berge

2020.06.06

Bevegelse er et av hovedprinsippene bak profilen til Fremtind. Men animasjon er ikke lett. Frem til nå har vi stort sett basert oss på enkle transformasjoner i ren CSS. Når man skal gjøre mer animasjon, på større deler av applikasjonen blir det litt kronglete å gjøre alt for hånd i CSS. Det finnes utallige javascript bibliotek for å forbedre utvikleropplevelsen rundt animasjon. En av de nyere alternativene er Framer Motion. Det er arvtageren til Pose fra Popmotion, en av de mest populære og likte animasjonsbibilotekene der ute. De fleste animasjonsbibilotekene har en tendens til å bli veldig imperative, så det parrer ikke så pent med øvrig React kode. Framer Motion derimot er først og fremst deklarativt, selvom du har muligheten til å være imperativ om du må.

<motion.div whileHover={{ scale: 1.15 }} whileTap={{ rotate: 45, scale: 0.95 }} />

APIet til Framer Motion er relativt enkelt å sette seg inn i, og gir mange mulighter til å lage interaktive animasjoner. I eksemplet over er det enkle bevelgelser på hover og tap. Disse blir oversatt til inline style, og så langt det er mulig blir det performant, gpu akselererte, 60 fps css transisjoner. Bibliotektet baserer seg på noen få kompontenter og noen utility hooks. Stort sett så kan du gjøre det meste du trenger av enkle animasjoner med motion.{htmlElement}, og AnimatePresence. AnimatePresence gir muligheten til å lage exit animasjoner, noe som alltid har vært en utfordring i HTML.

<AnimatePresence>
    {isOpen &&
        filteredItems.map((item) => (
            <motion.li
                key={item.title}
                initial={{ y: -60, opacity: 0 }}
                animate={{ y: 0, x: 0, opacity: 1, transition: { duration: 0.25, delay: 0.2 } }}
                exit={{ x: 10, opacity: 0, transition: { duration: 0.25 } }}
                className="jkl-portal-full-screen-menu-item"
            >
                <FullScreenMenuItem path={item.path} title={item.title} />
            </motion.li>
        ))}
</AnimatePresence>

Dette er animasjonen til hovedmenyen her i portalen. Initial er ikke overaskende startverdien til animasjonen. Denne kan være false, om du ikke vil at første render skal animere noe. Animate blir overgangen fra intital staten, til elementet er klart. Her er det også spesifisert noe rundt hvor lenge animasjonen skal vare og et lite delay. Exit spesifiserer hvordan animasjonen ut av DOMet skal være. Noen viktige punkter med dette er at motion må være første child av AnimatePresence, dette kravet kommer nok til å forsvinne etterhvert, men sånn React virker per 16.12.0 må AnimatePresence og motion følge etter hverandre. Det er også viktig med unik key, ikke index, for at Framer Motion skal holde kontroll på elementene.

Noen ganger så må du programatisk sette igang animasjonen, da må du til med litt imperativitet, men vi kan pakke det inn på en måte som fortsatt gir mening i React-verden. I sidemenyen ønsker vi å animere innholdet i menyen ut med det gamle og inn med de nye når du bytter fra feks "Kompontenter" til "Kom i gang".

const controls = useAnimation();

useEffect(() => {
    (async () => {
        await controls.start({ x: 0, opacity: 0, transition: { duration: 0.1 } });
        await controls.start({ x: -200, opacity: 0, transition: { duration: 0.2 } });
        await controls.start({ x: 0, opacity: 1, transition: { duration: 0.2 } });
    })();
}, [currentSection, controls]);

return (
    <motion.ul animate={controls} className="jkl-portal-sidebar-menu__items">
        <Menu items={items} />
    </motion.ul>
);

Så når currentSection endrer seg, så sparker vi igang en async funksjon som starter animasjonssekvensen med objektet vi får tilbake fra useAnimation hooken. Vi kobler den til riktig motion element med å sende inn objektet i animate propen. Det gjør at man kan ganske enkelt orkistrere kompliserte animasjoner.

Så langt jeg holdt på med Framer Motion virker det til å være en lovende løsning for å få på plass bevegelsen som Fremtind profilen trenger. Det er fortsatt mye igjen å sette seg inn i, for å se om dette kan være en god løsning for flere av applikasjonene våre, og om vi skal kunne tilby standardiserte varianter igjenom Jøkul feks. God dokumentasjon, god utvikleropplevelse og bra resultat, gjør at jeg syns det er verdt å jobbe videre med Framer Motion.