Se hai utilizzato requestAnimationFrame
, avrai apprezzato la possibilità di vedere i tuoi dipinti sincronizzati con la frequenza di aggiornamento dello schermo, ottenendo le animazioni con la massima fedeltà possibile. Inoltre, riduci il rumore della ventola della CPU e il consumo della batteria degli utenti quando passano a un'altra scheda.
Tuttavia, a breve verrà apportata una modifica a parte dell'API. Il timestamp passato alla funzione di callback sta passando da un timestamp Date.now()
tipico a una misurazione ad alta risoluzione in millisecondi con virgola mobile dall'apertura della pagina. Se utilizzi questo valore, dovrai aggiornare il codice, in base alla spiegazione riportata di seguito.
Per chiarezza, 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 utilizzi il valore del timestamp. Non preoccuparti. :)
Perché
Perché? La modalità rAF ti consente di ottenere i 60 fps ideali e 60 fps si traducono in 16, 7 ms per frame. Tuttavia, la misurazione in 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 il tempo massimo a tua disposizione per completare tutto il lavoro prima di dipingere un nuovo frame (a 60 fps). Probabilmente esegui più di 16 operazioni, ma con i millisecondi interi hai la possibilità di pianificare e misurare solo in incrementi molto grandi. Non è abbastanza.
Il timer ad alta risoluzione risolve il problema fornendo un dato 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 è generalmente uguale al nuovo valore dell'argomento passato al callback rAF. Una volta che la specifica sarà stata ulteriormente implementata negli standard, il metodo eliminerà il prefisso e sarà disponibile tramite performance.now()
.
Inoltre, noterai che i due valori riportati sopra sono diversi di molti ordini di grandezza. performance.now()
è una misurazione in millisecondi con virgola mobile dal momento in cui è iniziato il caricamento della pagina in questione (in particolare performance.navigationStart
).
In uso
Il problema principale che si verifica è che le librerie di animazione 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 è abbastanza semplice: aumenta startTime
e now
per utilizzare window.performance.now()
.
this.startTime = window.performance.now ?
(performance.now() + performance.timing.navigationStart) :
Date.now();
Si tratta di un'implementazione abbastanza ingenua, non utilizza un metodo now()
con prefisso e presuppone anche il supporto di Date.now()
, che non è presente in IE8.
Rilevamento di funzionalità
Se non utilizzi il pattern riportato sopra e vuoi 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
}
// ...
La verifica di if (timestamp < 1e12)
è un rapido test per capire quanto grande è il numero con cui abbiamo a che fare. Tecnicamente potrebbe trattarsi di un falso positivo, ma solo se una pagina web è aperta ininterrottamente per 30 anni. Tuttavia, non siamo in grado di verificare se si tratta di un numero in virgola mobile (anziché arrotondato per difetto a un numero intero). Se chiedi timer con una risoluzione sufficientemente elevata, avrai sicuramente valori interi a un certo punto.
Abbiamo in programma di implementare questa modifica in Chrome 21, quindi se utilizzi già questo parametro di callback, assicurati di aggiornare il codice.