Se hai usato requestAnimationFrame
, ti è piaciuto vedere i colori sincronizzati con la frequenza di aggiornamento dello schermo, ottenendo le animazioni ad alta precisione possibili. Inoltre, risparmi il rumore della ventola della CPU e l'energia della batteria quando passano a un'altra scheda.
Tuttavia, sta per essere apportata una modifica a una parte dell'API. Il timestamp che viene passato alla tua funzione di callback sta cambiando da un tipico timestamp Date.now()
a una misurazione ad alta risoluzione dei millisecondi in virgola mobile dall'apertura della pagina. Se utilizzi questo valore, dovrai aggiornare il codice in base alla spiegazione riportata di seguito.
Giusto per essere chiari, ecco di cosa sto parlando:
// assuming requestAnimationFrame method has been normalized for all vendor prefixes..
requestAnimationFrame(function(timestamp){
// the value of timestamp is changing
});
Se utilizzi lo shim requestAnimFrame
comune fornito qui, non devi utilizzare il valore del timestamp. Sei fuori gioco. :)
Perché
Perché? Bene rAF ti aiuta a ottenere gli ultimi 60 fps che è l'ideale, e 60 fps si traduce in 16,7 ms per frame. Tuttavia, la misurazione con millisecondi interi significa che abbiamo una precisione di 1/16 per tutto ciò che vogliamo osservare e scegliere come target.
Come puoi vedere sopra, la barra blu rappresenta la quantità massima di tempo che hai a disposizione per completare il lavoro prima di dipingere un nuovo fotogramma (a 60 fps). Probabilmente stai effettuando più di 16 passaggi, ma con i millisecondi interi hai la possibilità di pianificare e misurare solo in questi incrementi molto consistenti. Non è abbastanza.
Il Timer ad alta risoluzione risolve questo problema fornendo un valore molto più preciso:
Date.now() // 1337376068250
performance.now() // 20303.427000007
Il timer ad alta risoluzione è attualmente disponibile in Chrome come window.performance.webkitNow()
e questo valore in genere corrisponde al valore del nuovo argomento passato al callback rAF. Una volta che le specifiche avranno superato gli standard, il metodo eliminerà il prefisso e sarà disponibile fino al giorno performance.now()
.
Noterai inoltre che i due valori sopra riportati presentano molti ordini di grandezza diversi. performance.now()
indica i millisecondi in virgola mobile da quando è iniziato il caricamento di quella pagina specifica (performance.navigationStart
per essere precisi).
In uso
Il problema principale che viene ritagliato riguarda le librerie di animazioni che utilizzano questo pattern di progettazione:
function MyAnimation(duration) {
this.startTime = Date.now();
this.duration = duration;
requestAnimFrame(this.tick.bind(this));
}
MyAnimation.prototype.tick = function(time) {
var now = Date.now();
if (time > now) {
this.dispatchEvent("ended");
return;
}
...
requestAnimFrame(this.tick.bind(this));
}
Una modifica per risolvere il problema è piuttosto semplice... aumenta startTime
e now
per usare window.performance.now()
.
this.startTime = window.performance.now ?
(performance.now() + performance.timing.navigationStart) :
Date.now();
Questa è un'implementazione abbastanza ingenua, non utilizza un metodo now()
con prefisso e presuppone anche il supporto di Date.now()
, che non è in IE8.
Rilevamento delle funzionalità
Se non utilizzi lo schema riportato sopra e desideri solo identificare il tipo di valore di callback che ricevi, puoi utilizzare questa tecnica:
requestAnimationFrame(function(timestamp){
if (timestamp < 1e12){
// .. high resolution timer
} else {
// integer milliseconds since unix epoch
}
// ...
Controllare if (timestamp < 1e12)
è un rapido test per capire con che numero abbiamo a che fare. Tecnicamente potrebbe essere un falso positivo, ma solo se una pagina web rimane aperta ininterrottamente per 30 anni. Tuttavia, non siamo in grado di verificare se si tratta di un numero in virgola mobile (invece che inferiore a un numero intero). Richiedi un numero sufficiente di timer per l'alta risoluzione e prima o poi riceverai valori interi.
Abbiamo intenzione di implementare questa modifica in Chrome 21, quindi se utilizzi già questo parametro di callback, assicurati di aggiornare il codice.