Si usaste requestAnimationFrame
, disfrutaste ver tus pinturas sincronizadas con la frecuencia de actualización de la pantalla, lo que genera las animaciones de mayor fidelidad posibles. Además, ahorras el ruido del ventilador de la CPU y la energía de la batería de los usuarios cuando cambian a otra pestaña.
Sin embargo, se realizará un cambio en parte de la API. La marca de tiempo que se pasa a tu función de devolución de llamada cambia de una marca de tiempo típica similar a Date.now()
a una medición de alta resolución de milisegundos de punto flotante desde que se abrió la página. Si usas este valor, deberás actualizar tu código según la explicación que se indica a continuación.
Para que quede claro, esto es a lo que me refiero:
// assuming requestAnimationFrame method has been normalized for all vendor prefixes..
requestAnimationFrame(function(timestamp){
// the value of timestamp is changing
});
Si usas el shim requestAnimFrame
común que se proporciona aquí, no estás usando el valor de marca de tiempo. No te preocupes. :)
Por qué
¿Por qué? Bueno, rAF te ayuda a obtener los 60 fps ideales, que se traducen en 16.7 ms por fotograma. Sin embargo, medir con milisegundos enteros significa que tenemos una precisión de 1/16 para todo lo que queremos observar y segmentar.
Como puedes ver arriba, la barra azul representa la cantidad máxima de tiempo que tienes para hacer todo tu trabajo antes de pintar un fotograma nuevo (a 60 fps). Es probable que estés haciendo más de 16 tareas, pero con milisegundos enteros, solo puedes programar y medir en esos incrementos muy grandes. Eso no es suficiente.
El cronómetro de alta resolución resuelve este problema, ya que proporciona una cifra mucho más precisa:
Date.now() // 1337376068250
performance.now() // 20303.427000007
Actualmente, el temporizador de alta resolución está disponible en Chrome como window.performance.webkitNow()
y, por lo general, este valor es igual al valor del argumento nuevo que se pasa a la devolución de llamada de rAF. Una vez que la especificación avance más en los estándares, el método dejará el prefijo y estará disponible a través de performance.now()
.
También notarás que los dos valores anteriores son muchos órdenes de magnitud diferentes. performance.now()
es una medición de milisegundos de punto flotante desde que comenzó a cargarse esa página en particular (el performance.navigationStart
, para ser más específicos).
En uso
El problema clave que producen los recortes son las bibliotecas de animación que usan este patrón de diseño:
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 edición para corregir esto es bastante fácil: aumenta startTime
y now
para usar window.performance.now()
.
this.startTime = window.performance.now ?
(performance.now() + performance.timing.navigationStart) :
Date.now();
Esta es una implementación bastante ingenua, no usa un método now()
con prefijo y también supone compatibilidad con Date.now()
, que no está en IE8.
Detección de funciones
Si no usas el patrón anterior y solo quieres identificar qué tipo de valor de devolución de llamada obtienes, puedes usar esta técnica:
requestAnimationFrame(function(timestamp){
if (timestamp < 1e12){
// .. high resolution timer
} else {
// integer milliseconds since unix epoch
}
// ...
Verificar if (timestamp < 1e12)
es una prueba rápida para ver qué tan grande es el número con el que estamos tratando. Técnicamente, podría ser un falso positivo, pero solo si una página web está abierta de forma continua durante 30 años. Sin embargo, no podemos probar si es un número de punto flotante (en lugar de redondearse a un número entero). Solicita suficientes temporizadores de alta resolución y obtendrás valores enteros en algún momento.
Planeamos implementar este cambio en Chrome 21, por lo que, si ya aprovechas este parámetro de devolución de llamada, asegúrate de actualizar tu código.