¿HTML5 / Canvas Soporta Doble Buffering?


Lo que me gustaría hacer es dibujar mis gráficos en un búfer y luego ser capaz de copiarlos tal como están en el lienzo para que pueda hacer animación y evitar el parpadeo. Pero no pude encontrar esta opción. ¿Alguien sabe cómo puedo hacer esto?

Author: Jonas, 2010-05-08

12 answers

El siguiente enlace útil, además de mostrar ejemplos y ventajas de usar doble buffering, muestra varios otros consejos de rendimiento para usar el elemento canvas html5. Incluye enlaces a las pruebas jsPerf, que agregan los resultados de las pruebas a través de los navegadores en una base de datos Browserscope. Esto asegura que se verifican los consejos de rendimiento.

Http://www.html5rocks.com/en/tutorials/canvas/performance /

Para su conveniencia, he incluido un ejemplo mínimo de doble buffering efectivo 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);
 22
Author: ricksuggs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-03 04:03:51

Un método muy simple es tener dos elementos canvas en la misma ubicación de la pantalla y establecer la visibilidad para el búfer que necesita mostrar. Dibujar en el oculto y voltear cuando haya terminado.

Algún 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 el array 'Buffers[]' contiene ambos objetos canvas. Así que cuando quieras empezar a dibujar todavía necesitas obtener el contexto:

var context = Buffers[DrawingBuffer].getContext('2d');
 81
Author: Fedor van Eldijk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-25 21:55:09

Los navegadores que he probado manejan este almacenamiento en búfer por usted al no volver a pintar el lienzo hasta que el código que dibuja su marco se haya completado. Vea también la lista de correo WHATWG: http://www.mail-archive.com/[email protected]/msg19969.html

 18
Author: Edward Coffey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-28 02:54:57

Siempre podrías hacer var canvas2 = document.createElement("canvas"); y no añadirlo al DOM en absoluto.

Solo digo que ya que ustedes parecen tan obsesionados con display:none; simplemente me parece más limpio e imita la idea del doble buffering de manera más precisa que simplemente tener un lienzo torpemente invisible.

 10
Author: DeadlyBacon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-16 12:22:35

Más de dos años después:

No hay necesidad de implementar 'manualmente' el doble buffering. El Sr. Geary escribió sobre esto en su libro "HTML5 Canvas".

Para reducir eficazmente el uso de parpadeo requestAnimationFrame()!

 7
Author: ohager,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-09 16:46:50

Para los incrédulos, aquí hay un código parpadeante. Tenga en cuenta que estoy limpiando 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>
 6
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-25 10:55:58

Josh preguntó (hace un tiempo) sobre cómo el navegador sabe "cuándo termina el proceso de dibujo" para evitar el parpadeo. Yo habría comentado directamente a su post, pero mi reputación no es lo suficientemente alta. También esta es solo mi opinión. No tengo hechos que lo respalden, pero me siento bastante confiado al respecto y puede ser útil para otros que lean esto en el futuro.

Supongo que el navegador no "sabe" cuando has terminado de dibujar. Pero al igual que la mayoría de javascript, siempre y cuando su código se ejecute sin cediendo el control al navegador, el navegador está esencialmente bloqueado y no / no puede actualizar / responder a su interfaz de usuario. Supongo que si borra el lienzo y dibuja todo el marco sin ceder el control al navegador, en realidad no dibujará el lienzo hasta que haya terminado.

Si configura una situación en la que su procesamiento abarca varias llamadas setTimeout/setInterval/requestAnimationFrame, en la que borra el lienzo en una llamada y dibuja elementos en el lienzo en la siguiente varias llamadas, repitiendo el ciclo (por ejemplo) cada 5 llamadas, estaría dispuesto a apostar que verías parpadeo ya que el lienzo se actualizaría después de cada llamada.

Dicho esto, no estoy seguro de confiar en eso. Ya estamos en el punto de que javascript se compila hasta el código nativo de la máquina antes de la ejecución (al menos eso es lo que el motor V8 de Chrome hace por lo que entiendo). No me sorprendería si no pasara mucho tiempo antes de que los navegadores comenzaran a ejecutar su javascript en un hilo separado desde la interfaz de usuario y la sincronización de cualquier acceso a los elementos de la interfaz de usuario que permiten a la interfaz de usuario actualizar/responder durante la ejecución de javascript que no estaba accediendo a la interfaz de usuario. Cuando / si eso sucede (y entiendo que hay muchos obstáculos que habría que superar, como los controladores de eventos que se inician mientras todavía se está ejecutando otro código), probablemente veremos parpadeo en la animación de lienzo que no está utilizando algún tipo de doble buffering.

Personalmente, me encanta la idea de dos elementos de lienzo colocados encima de uno a otro y alternando que se muestra / dibujado en cada marco. Bastante poco intrusivo y probablemente bastante fácil de agregar a una aplicación existente con unas pocas líneas de código.

 6
Author: Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-25 03:39:32

No hay parpadeo en los navegadores web! Ya utilizan el búfer dbl para su renderizado. Js engine hará todo su renderizado antes de mostrarlo. Además, el contexto guarda y restaura solo los datos de la matriz transformacional de pila y tales, no el contenido de canvas en sí. Por lo tanto, usted no necesita o quiere dbl buffering!

 5
Author: Luka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-06 04:53:17

En lugar de rodar la suya, probablemente obtendrá el mejor kilometraje utilizando una biblioteca existente para crear animación JavaScript limpia y sin parpadeos:

Aquí hay uno popular: http://processingjs.org

 3
Author: a7drew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-12 20:39:08

Opera 9.10 es muy lenta y muestra el proceso de dibujo. Si quieres ver que un navegador no usa doble buffering, prueba Opera 9.10.

Algunas personas sugirieron que los navegadores están determinando de alguna manera cuándo termina el proceso de dibujo, pero ¿puede explicar cómo puede funcionar? No he notado ningún parpadeo obvio en Firefox, Chrome o IE9, incluso cuando el dibujo es lento, por lo que parece que eso es lo que están haciendo, pero cómo se logra es un misterio para mí. Cómo sería el navegador nunca ¿sabe que está actualizando la pantalla justo antes de que se ejecuten más instrucciones de dibujo? ¿Crees que solo lo cronometran para que si un intervalo de más de 5 ms o así pasa sin ejecutar una instrucción de dibujo de lienzo, asuma que puede intercambiar búferes de forma segura?

 2
Author: Josh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-23 04:25:31

Necesita 2 canvas: (observe el índice z de css y la posición: absoluta)

<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 el segundo está oculto la idea es dibujar sobre lo oculto después de eso ocultaremos lo visible y haremos visible el lienzo oculto. cuando está oculto ' clear hidden canvas

<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;
 2
Author: Aviad Gispan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-14 23:21:57

En la mayoría de las situaciones, no es necesario hacer esto, el navegador implementa esto para usted. Pero no siempre útil!

Todavía tiene que implementar esto cuando su dibujo es muy complicado. La mayor parte de la tasa de actualización de la pantalla es de aproximadamente 60 Hz, lo que significa que las actualizaciones de la pantalla por 16 ms. La tasa de actualización del navegador puede estar cerca de este número. Si su forma necesita 100 ms para completarse, verá una forma incompleta. Así que puede implementar doble buffering en esta situación.

He hecho un prueba: Clear a rect, wait for some time, then fill with some color. Si establezco el tiempo en 10ms, no veré parpadeo. Pero si lo pongo en 20ms, el parpadeo ocurre.

 1
Author: Chien-Wei Huang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-16 12:08:10