javascript - ¿Admite HTML5/Canvas doble búfer?
double-buffering (12)
¡No hay parpadeos en los navegadores web! Ya usan el buffer de dbl para su renderizado. El motor de Js hará toda tu representación antes de mostrarla. Además, el contexto de guardar y restaurar solo pila de datos de matriz transformacional y tal, no el contenido del lienzo en sí. Entonces, ¡no necesitas ni quieres el buffer de dbl!
Lo que me gustaría hacer es dibujar mis gráficos en un búfer y luego poder copiarlos tal como están en el lienzo para poder hacer animaciones y evitar el parpadeo. Pero no pude encontrar esta opción. Alguien sabe cómo puedo hacer esto?
El siguiente enlace útil, además de mostrar ejemplos y ventajas de utilizar el doble almacenamiento en búfer, muestra varios otros consejos de rendimiento para usar el elemento de lienzo html5. Incluye enlaces a pruebas jsPerf, que agregan resultados de prueba a través de navegadores en una base de datos de Browserscope. Esto asegura que las sugerencias de rendimiento sean verificadas.
http://www.html5rocks.com/en/tutorials/canvas/performance/
Para su comodidad, he incluido un ejemplo mínimo de doble buffer eficaz como se describe en el artículo.
// canvas element in DOM
var canvas1 = document.getElementById(''canvas1'');
var context1 = canvas1.getContext(''2d'');
// buffer canvas
var canvas2 = document.createElement(''canvas'');
canvas2.width = 150;
canvas2.height = 150;
var context2 = canvas2.getContext(''2d'');
// create something on the canvas
context2.beginPath();
context2.moveTo(10,10);
context2.lineTo(10,30);
context2.stroke();
//render the buffered canvas onto the original canvas element
context1.drawImage(canvas2, 0, 0);
En la mayoría de las situaciones, no necesita hacer esto, el navegador implementa esto para usted. ¡Pero no siempre es útil!
Aún debes implementar esto cuando tu dibujo es muy complicado. La mayor parte de la velocidad de actualización de la pantalla es de aproximadamente 60Hz, significa que la pantalla se actualiza por 16 ms. La tasa de actualización del navegador puede acercarse a este número. Si su forma necesita 100ms para completarse, verá una forma incompleta. Entonces puedes implementar doble buffering en esta situación.
Hice una prueba: Clear a rect, wait for some time, then fill with some color.
Si configuro el tiempo en 10 ms, no veré el parpadeo. Pero si lo configuro en 20 ms, el parpadeo sucede.
En lugar de tirar el tuyo, probablemente obtendrás el mejor millaje al usar una biblioteca existente para crear animaciones de JavaScript limpias y sin parpadeos:
Aquí hay uno popular: http://processingjs.org
Josh preguntó (hace un tiempo) sobre cómo el navegador sabe "cuando termina el proceso de dibujo" para evitar el parpadeo. Hubiera comentado directamente en su publicación, pero mi representante no es lo suficientemente alto. También esta es solo mi opinión. No tengo datos para respaldarlo, pero me siento bastante seguro al respecto y puede ser útil para otros que lean esto en el futuro.
Supongo que el navegador no "sabe" cuando termines de dibujar. Pero al igual que la mayoría de los javascript, siempre y cuando el código se ejecute sin renunciar al control del navegador, el navegador está esencialmente bloqueado y no podrá / no podrá actualizar / responder a su UI. Supongo que si borras el lienzo y dibujas tu fotograma completo sin ceder el control al navegador, en realidad no dibujará tu lienzo hasta que hayas terminado.
Si configura una situación en la que su representación abarque varias llamadas setTimeout / setInterval / requestAnimationFrame, en las que borre el lienzo en una llamada y dibuje elementos en su lienzo en las próximas llamadas, repitiendo el ciclo (por ejemplo) cada 5 llamadas, I Estaría dispuesto a apostar que vería parpadeo ya que el lienzo se actualizaría después de cada llamada.
Dicho eso, no estoy seguro de que confíe en eso. Ya estamos en el punto de que javascript se compila en código de máquina nativo antes de la ejecución (al menos eso es lo que hace el motor V8 de Chrome por lo que yo entiendo). No me sorprendería si no fuera demasiado tiempo antes de que los navegadores comenzaran a ejecutar su javascript en un hilo separado de la IU y sincronizando cualquier acceso a los elementos de la IU que permitiera a la IU actualizar / responder durante la ejecución de javascript que no estaba accediendo a UI. Cuando / si sucede eso (y entiendo que hay muchos obstáculos que deberían superarse, como los controladores de eventos que se inician mientras todavía está ejecutando otro código), probablemente veamos un parpadeo en la animación de lienzo que no están usando algún tipo de doble buffering
Personalmente, me encanta la idea de dos elementos de lona colocados uno encima del otro y alternando lo que se muestra / dibuja en cada cuadro. Bastante poco intrusivo y probablemente bastante fácil de agregar a una aplicación existente con algunas líneas de código.
Los navegadores que he probado manejan este buffer por ti al no volver a pintar el lienzo hasta que el código que dibuja tu fotograma haya finalizado. Ver también la lista de correo de WHATWG: http://www.mail-archive.com/[email protected]/msg19969.html
Más de dos años después:
No es necesario implementar ''manualmente'' doble buffering. El Sr. Geary escribió sobre esto en su libro "HTML5 Canvas" .
Para reducir efectivamente el uso del parpadeo requestAnimationFrame()
!
Opera 9.10 es muy lento y muestra el proceso de dibujo. Si desea que un navegador no utilice doble almacenamiento en búfer, pruebe Opera 9.10 out.
Algunas personas sugirieron que los navegadores de alguna manera determinan cuándo termina el proceso de dibujo, pero ¿pueden explicar cómo puede funcionar? No he notado ningún parpadeo obvio en Firefox, Chrome o IE9, incluso cuando el dibujo es lento, así que parece que eso es lo que están haciendo, pero la forma en que se logra es un misterio para mí. ¿Cómo sabría el navegador que está actualizando la pantalla justo antes de que se ejecuten más instrucciones de dibujo? ¿Crees que simplemente lo cronometran, por lo que si transcurre un intervalo de más de 5 ms o más sin ejecutar una instrucción de dibujo de lienzo, se supone que puede intercambiar memorias intermedias de forma segura?
Para los incrédulos, aquí hay un código parpadeante. Tenga en cuenta que estoy aclarando explícitamente para borrar el círculo anterior.
http://coderextreme.net/basketball2.html ( http://jsfiddle.net/GzSWJ/ )
<!DOCTYPE html>
<html>
<head><title>Basketball</title></head>
<body>
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function draw_ball(ball) {
ctx.clearRect(0, 0, 400, 400);
ctx.fillStyle = "#FF0000";
ctx.beginPath();
ctx.arc(ball.x, ball.y, 30, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
}
var deltat = 1;
var ball = {};
ball.y = 0;
ball.x = 200;
ball.vy = 0;
ball.vx = 10;
ball.ay = 9.8;
ball.ax = 0.1;
function compute_position() {
if (ball.y > 370 && ball.vy > 0) {
ball.vy = -ball.vy * 84 / 86;
}
if (ball.x < 30) {
ball.vx = -ball.vx;
ball.ax = -ball.ax;
} else if (ball.x > 370) {
ball.vx = -ball.vx;
ball.ax = -ball.ax;
}
ball.ax = ball.ax / 2;
ball.vx = ball.vx * 185 / 186;
ball.y = ball.y + ball.vy * deltat + ball.ay * deltat * deltat / 2
ball.x = ball.x + ball.vx * deltat + ball.ax * deltat * deltat / 2
ball.vy = ball.vy + ball.ay * deltat
ball.vx = ball.vx + ball.ax * deltat
draw_ball(ball);
}
setInterval(compute_position, 40);
</script></body></html>
Siempre puedes hacer var canvas2 = document.createElement("canvas");
y no agregarlo al DOM en absoluto.
Solo digo que ustedes parecen obsesionados con la display:none;
me parece más limpio e imita la idea del doble buffer de forma más precisa que tener un lienzo torpemente invisible.
Un método muy simple es tener dos elementos de lienzo en la misma ubicación de pantalla y establecer la visibilidad para el búfer que necesita mostrar. Dibuja en el escondido y voltea cuando hayas terminado.
Cierto código:
CSS:
canvas { border: 2px solid #000; position:absolute; top:0;left:0;
visibility: hidden; }
Volteando en JS:
Buffers[1-DrawingBuffer].style.visibility=''hidden'';
Buffers[DrawingBuffer].style.visibility=''visible'';
DrawingBuffer=1-DrawingBuffer;
En este código, la matriz ''Buffers []'' contiene ambos objetos canvas. Entonces, cuando quiera comenzar a dibujar, aún necesita obtener el contexto:
var context = Buffers[DrawingBuffer].getContext(''2d'');
necesita 2 lienzos: (observe el css z-index y position: absolute)
<canvas id="layer1" width="760" height="600" style=" position:absolute; top:0;left:0;
visibility: visible; z-index: 0; solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<canvas id="layer2" width="760" height="600" style="position:absolute; top:0;left:0;
visibility: visible; z-index: 1; solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
puedes notar que el primer lienzo es visible y en el segundo está oculta la idea de dibujar en el lado oculto, luego de eso ocultaremos lo visible y haremos visible el lienzo oculto. cuando está oculto ''lienzo oculto claro
<script type="text/javascript">
var buff=new Array(2);
buff[0]=document.getElementById("layer1");
buff[1]=document.getElementById("layer2");
ctx[0]=buff[0].getContext("2d");
ctx[1]=buff[1].getContext("2d");
var current=0;
// draw the canvas (ctx[ current ]);
buff[1- current ].style.visibility=''hidden'';
buff[ current ].style.visibility=''visible'';
ctx[1-current].clearRect(0,0,760,600);
current =1-current;