webkitrequestanimationframe w3schools tiempo soy loop eliminar control como cancelanimationframe javascript html google-chrome requestanimationframe

javascript - w3schools - Cuestiones de Chrome requestAnimationFrame



requestanimationframe w3schools (2)

Creé las animaciones para http://www.testufo.com y también un comprobador de coherencia requestAnimationFrame () en http://www.testufo.com/animation-time-graph

La lista de navegadores web que admiten la sincronización automática de requestAnimationFrame () con la frecuencia de actualización del monitor de la computadora (incluso si es diferente a 60Hz), se encuentra en http://www.testufo.com/browser.html ... Esto significa que Monitor de 75Hz, requestAnimationFrame () ahora se llama 75 veces por segundo en los navegadores compatibles, siempre que la página web esté actualmente en primer plano, y el rendimiento de la CPU / gráficos lo permita.

Chrome 29 y 31 funciona bien, al igual que las versiones más recientes de Chrome 30. Afortunadamente, chrome 33 Canary parece haber solucionado más el problema que veo por lo que sé. Ejecuta animaciones mucho más suavemente, sin llamadas innecesarias a requestAnimationFrame ().

También he notado que la administración de energía (la desaceleración / aceleración de la CPU también ahorra energía de la batería) puede causar estragos en la tasa de devolución de llamada de requestAnimationFrame () ... Se manifiesta como picos extraños hacia arriba / hacia abajo en los tiempos de procesamiento de cuadros ( http://www.testufo.com/#test=animation-time-graph&measure=rendering )

Tema relacionado: recolección de basura requestAnimationFrame

He estado trabajando para lograr animaciones suaves en un widget que estoy creando para dispositivos táctiles, y una de las herramientas que encontré para ayudarme con esto ha sido la pantalla de la línea de tiempo de la memoria de Chrome.

Me ha ayudado un poco evaluar el consumo de memoria en el bucle de rAF, pero me preocupan algunos aspectos del comportamiento que estoy observando en Chrome 30 en este momento.

Al ingresar inicialmente a mi página, que tiene el bucle rAF en ejecución, veo esto.

Se ve bien No debería haber un diente de sierra si he hecho mi trabajo y eliminado las asignaciones de objetos en mi bucle interno. Este es un comportamiento consistente con el tema vinculado, es decir, que Chrome tiene una fuga incorporada cada vez que usa rAF. (¡Ay!)

Se vuelve más interesante cuando comienzo a hacer varias cosas en la página.

Realmente no estoy haciendo nada diferente, solo estoy agregando temporalmente dos elementos más que hacen que se apliquen algunos estilos de transformación 3D de CSS3 y luego dejo de interactuar con ellos.

Lo que vemos aquí es que Chrome informa que, de repente, cada disparo de rAF (16 ms) da como resultado un Animation Frame Fired x 3 activado Animation Frame Fired x 3 .

Esta repetición, y la velocidad a la que lo hace, aumenta monótonamente hasta la actualización de la página.

Ya se puede ver en la segunda pantalla la pendiente del diente de sierra que se ha incrementado dramáticamente después de ese salto inicial de Animation Frame Fired a Animation Frame Fired x 3 .

Poco tiempo después ha saltado a x 21 :

Parecería que mi código se está ejecutando un montón de veces adicionales, pero todas las ejecuciones múltiples adicionales son solo desperdicio de calor, cálculos descartados.

Mientras estaba tomando la tercera captura de pantalla, mi Macbook se estaba calentando bastante mal. Poco después, antes de poder arrastrar la línea de tiempo hasta el bit final (alrededor de 8 minutos) para ver a qué se había incrementado el número x , la ventana del inspector dejó de responder por completo, y se me indicó que mi página había dejado de responder y tenía que ser terminado

Aquí está la totalidad del código que se ejecuta en la página:

// ============================================================================ // Copyright (c) 2013 Steven Lu // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // ============================================================================ // This is meant to be a true velocity verlet integrator, which means sending // in for the force and torque a function (not a value). If the forces provided // are evaluated at the current time step then I think we are left with plain // old Euler integration. This is a 3 DOF integrator that is meant for use // with 2D rigid bodies, but it should be equally useful for modeling 3d point // dynamics. // this attempts to minimize memory waste by operating on state in-place. function vel_verlet_3(state, acc, dt) { var x = state[0], y = state[1], z = state[2], vx = state[3], vy = state[4], vz = state[5], ax = state[6], ay = state[7], az = state[8], x1 = x + vx * dt + 0.5 * ax * dt * dt, y1 = y + vy * dt + 0.5 * ay * dt * dt, z1 = z + vz * dt + 0.5 * az * dt * dt, // eqn 1 a1 = acc(x1, y1, z1), ax1 = a1[0], ay1 = a1[1], az1 = a1[2]; state[0] = x1; state[1] = y1; state[2] = z1; state[3] = vx + 0.5 * (ax + ax1) * dt, state[4] = vy + 0.5 * (ay + ay1) * dt, state[5] = vz + 0.5 * (az + az1) * dt; // eqn 2 state[6] = ax1; state[7] = ay1; state[8] = az1; } // velocity indepedent acc --- shit this is gonna need to change soon var acc = function(x, y, z) { return [0,0,0]; }; $("#lock").click(function() { var values = [Number($(''#ax'').val()), Number($(''#ay'').val()), Number($(''#az'').val())]; acc = function() { return values; }; }); // Obtain the sin and cos from an angle. // Allocate nothing. function getRotation(angle, cs) { cs[0] = Math.cos(angle); cs[1] = Math.sin(angle); } // Provide the localpoint as [x,y]. // Allocate nothing. function global(bodystate, localpoint, returnpoint) { getRotation(bodystate[2], returnpoint); // now returnpoint contains cosine+sine of angle. var px = bodystate[0], py = bodystate[1]; var x = localpoint[0], y = localpoint[1]; // console.log(''global():'', cs, [px, py], localpoint, ''with'', [x,y]); // [ c -s px ] [x] // [ s c py ] * [y] // [1] var c = returnpoint[0]; var s = returnpoint[1]; returnpoint[0] = c * x - s * y + px; returnpoint[1] = s * x + c * y + py; } function local(bodystate, globalpoint, returnpoint) { getRotation(bodystate[2], returnpoint); // now returnpoint contains cosine+sine of angle var px = bodystate[0], py = bodystate[1]; var x = globalpoint[0], y = globalpoint[1]; // console.log(''local():'', cs, [px, py], globalpoint, ''with'', [x,y]); // [ c s ] [x - px] // [ -s c ] * [y - py] var xx = x - px, yy = y - py; var c = returnpoint[0], s = returnpoint[1]; returnpoint[0] = c * xx + s * yy; returnpoint[1] = -s * xx + c * yy; } var cumulativeOffset = function(element) { var top = 0, left = 0; do { top += element.offsetTop || 0; left += element.offsetLeft || 0; element = element.offsetParent; } while (element); return { top: top, left: left }; }; // helper to create/assign position debugger (handles a single point) // offset here is a boundingclientrect offset and needs window.scrollXY correction var hasDPOffsetRun = false; var dpoff = false; function debugPoint(position, id, color, offset) { if (offset) { position[0] += offset.left; position[1] += offset.top; } // if (position[0] >= 0) { console.log(''debugPoint:'', id, color, position); } var element = $(''#point'' + id); if (!element.length) { element = $(''<div></div>'') .attr(''id'', ''point'' + id) .css({ pointerEvents: ''none'', position: ''absolute'', backgroundColor: color, border: ''#fff 1px solid'', top: -2, left: -2, width: 2, height: 2, borderRadius: 300, boxShadow: ''0 0 6px 0 '' + color }); $(''body'').append( $(''<div></div>'') .addClass(''debugpointcontainer'') .css({ position: ''absolute'', top: 0, left: 0 }) .append(element) ); if (!hasDPOffsetRun) { // determine the offset of the body-appended absolute element. body''s margin // is the primary offender that tends to throw a wrench into our shit. var dpoffset = $(''.debugpointcontainer'')[0].getBoundingClientRect(); dpoff = [dpoffset.left + window.scrollX, dpoffset.top + window.scrollY]; hasDPOffsetRun = true; } } if (dpoff) { position[0] -= dpoff[0]; position[1] -= dpoff[1]; } // set position element[0].style.webkitTransform = ''translate3d('' + position[0] + ''px,'' + position[1] + ''px,0)''; } var elements_tracked = []; /* var globaleventhandler = function(event) { var t = event.target; if (false) { // t is a child of a tracked element... } }; // when the library is loaded the global event handler for GRAB is not // installed. It is lazily installed when GRAB_global is first called, and so // if you only ever call GRAB then the document does not get any handlers // attached to it. This will remain unimplemented as it''s not clear what the // semantics for defining behavior are. It''s much more straightforward to use // the direct API function GRAB_global(element, custom_behavior) { // this is the entry point that will initialize a grabbable element all state // for the element will be accessible through its __GRAB__ element through // the DOM, and the DOM is never accessed (other than through initial // assignment) by the code. // event handlers are attached to the document, so use GRAB_direct if your // webpage relies on preventing event bubbling. if (elements_tracked.indexOf(element) !== -1) { console.log(''You tried to call GRAB() on an element more than once.'', element, ''existing elements:'', elements_tracked); } elements_tracked.push(element); if (elements_tracked.length === 1) { // this is the initial call document.addEventListener(''touchstart'', globaleventhandler, true); document.addEventListener(''mousedown'', globaleventhandler, true); } } // cleanup function cleans everything up, returning behavior to normal. // may provide a boolean true argument to indicate that you want the CSS 3D // transform value to be cleared function GRAB_global_remove(cleartransform) { document.removeEventListener(''touchstart'', globaleventhandler, true); document.removeEventListener(''mousedown'', globaleventhandler, true); } */ var mousedownelement = false; var stop = false; // there is only one mouse, and the only time when we need to handle release // of pointer is when the one mouse is let go somewhere far away. function GRAB(element, onfinish, center_of_mass) { // This version directly assigns the event handlers to the element // it is less efficient but more "portable" and self-contained, and also // potentially more friendly by using a regular event handler rather than // a capture event handler, so that you can customize the grabbing behavior // better and also more easily define it per element var offset = center_of_mass; var pageOffset = cumulativeOffset(element); var bcrOffset = element.getBoundingClientRect(); bcrOffset = { left: bcrOffset.left + window.scrollX, right: bcrOffset.right + window.scrollX, top: bcrOffset.top + window.scrollY, bottom: bcrOffset.bottom + window.scrollY }; if (!offset) { offset = [element.offsetWidth / 2, element.offsetHeight / 2]; } var model = { state: [0, 0, 0, 0, 0, 0, 0, 0, 0], offset: offset, pageoffset: bcrOffset // remember, these values are pre-window.scroll[XY]-corrected }; element.__GRAB__ = model; var eventhandlertouchstart = function(event) { // set var et0 = event.touches[0]; model.anchor = [0,0]; local(model.state, [et0.pageX - bcrOffset.left - offset[0], et0.pageY - bcrOffset.top - offset[1]], model.anchor); debugPoint([et0.pageX, et0.pageY], 1, ''red''); event.preventDefault(); requestAnimationFrame(step); }; var eventhandlermousedown = function(event) { console.log(''todo: reject right clicks''); // console.log(''a'', document.body.scrollLeft); // set // model.anchor = [event.offsetX - offset[0], event.offsetY - offset[1]]; model.anchor = [0,0]; var globalwithoffset = [event.pageX - bcrOffset.left - offset[0], event.pageY - bcrOffset.top - offset[1]]; local(model.state, globalwithoffset, model.anchor); debugPoint([event.pageX, event.pageY], 1, ''red''); mousedownelement = element; requestAnimationFrame(step); }; var eventhandlertouchend = function(event) { // clear model.anchor = false; requestAnimationFrame(step); }; element.addEventListener(''touchstart'', eventhandlertouchstart, false); element.addEventListener(''mousedown'', eventhandlermousedown, false); element.addEventListener(''touchend'', eventhandlertouchend, false); elements_tracked.push(element); // assign some favorable properties to grabbable element. element.style.webkitTouchCallout = ''none''; element.style.webkitUserSelect = ''none''; // TODO: figure out the proper values for these element.style.MozUserSelect = ''none''; element.style.msUserSelect = ''none''; element.style.MsUserSelect = ''none''; } document.addEventListener(''mouseup'', function() { if (mousedownelement) { mousedownelement.__GRAB__.anchor = false; mousedownelement = false; requestAnimationFrame(step); } }, false); function GRAB_remove(element, cleartransform) {} // unimpld function GRAB_remove_all(cleartransform) {} GRAB($(''#content2'')[0]); (function() { var requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.requestAnimationFrame; window.requestAnimationFrame = requestAnimationFrame; })(); var now = function() { return window.performance ? performance.now() : Date.now(); }; var lasttime = 0; var abs = Math.abs; var dt = 0; var scratch0 = [0,0]; var scratch1 = [0,0]; // memory pool var step = function(time) { dt = (time - lasttime) * 0.001; if (time < 1e12) { // highres timer } else { // ms since unix epoch if (dt > 1e9) { dt = 0; } } // console.log(''dt: '' + dt); lasttime = time; var foundnotstopped = false; for (var i = 0; i < elements_tracked.length; ++i) { var e = elements_tracked[i]; var data = e.__GRAB__; if (data.anchor) { global(data.state, data.anchor, scratch0); scratch1[0] = scratch0[0] + data.offset[0]; scratch1[1] = scratch0[1] + data.offset[1]; //console.log("output of global", point); debugPoint(scratch1, 0, ''blue'', data.pageoffset); } else { scratch1[0] = -1000; scratch1[1] = -1000; debugPoint(scratch1, 0, ''blue''); } // timestep is dynamic and based on reported time. clamped to 100ms. if (dt > 0.3) { //console.log(''clamped from '' + dt + '' @'' + now()); dt = 0.3; } vel_verlet_3(data.state, acc, dt); e.style.webkitTransform = ''translate3d('' + data.state[0] + ''px,'' + data.state[1] + ''px,0)'' + ''rotateZ('' + data.state[2] + ''rad)''; } requestAnimationFrame(step); }; requestAnimationFrame(step);

Para completar, aquí está el HTML de la página de prueba:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta http-equiv="cache-control" content="max-age=0" /> <meta http-equiv="cache-control" content="no-cache" /> <meta http-equiv="expires" content="0" /> <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" /> <meta http-equiv="pragma" content="no-cache" /> <title>symplectic integrator test page</title> <script src="zepto.js"></script> <script src="d3.v3.js"></script> <style type=''text/css''> body { position: relative; margin: 80px; } #content { width: 800px; height: 40px; display: inline-block; background: lightgreen; padding: 20px; margin: 30px; border: green dashed 1px; } #content2 { top: 200px; width: 600px; height: 200px; display: inline-block; background: lightblue; padding: 20px; margin: 30px; border: blue dashed 1px; } </style> </head> <body> <div id=''scrolling-placeholder'' style=''background-color: #eee; height: 1000px;''></div> <label>dt:<input id=''dt'' type=''number'' step=''0.001'' value=''0.016666666'' /></label> <label>ax:<input id=''ax'' type=''number'' step=''0.25'' value=''0'' /></label> <label>ay:<input id=''ay'' type=''number'' step=''0.25'' value=''0'' /></label> <label>t:<input id=''az'' type=''number'' step=''0.01'' value=''0'' /></label> <button id=''lock''>Set</button> <button id=''zerof'' onclick=''$("#ax,#ay,#az").val(0);''>Zero forces</button> <button id=''zerov''>Zero velocities</button> <div> <span id=''content''>content</span> <span id=''content2''>content2</span> </div> <div id=''debuglog''></div> <script src="rb2.js"></script> </body> </html>

Eso debería satisfacer cualquier petición "muéstranos el código".

Ahora no apostaría mi vida en ello, pero estoy bastante seguro de que al menos hice un buen trabajo al usar rAF de manera adecuada. No estoy abusando de nada, y en este punto he refinado el código para que sea muy ligero en la asignación de memoria de Javascript.

Entonces, realmente, no hay ninguna razón para que Chrome tome esto e intente montar mi computadora portátil en órbita como un cohete. Sin razón.

Safari, en general, parece manejarlo mejor (no se muere) y también señalaré que iOS generalmente es capaz de mantener una división div de 200x600px y rotar a 60 fps.

Sin embargo, admito que no he visto a Chrome realmente morir así a menos que lo tenga grabando la línea de tiempo de la memoria.

Estoy bastante solo rascándome la cabeza en este punto. Probablemente sea solo una interacción no intencionada e imprevista con esta característica particular de la herramienta de desarrollo (la única de su tipo, que yo sepa).

Entonces intenté algo nuevo para al menos ayudar a investigar este problema con la activación de devolución de llamada adicional de la línea de tiempo de la memoria:

Añadido estas lineas.

window.rafbuf = []; var step = function(time) { window.rafbuf.push(time);

Básicamente, esto cierra la sesión todas las veces que se llama a mi rutina rAF (la función step() ).

Cuando se ejecuta normalmente, escribe un tiempo aproximadamente cada 16.7 ms.

Tengo esto:

Eso indica claramente que se está repitiendo el step() con el mismo parámetro de entrada de tiempo al menos 22 veces, al igual que la línea de tiempo está tratando de decirme.

Así que te atrevo, internet, a decirme que este es un comportamiento intencionado. :)


Creo que tienes un problema porque llamas a requestAnimationFrame(step); En cada evento de mousedown y mouseup . Ya que su función step() también (como debería) llama a requestAnimationFrame(step); Básicamente, comienza un nuevo "bucle de animación" para cada evento de mousedown y mouseup y, como nunca los detiene, se acumulan.

Puedo ver que también comienzas "bucle de animación" al final de tu código. Si desea volver a dibujar inmediatamente en el evento del mouse, debe mover la función de dibujo de la función step() y llamar directamente desde los controladores de eventos del mouse.

Haciendo algo así:

function redraw() { // drawing logic } function onmousedown() { // ... redraw() } function onmouseup() { // ... redraw() } function step() { redraw(); requestAnimationFrame(step); } requestAnimationFrame(step);