Smidigare animering

När vi animerar i JavaScript finns där ofta rätt stort utrymme för förbättringar. Principer som många inte känner till och föråldrade metoder som gör att du inte får ut allt det där som du egentligen önskar.

Vanliga metoder som används är separata callbacks med funktionerna setTimeout eller setInterval. I denna artikeln kommer jag gå över en, i skrivande stund, hyfsat ny metod kallad requestAnimationFrame samt en del principer som gäller för en smidigare animering.

Funktionen requestAnimationFrame

Istället för att använda de vanliga funktionerna som setTimeout och setInterval så borde ni ta en titt på funktionen requestAnimationFrame. När jag först implementera denna i mina script var det som dag och natt. Renderingen flyter helt enkelt på mycket bättre i jämförelse med alternativen.

Du använder funktionen teoretiskt sätt på följande vis:

int requestAnimationFrame( callback [, Element ] );

Den först parametern är alltså callbackfunktionen som ska kallas vid renderingen. Funktionen behöver också åberopa requestAnimationFrame för att loopa, ex:

( function loop()
{
    requestAnimationFrame( loop );

    // Animation logic
})();

Den andra parametern är Elementet som är relevant till animeringen. Tänk dig ett canvas element eller ett alternativt element som agerar på liknande vis. Detta element är valfritt att ange men kan resultera i ytterligare trimning om det specificeras.

För att avsluta en setTimeout eller setInterval loop så åberopar vi antingen clearTimeout eller clearInterval. På samma vis använder vi i detta sammanhanget cancelRequestAnimationFrame:

void cancelRequestAnimationFrame( id );

Webbläsarkompatibilitet

Självklart så har den teoretiska implementationen inte så stort användningsområde då vi ska applicera detta i vårt arbete. Webbläsarna har alla sina egna varianter på funktionen, naturligtvis.

För att få ett fungerande exempel så lånar jag här ett stycke kod från Paul Irish:

var requestAnimationFrame = ( function()
{
    return window.requestAnimationFrame
        || window.webkitRequestAnimationFrame
        || window.mozRequestAnimationFrame
        || window.oRequestAnimationFrame
        || window.msRequestAnimationFrame

        // Fallback
        || function( callback )
        {return window.setTimeout( callback, 1000 / 60 );};
})();

Vilka webbläsare som har vilken lösning framgår nog lite av dess namn, det är iaf inget jag går in på djupet med här i denna artikeln.

Jag har nu nämnt lite kort hur det funkar och att det finns, vill ni gräva ner er mer i detaljerna så kan ni kolla länkarna längst ner på denna sida för bra referenser.

Principer

Ovanstående är troligen det som kommer göra stört skillnad för dig, om du inte redan använde det dvs. Ytterligare saker vi behöver tänka på kan vi se som en grupp animeringsprinciper. Listan är inte mer än mina egna tankar som utgår från min erfarenhet då jag har jobbat med detta. Känner du att det finns ytterligare punkter så lämna gärna en kommentar.

Håll loopen simpel

Processen som loopas för rendering bör hållas så ren som möjligt från övrig logik. För att åstadkomma detta kan vi försöka räkna ut allt som ska göras innan processen sätts igång. En sådan arbetsgång är självklart inte alltid möjlig, men då den är det så rekomenderar jag att vara strikt med det.

Endast en callback rutin

Ytterligare en princip som jag märkt gör mycket för renderingen är att endast använda en rutin i taget. Om vi startar upp för många simultana ”trådar” så resulterar detta alltså i en sämre rendering. Om du ändå måste animera mer än en sak åt gången så föreslår jag att du bygger ett kösystem som tillåter flera callbacks i samma rutin. Ett exempel på detta har jag lagt ut på github under namnet Animator.

Animator

Jag har byggt ett JavaScript jag döpt till Animator som bygger på ovanstående principer. Skriptet är släppt under både GPL och MIT licens, vilket i princip betyder att du är fri att använda denna koden. För senaste version så kolla mitt github konto.

Fördelarna med Animator

Animator använder sig av requestAnimationFrame ovan förklarat istället för dom vanligare rutinerna.

Animator använder ett kösystem som gör att du inte behöver använda flera simultana rutiner på din hemsida.

Animator är väldokumenterat och städat.

Animator tillhandahåller ett enkelt interface som borde vara lätt att sätta sig in i.

Animator erbjuds med en väldigt öppen license som går att använda i alla sammanhang.

Hur man arbetar med Animator

Vi börjar med att definiera vår renderingsloop:

var loop = function()
{
    // do cool stuff
}

Här efter vill då instansiera Animator:

var ani = new Animator();

Nu har vi alltså vår loop samt vår instans av Animator. För att koppla samman dessa så läger vi funktionen ”loop” i kön på Animator instansen:

ani.addCallback( loop );

Detta kallar automatiskt på metoden ”start” som startar rutinen.

Det var allt för att få igång animationen. För att stoppa den kan det vara passande att redigera vår renderingsprocess:

var loop = function()
{
    // do cool stuff
    if( ended )
        ani.removeCallback( this );
}

På detta viset har vi nu tagit bort just denna callback -funktionen från kön men tillåter andra att köras vidare. Det finns även ytterligare sätt att utföra detta på. För en fullständig beskrivning av gränssnittet så rekomenderar jag att du kollar beskrvningen på github.

Vill vi stoppa hela rutinen kallar vi på metoden ”stop”:

ani.stop();

Är kön dock tom så kommer Animator självmant sluta loopa.

Länkar

http://webstuff.nfshost.com/anim-timing/Overview.html
https://developer.mozilla.org/en/DOM/window.requestAnimationFrame
http://dev.chromium.org/developers/design-documents/requestanimationframe-implementation
http://paulirish.com/2011/requestanimationframe-for-smart-animating/