10 principes voor vloeiende webanimaties
De complete gids voor 60fps-animaties met CSS

Sinds we vorig jaar Gyroscope lanceerden, hebben veel mensen gevraagd naar de JavaScript-library die we voor onze animaties gebruiken. We hebben overwogen die vrij te geven, maar daar zit de magie eigenlijk niet.
We willen niet dat mensen het gevoel krijgen dat ze afhankelijk zijn van een speciale JavaScript-plug-in die deze problemen op magische wijze oplost. Voor het grootste deel maken we gewoon gebruik van de vooruitgang in browserprestaties, gpu's en de CSS3-spec.
Er is geen wondermiddel voor mooie animaties, behalve veel tijd steken in testen en optimaliseren. Maar na jaren experimenteren en tegen de grenzen van browserprestaties aanlopen, hebben we een reeks design- & codeprincipes opgesteld die betrouwbaar mooie animaties lijken op te leveren. Met deze technieken krijg je pagina's die soepel aanvoelen, die werken in moderne desktop- en mobiele browsers en, het belangrijkst, die makkelijk te onderhouden zijn.

De techniek en de uitvoering zullen voor iedereen net iets anders zijn, maar de algemene principes zouden in vrijwel elke situatie moeten helpen.
Wat is een animatie?
Animaties bestonden al ver voor het internet, en er goed in worden is iets waar je je hele leven aan kunt besteden. Maar ze voor het internet maken brengt zijn eigen beperkingen en uitdagingen mee.
Voor soepele 60fps moet elk frame in minder dan 16 ms worden gerenderd! Dat is niet veel tijd, dus we moeten heel efficiënte manieren vinden om elk frame te renderen.
Er zijn tientallen manieren om animaties op het web te maken. De filmstrip bijvoorbeeld is een aanpak die al van voor het internet stamt, waarbij per seconde vele iets verschillende, met de hand getekende frames worden verwisseld om de illusie van beweging te wekken.
Twitter gebruikte deze simpele aanpak onlangs voor hun nieuwe hartjesanimatie, door door een sprite met frames te bladeren.

Dit effect had ook gekund met een hoop kleine elementen die stuk voor stuk animeren, of misschien als SVG, maar dat zou onnodig ingewikkeld zijn en waarschijnlijk minder soepel.

In veel gevallen wil je de CSS-eigenschap transition gebruiken om een element automatisch te animeren als het verandert. Deze techniek heet ook wel “tweening”, van het overgaan tussen (between) twee waarden. Het voordeel is dat je hem makkelijk kunt annuleren of omkeren zonder al die logica zelf te bouwen. Ideaal voor animaties die je instelt en vergeet, zoals introsequenties, of voor simpele interacties als hovers.
Meer lezen: All you need to know about CSS Transitions

In andere gevallen is de op keyframes gebaseerde CSS-eigenschap animation ideaal voor details die continu op de achtergrond doorlopen. De ringen in het Gyroscope-logo zijn bijvoorbeeld ingesteld om onophoudelijk te draaien. Andere dingen die baat hebben bij de animation-syntax van CSS zijn tandwielverhoudingen.
Zonder verder oponthoud: hier zijn een paar tips die je animatieprestaties hopelijk enorm verbeteren…
#1
Verander geen enkele eigenschap behalve opacity of transform!
Ook al denk je dat het wel goed komt, niet doen!
Alleen dit ene basisprincipe brengt je al 80% van de weg, zelfs op mobiel. Je hebt het vast eerder gehoord, het is geen origineel idee maar het wordt zelden opgevolgd. Het is het webequivalent van “eet gezond en beweeg”: klinkt als goed advies, maar je negeert het waarschijnlijk.
Het is heel overzichtelijk zodra je zo leert denken, maar het kan een grote stap zijn als je gewend bent traditionele CSS-eigenschappen te animeren.
Als je iets kleiner wilt maken, kun je bijvoorbeeld transform: scale() gebruiken in plaats van de breedte te veranderen. Wil je iets verplaatsen, dan hoef je niet met marges of padding te rommelen, wat de hele paginalayout voor elk frame opnieuw zou moeten opbouwen, maar kun je simpelweg transform: translateX of transform: translateY gebruiken.
Waarom werkt dit?
Voor een mens lijkt breedte, marge of een andere eigenschap veranderen misschien geen groot verschil, of zelfs handiger omdat het simpeler is, maar in wat de computer moet doen liggen ze werelden uit elkaar, en het ene is veel, veel slechter.
De browserteams hebben veel goed werk verzet om deze bewerkingen te optimaliseren. Transforms zijn heel makkelijk efficiënt uit te voeren en kunnen vaak je grafische kaart gebruiken zonder de elementen opnieuw te renderen.
Bij het laden van de pagina mag je losgaan: rond alle hoeken af, gebruik afbeeldingen, zet overal schaduwen op, en als je je echt roekeloos voelt zelfs een dynamische blur. Als het maar één keer gebeurt, maken een paar milliseconden extra rekentijd niets uit. Maar zodra de inhoud is gerenderd, wil je niet alles blijven herberekenen.
Meer lezen: Moving elements with translate (Paul Irish)
#2
Verberg inhoud in het volle zicht.
Gebruik pointer-events: none samen met opacity nul om elementen te verbergen
Hier zitten misschien wat haken en ogen tussen browsers, maar als je alleen voor webkit en andere moderne browsers bouwt, maakt dit je leven een stuk makkelijker.
Lang geleden, toen animaties via animate() van jQuery moesten, kwam een groot deel van de complexiteit van in- en uitfaden neer op op het juiste moment schakelen tussen display: none en block. Te vroeg en de animatie maakte hem niet af, te laat en je had onzichtbare inhoud met opacity nul die je pagina afdekte. Alles had callbacks nodig om na afloop op te ruimen.
De CSS-eigenschap pointer-events (die er al heel lang is, maar weinig wordt gebruikt) zorgt er in feite voor dat dingen niet op klikken of interacties reageren, alsof ze er niet zijn. Hij kan simpel via CSS aan en uit zonder animaties te onderbreken of het renderen of de zichtbaarheid te beïnvloeden.
In combinatie met een opacity van nul heeft het praktisch hetzelfde effect als display none, maar zonder de prestatieklap van nieuwe renders. Als ik dingen verberg, zet ik meestal gewoon de opacity op 0 en pointer-events uit, en dan vergeet ik het element in de wetenschap dat het zichzelf redt.
Dit werkt vooral goed met absoluut gepositioneerde elementen, omdat je er zeker van kunt zijn dat ze absoluut geen invloed hebben op iets anders op de pagina.
Het geeft je ook wat speelruimte, want de timing hoeft niet perfect te zijn: het is geen ramp als een element een seconde langer klikbaar is of iets afdekt dan het zichtbaar was, of als het pas klikbaar wordt als het helemaal is ingefade.
#3
Animeer niet alles tegelijk.
Werk liever met choreografie.
Eén animatie kan op zichzelf soepel zijn, maar tegelijk met een hoop andere gaat het waarschijnlijk mis. Het is heel makkelijk om een simpele demo te maken waarin bijna alles soepel loopt, maar een orde van grootte moeilijker om die prestaties op een hele site vast te houden. Daarom is het belangrijk ze goed in te plannen.
Je wilt de timings spreiden, zodat niet alles op precies hetzelfde moment start of loopt. Meestal kunnen er 2 of 3 dingen tegelijk bewegen zonder te haperen, zeker als ze net iets na elkaar zijn gestart. Meer, en je riskeert pieken in de vertraging.
Tenzij er letterlijk maar één ding op je pagina staat, is het belangrijk het begrip choreografie te snappen. Het lijkt een term uit de dans, maar hij is net zo belangrijk voor het animeren van interfaces. Dingen moeten uit de juiste richting en op het juiste moment binnenkomen. Ook al staan ze los van elkaar, ze moeten voelen als onderdeel van één goed ontworpen geheel.
Google's material design doet hier interessante suggesties over. Het is niet de enige juiste manier, maar wel iets waar je over na moet denken en mee moet testen.

Meer lezen: Google Material Design · Motion
#4
Met licht oplopende transition delays choreografeer je beweging moeiteloos.
Animaties choreograferen is echt belangrijk en vraagt veel experimenteren en testen voor het goed voelt. Maar de code ervoor hoeft niet ingewikkeld te zijn.
Ik verander meestal één class op een bovenliggend element (vaak op body) om een reeks transitions af te vuren, en elke transition heeft zijn eigen transition-delay om op het juiste moment binnen te komen. Vanuit de code hoef je je maar om één toestandswisseling te bekommeren, in plaats van tientallen timings in je JavaScript bij te houden.

Een reeks elementen laten inlopen is een simpele en makkelijke manier om ze te choreograferen. Het is krachtig omdat het er tegelijk goed uitziet én je kostbare prestaties oplevert, want onthoud dat je maar een paar dingen tegelijk wilt laten gebeuren. Je wilt ze ver genoeg uit elkaar zetten zodat elk element soepel voelt, maar niet zo ver dat het geheel te traag wordt. Er moet genoeg overlappen om het als een doorlopende stroom te laten voelen in plaats van een rijtje losse dingen.
Codevoorbeeld
Er zijn een paar simpele technieken om je elementen te spreiden, zeker bij een lange lijst. Bij minder dan 10 items, of bij een goed voorspelbaar aantal (zoals op een statische pagina), zet ik de waarden meestal in CSS. Dat is het simpelst en het makkelijkst te onderhouden.

Bij langere lijsten of heel dynamische inhoud kun je de timings dynamisch instellen door door elk item te lopen.

Er zijn meestal twee variabelen: je basisvertraging en de vertraging tussen elk item. Het is een lastige balans, maar als je de juiste getallen te pakken hebt, voelt het precies goed.
#5
Gebruik een globale vermenigvuldiger om in slow motion te ontwerpen
En zet daarna alles op snelheid.
Bij het ontwerpen van animaties draait alles om timing. 20% van het werk is iets bouwen, de andere 80% is de juiste waarden & duren vinden zodat alles synchroon loopt en soepel voelt.
Zeker als je aan de choreografie van meerdere dingen werkt en de prestaties en gelijktijdigheid uit de pagina probeert te persen, wordt het een stuk makkelijker als je het geheel in slow motion kunt zien.
Of je nu Javascript gebruikt of een CSS-preprocessor als SASS (waar wij dol op zijn), het zou vrij eenvoudig moeten zijn om wat extra rekenwerk te doen en met variabelen te bouwen.
Zorg dat het makkelijk is om verschillende snelheden of timings te proberen. Als een animatie zelfs op 1/10 snelheid hapert, is er misschien iets fundamenteel mis. Loopt hij soepel als je hem 50x uitrekt, dan is het alleen nog een kwestie van de hoogste snelheid vinden waarop hij nog werkt. Problemen van 5 milliseconden vallen op volle snelheid nauwelijks op, maar als je het geheel vertraagt worden ze pijnlijk duidelijk.
Zeker bij heel complexe animaties, of bij het oplossen van lastige knelpunten in de prestaties, is het heel nuttig om dingen in slow motion te kunnen zien.
Het idee is dat je terwijl het langzaam gaat een hoop perfecte details inbouwt, en het geheel daarna versnelt zodat het perfect voelt. Het is heel subtiel, maar de gebruiker merkt de soepelheid en de details.
Deze functie zit trouwens in OS X: als je shift ingedrukt houdt terwijl je op de minimaliseerknop of een app-icoon klikt, zie je de animatie in slow motion. Op een gegeven moment hadden we dit slow-motioneffect zelfs in Gyroscope ingebouwd, geactiveerd door shift in te drukken.
#6
Maak video's van je UI en speel ze terug voor een waardevol blik van buitenaf.
Soms zie je dingen scherper vanuit een ander perspectief, en video is daar een prima middel voor.
Sommige mensen bouwen een video in after effects en proberen die daarna op een site na te maken. Ik doe het vaak andersom, en probeer een goede video te maken van de UI van een site.
Een Vine* of video van iets kunnen plaatsen is een behoorlijk hoge lat. Op een dag was ik enthousiast over iets wat ik had gebouwd en probeerde ik een opname te maken om met wat vrienden te delen.
Maar toen ik hem terugkeek, viel me een hoop op wat niet goed was. Er zat een flinke haperpiek in en alle timings waren net verkeerd. Ik kromp een beetje ineen, en in plaats van hem te versturen besefte ik dat er nog veel werk te doen was.
Terwijl je het live gebruikt, kijk je hier makkelijk overheen, maar animaties op video bekijken, keer op keer of in slow motion, maakt elk probleem pijnlijk duidelijk.
Ze zeggen dat de camera je 10 pond dikker maakt. Misschien doet hij er ook 10 frames bij.
Slow-motionvideo's van mijn pagina's bekijken en dingen aanpassen als een frame niet goed voelt, is inmiddels een vast onderdeel van mijn werkwijze. Het is makkelijk om het op trage browsers te schuiven, maar met wat meer optimaliseren en testen kun je al die problemen oplossen.
Zodra je je niet meer schaamt als je op video haperingen betrapt, en de video goed genoeg vindt om te delen, is de pagina waarschijnlijk klaar om uit te brengen.
#7
Netwerkverkeer kan haperingen veroorzaken.
Laad grote HTTP-verzoeken vooraf of stel ze uit
Afbeeldingen zijn hier een grote boosdoener, of het nu om een paar grote gaat (een grote achtergrond bijvoorbeeld), om heel veel kleine (denk aan 50 avatars die laden), of gewoon om veel inhoud (een lange pagina met afbeeldingen tot aan de footer).
Als de pagina voor het eerst laadt, worden er ontelbare dingen geïnitialiseerd en gedownload. Analytics, advertenties en andere scripts van derden maken het nog erger. Soms doet alle animaties een paar honderd milliseconden na het laden uitstellen wonderen voor de prestaties.
Optimaliseer hier niet te ver op door tot het echt nodig is, maar een ingewikkelde pagina heeft misschien heel precieze vertragingen en timings van inhoud nodig om soepel te lopen. In het algemeen wil je in het begin zo min mogelijk data laden, en de rest van de pagina pas laden als het zware werk en de intro-animaties klaar zijn.
Op pagina's met veel data kan het laden een flinke klus zijn. Een animatie die met statische inhoud prima werkt, kan instorten zodra je hem tegelijk met echte data laadt. Als iets zou moeten werken, of soms wel soepel loopt en soms niet, zou ik het netwerkverkeer nakijken om te zien of je niet tegelijk andere dingen doet.
#8
Koppel niets direct aan scroll.
Lijkt een gaaf idee, maar dat is het niet echt.
Animaties op basis van scrollen zijn de afgelopen jaren behoorlijk populair geworden, zeker die met parallax of andere speciale effecten. Of het goed design is, valt te bediscussiëren, maar er zijn technisch betere en slechtere manieren om ze te bouwen.
Een redelijk performante aanpak in deze categorie is een bepaalde scrollafstand als een gebeurtenis behandelen en dingen één keer afvuren. Tenzij je echt weet wat je doet, zou ik deze categorie mijden, want het gaat makkelijk mis en het is lastig te onderhouden.
Nog erger is je eigen scrollbalk bouwen in plaats van de standaard te gebruiken, oftewel scrolljacking. Doe dat alsjeblieft niet.
Dit is zo'n regel die vooral nuttig is voor mobiel, maar waarschijnlijk ook gewoon goed is voor de beste gebruikerservaring.
Wil je echt een bepaald soort ervaring die om scrollen of speciale gebeurtenissen draait, dan zou ik er snel een prototype van bouwen om te zien of het goed presteert, voor je er veel ontwerptijd in steekt.
#9
Test vroeg & vaak op mobiel.
De meeste sites worden op een computer gebouwd, en meestal ook het vaakst getest op diezelfde machine. Daardoor zijn de mobiele ervaring & de animatieprestaties vaak een bijzaak. Sommige technieken (zoals canvas) of animatiemethodes presteren op mobiel minder goed.
Maar goed gecodeerd & geoptimaliseerd (zie regel #1) kan een mobiele ervaring nog soepeler zijn dan op een computer. Optimaliseren voor mobiel was ooit een lastig onderwerp, maar nieuwe iPhones zijn tegenwoordig sneller dan de meeste laptops! Als je de vorige tips hebt gevolgd, kunnen je mobiele prestaties zomaar meteen prima zijn.

Mobiel gebruik is bij vrijwel elke site een groot en heel belangrijk deel. Het klinkt misschien extreem, maar ik zou aanraden om een hele week alleen vanaf je telefoon te kijken. Gedwongen de mobiele versie gebruiken zou niet als straf moeten voelen, maar dat doet het vaak wel.
Blijf het ontwerp verbeteren & de prestaties opvijzelen tot het net zo verzorgd en handig voelt als de grote versie van de site.
Als je jezelf dwingt een week alleen je mobiele site te gebruiken, optimaliseer je hem waarschijnlijk tot een nog betere ervaring dan de grote. Dat je je er tijdens het gebruiken aan ergert is het waard, als de problemen daardoor worden opgelost voordat je gebruikers ze meemaken!
#10
Test regelmatig op meerdere apparaten
Schermgrootte, pixeldichtheid en apparaat kunnen allemaal veel uitmaken
Er zijn behalve mobiel versus desktop nog veel factoren die de prestaties sterk beïnvloeden, zoals of een scherm “retina” is of niet, het totale aantal pixels in het venster, hoe oud de hardware is, enzovoort.
Ook al zijn Chrome en Safari allebei op Webkit gebaseerde browsers met vergelijkbare syntax, ze hebben allebei hun eigen eigenaardigheden. Elke Chrome-update kan dingen repareren en nieuwe bugs introduceren, dus je moet voortdurend alert zijn.
Natuurlijk wil je niet alleen voor de kleinste gemene deler bouwen, dus slimme manieren vinden om verfraaiingen geleidelijk toe te voegen of weg te laten kan heel nuttig zijn.
Ik wissel geregeld tussen mijn kleine MacBook Air en mijn enorme iMac, en elke ronde legt kleine problemen en verbeteringen bloot, vooral in animatieprestaties maar ook in het algemene ontwerp, de informatiedichtheid, de leesbaarheid enzovoort.
Media queries zijn krachtige middelen om deze verschillende groepen te bedienen: anders opmaken op hoogte of breedte is het klassieke gebruik, maar je kunt ze ook inzetten om op pixeldichtheid of andere eigenschappen te richten. Het besturingssysteem en het type apparaat achterhalen kan ook nuttig zijn, want de prestaties op mobiel kunnen heel anders liggen dan op computers.

Ik hoop dat je iets aan deze technieken hebt bij je volgende project. Succes!


