Blog

Gebruik scroll-gedreven animaties voor betere transities

De nieuwe CSS scroll-gedreven animaties API maakt meer gedetailleerde en interactieve overgangen mogelijk.

CSS scroll-gedreven animaties zijn een nieuwe manier om animaties te definiëren die niet gebonden zijn aan tijd. In plaats van een specifieke duur te declareren, gebruik je een functie die een positie koppelt aan een specifieke frame in de animatie. Dit maakt het eenvoudiger om scroll-gedreven animaties toe te voegen, zoals het parallax-effect. Maar het kan ook andere overgangen makkelijker toepasbaar maken, zelfs wanneer er geen directe relatie is tussen de animatiepositie en de gebruikersinteractie.

Transities vs Animaties

Wanneer we werken met interactieve elementen, denken we vaak in termen van verschillende toestanden, waarbij animaties meestal de overgang van de ene toestand naar de andere vormen. Niet verrassend worden deze vaak gerealiseerd door de CSS-transition-eigenschap toe te voegen aan de initiële toestand. Dit in contrast met daadwerkelijke animaties, gedefinieerd met @keyframes, waarbij er een standaard toestand is en de animatie over een bepaalde duur meerdere gedefinieerde toestanden doorloopt. Met de nieuwe eigenschappen wordt de tijdlijn een functie van gebruikersinteractie. De tijdlijn wordt in feite gekoppeld aan de interactie:

  • Bij scroll() komt het eerste frame overeen met de startpositie van de scroll en het laatste frame met de eindpositie.
  • Bij view() wordt de animatie gekoppeld aan de zichtbaarheid van het element. Het eerste keyframe start wanneer het element in beeld komt, en het laatste keyframe wordt bereikt wanneer de tegenoverliggende rand van het element uit de scrollcontainer verdwijnt.

Voorbeeld: Een scrollbare slideshow

Stel je een horizontale lijst met afbeeldingen voor, waarbij alle afbeeldingen behalve degene die in beeld is, in grijstinten worden weergegeven. Zonder scroll-gedreven animaties zou je kunnen zeggen dat de slides ofwel een actieve of een inactieve toestand hebben—de eerste in full-color, de tweede in grijstinten.

Wanneer een slide volledig zichtbaar is, zou de toestand actief moeten zijn, anders inactief. De meest voor de hand liggende oplossing zou zijn om twee klassen te creëren en een IntersectionObserver toe te voegen met een callback die de klassen wisselt zodra een slide het zichtbare gebied binnenkomt of verlaat.

Probeer horizontaal te scrollen in het volgende voorbeeld:

Helaas, je browser heeft geen ondersteuning voor de technologie die in deze demo gebruikt wordt.

Bekijk nu hetzelfde gedrag met een scroll-gedreven animatie met animation-timeline: view(). In plaats van twee toestanden kun je een slide zien als een animatie, die start wanneer de slide in de viewport komt en eindigt wanneer de slide het frame verlaat. Dit betekent dat het midden van de animatie het moment is waarop de slide zich in het centrum van het frame bevindt, de impliciete actieve toestand.

Dit wordt duidelijker als we twee aparte animaties maken met een gedeelde duur, één genaamd move en één genaamd highlight. We kunnen de eerste gebruiken om het scrollgedrag na te bootsen en de tweede om de overgang te regelen:

Helaas, je browser heeft geen ondersteuning voor de technologie die in deze demo gebruikt wordt.

.example__slide {
  animation-name:  move, highlight;
  animation-duration: 5s;
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
  animation-diretion: alternate;
}

@keyframes move {
  0% {
    transform: translateX(130%);
  }
  100% {
    transform: translateX(-110%);
  }
}

@keyframes highlight {
  0% {
    background-color: transparent;
  }
  50% {
    background-color: blue;
  }
  100% {
    background-color: transparent;
  }
}

In plaats van deze animatie te zien als een functie van tijd, beschouw deze als een functie van de scrollpositie van de container en dus als een functie van de positie van de slide. Dit betekent dat we de move-animatie kunnen verwijderen en de animation-timeline kunnen definiëren als een functie van de view. Als je een op Chromium-gebaseerde browser gebruikt, kun je horizontaal scrollen in het volgende voorbeeld om het in actie te zien.

Helaas, je browser heeft geen ondersteuning voor de technologie die in deze demo gebruikt wordt.

.example__slide {
  animation-timeline: view(x);
  animation-name: highlight;
}

Verdere aanpassingen kunnen worden gemaakt door verschillende offsets op te geven voor waar de animation-timeline moet beginnen of eindigen, zodat het hoogtepunt samenvalt met het rustpunt van de slide. In het volgende voorbeeld zorgen de offsets ervoor dat de triggering viewbox een inset heeft, zodat gedeeltelijk zichtbare slides de animatie pas starten wanneer ze de inset-grens overschrijden. Voor het eerste kind is een uitzondering nodig om de linkerzijde van de viewbox uit te lijnen met de zichtbare rand van de container.

Helaas, je browser heeft geen ondersteuning voor de technologie die in deze demo gebruikt wordt.

.example__slide:first-child {
  animation-timeline: view(x 0 var(--offset));
}  
.example__slide:last-child {
  animation-timeline: view(x var(--offset) 0);
}

Hoewel dit gedrag niet per se gekoppeld hoeft te zijn aan scrollinteractie, zorgt dit wel voor een beknoptere implementatie en maakt het complexere, meerstaps animaties als overgangen mogelijk. En niet te vergeten, met het extra voordeel dat het zonder JavaScript werkt! Voorlopig wordt dit alleen ondersteund in Chromium-gebaseerde browsers, maar ondersteuning voor Firefox en Safari is onderweg.

← Alle blogposts