Nettsider med animasjon får litt personlighet, og det skal lite til for å oppnå det. Her er en guide til bruk av CSS-transitions.
Hvilke følelser får du når du ser en nettside med animasjoner? Jeg blir litt nysgjerrig. Nettsiden blir livlig og får personlighet. Animasjoner kan også gjøre nettsiden mer profesjonell, ved at innholdet blir mer sammenhengende. Istedenfor plutselige endringer, kan det vises smarte overganger, som ikke overrasker brukeren. Animasjoner trenger ikke å være spektakulære for å vekke positive følelser eller å gjøre nettsiden mer brukervennlig. I denne teksten ønsker jeg å vise hvor enkelt animasjon kan implementeres.
Hva skal vi lage?
Vi skal lage en animasjon hvor innholdet på nettsiden dukker opp gradvis idet vi scroller.
Kodeeksempelet er skrevet i React, men mye av koden vil være overførbar til annen kode, så lenge du bruker CSS og JavaScript. Nettsiden består av en velkomsttekst, etterfulgt av to rader med tre bokser i hver rad. Det er disse boksene vi skal animere til å først være skjult, så dukke opp.
Hva er egentlig animasjon?
Greia med animasjon er at et element går fra én tilstand til en annen. I vårt tilfelle, har vi én tilstand hvor innholdet først er skjult, så en annen tilstand hvor innhold vises.
I vårt eksempel skal vi først skjule innholdet ved å ha en CSS-klasse reveal
som har egenskapen opacity
satt til en verdi av 0. Vi endrer til tilstanden “vist” ved å også legge til klassen active
, som gir opacity
verdien 1.
// styles.css
.reveal {
opacity: 0;
}
.reveal.active {
opacity: 1;
}
Greia med animasjon er en gradvis endring. Vi ønsker ikke at innholdet plutselig dukker opp fra ingensteds. Det er nettopp overgang, eller transition
, som brukes for animasjoner. Nettleseren vil sømløst forandres fra skjult til synlig.
transition: 1s opacity;
betyr at endring i egenskapen opacity
skal endres i løpet av ett sekund.
// styles.css
.reveal {
opacity: 0;
transition: 1s opacity;
}
Hvordan aktiverer vi animasjonen?
I vårt tilfelle har vi lagd to mulige tilstander ved å lage to klasser med ulike tilstander. Vi ønsker at innholdet er skjult som standard, så innholdet kan alltids ha reveal
-klassen. Spørsmålet blir dermed:
Hvordan kan vi legge til eller fjerne klassen active
på element?
Det finnes veldig mange måter å legge til eller å fjerne en klasse. En måte er å ha en eventlistener som trigger en funksjon. Avhengig av en betingelse, vil funksjonen så enten legge til eller fjerne en klasse.
if (shouldAnimate) {
domElement.classList.add("active");
} else {
domElement.classList.remove("active");
}
Denne funksjonen blir så kalt av en eventlistener som lytter på scrolling.
Hvordan lage en eventlistener for scrolling?
Vi legger til lytteren på vinduet.
window.addEventListener("scroll", revealOrHideElement);
Vi ønsker å sjekke for scrolling idet brukeren besøker siden. Siden vi bruker React, legger vi derfor denne kodesnutten til i en useEffect
. Jeg ønsker å kunne gjenbruke logikken vi lager, så jeg putter det i en custom hook useAddRevealListener
. Når vi bruker eventlisteners, ønsker vi også å fjerne lytteren når komponentet demounter, så ubrukte lyttere ikke bruker unødvendig ytelse.
// useAddRevealListener.jsx
function useAddRevealListener() {
useEffect(() => {
revealOrHideElement();
window.addEventListener("scroll", revealOrHideElement);
return () => window.removeEventListener("scroll", revealOrHideElement);
}, []);
}
Vi har nå en lytter som kaller funksjonen revealOrHideElement
ved scrolling. Men hva skal denne funksjonen egentlig gjøre?
Hvordan vet vi om innholdet bør vises eller ikke?
Vi ønsker å ha en animasjon som viser innhold idet vi scroller ned på innholdet. Hvordan vet vi om brukeren har innholdet i synsfeltet av skjermen?
For brukerens skjerminnhold kan vi bruke window.innerHeight , som forteller oss skjermens høyde. Vi kan så bruke getBoundingClientRect().top som henter ut et element sitt høyde-verdi, som er toppen av elementet. Om toppen av elementet er mindre enn skjermens høydeposisjon, vet vi at den synes. For å sikre oss at vi ser en viss del av elementet, kan vi legge til en terskelverdi for antall piksler av elementet vi ser før animasjonen skjer.
const viewHeight = window.innerHeight;
const elementTopPosition = domElement.getBoundingClientRect().top;
const pixelsOfElementInViewBeforeReveal = 100;
const topOfElementIsWithinViewThreshold =
elementTopPosition <
viewHeightPosition - pixelsOfElementInViewBeforeReveal;
Vi har løst hvordan vi vet om et element er i syne, men hvordan vet vi hvilke elementer vi skal gjøre denne sjekken på? Det kan vi gjøre ved å hente ut alle elementene som har klassenavnet reveal
via en querySelector
.
Dermed har vi det vi trenger for funksjonen scroll-lytteren skal kalle. For hvert element som har klassenavnet reveal
, vil vi sjekke om elementet er i syne. Om brukeren kan se den, legger vi til en klasse. Hvis ikke, fjerner vi klassen.
// useAddRevealListener.jsx
function revealOrHideElement() {
const revealElements = document.querySelectorAll(".reveal");
revealElements.forEach((domElement) => {
const viewHeight = window.innerHeight;
const elementTopPosition = domElement.getBoundingClientRect().top;
const pixelsOfElementInViewBeforeReveal = 100;
const topOfElementIsWithinViewThreshold =
elementTopPosition <
viewHeight - pixelsOfElementInViewBeforeReveal;
if (topOfElementIsWithinViewThreshold) {
domElement.classList.add("active");
} else {
domElement.classList.remove("active");
}
});
}
Hvordan bruker vi animasjonsfunksjonen?
Ettersom vi har puttet all logikken vi trenger i en custom hook, kan vi enkelt kalle denne fra et topp-nivå, for eksempel App
. Om du nå legger til reveal
på et element under App
, vil du se en animasjon idet du scroller over boksene.
// App.js
const App = () => {
useAddRevealListener();
return(
<div className="row reveal">
<div className="box" />
<div className="box" />
<div className="box" />
</div>
);
}
Bonus: Flere animasjoner!
Vi har nå lagd en animasjon for opacity
. Den observante så kanskje i demoen at elementene beveger seg også oppover idet de kommer til synet. En måte å flytte elementer på er å bruke transform
. Vi kan først holde skjulte elementer 100px nedenfor sin vanlige posisjon, så nullstille denne verdien idet de kommer til syne. Legg også merke til i transition
-verdien at vi spesifiserer hvilke animasjoner som overgangen skal gjelde.
/* styles.css */
.reveal {
transform: translateY(100px);
opacity: 0;
transition: 1s opacity, 1s transform;
}
.reveal.active {
transform: translateY(0);
opacity: 1;
}
Vi har nå en mer levende animasjon!
Avsluttende ord
En animasjon kan forklares med at et element endrer fra én tilstand til en annen med en gradvis overgang. Her har vi sett på to overganger: synlighet og bevegelse, men det finnes mange flere å utforske. Vi har brukt transition
uten å spesifisere type animasjon, og har dermed brukt standard-animasjonen ease
. Det finnes flere!
Koden for demoen finner du på denne codepenen: https://codepen.io/haalmarc/pen/LYrmqwW
Videre lesing
Er du mer nysgjerrig på animasjon, kan jeg anbefale denne interaktive guiden: https://www.joshwcomeau.com/animation/css-transitions/
Ønsker du inspirasjon til hvor du kan bruke animasjon, ta en titt på denne: https://yalantis.com/blog/web-animation-technologies-and-tools/
Utgangspunktet for koden min er fra en animasjonsguide for HTML og CSS som du kan finne her: https://alvarotrigo.com/blog/css-animations-scroll/