Lo que me gustaría hacer es dibujar mis gráficos en un búfer y luego poder copiarlos como están en el lienzo para poder hacer la animación y evitar el parpadeo. Pero no pude encontrar esta opción. ¿Alguien sabe cómo puedo hacer esto?
javascript
html
canvas
double-buffering
Interfaz de usuario de Shai
fuente
fuente
explorercanvas
, pero eso, por supuesto, no es HTML5 y es uncanvas
elemento simplemente emulado por VML. Sin embargo, nunca he visto a ningún otro navegador hacerlo.Respuestas:
El siguiente enlace útil, además de mostrar ejemplos y ventajas del uso de búfer doble, 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 de Browserscope. Esto asegura que se verifiquen las sugerencias de rendimiento.
http://www.html5rocks.com/en/tutorials/canvas/performance/
Para su conveniencia, he incluido un ejemplo mínimo de almacenamiento en búfer doble 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);
fuente
Un método muy simple es tener dos elementos de lienzo en la misma ubicación de la pantalla y establecer la visibilidad para el búfer que necesita mostrar. Dibuja lo oculto y voltea cuando hayas 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, 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');
fuente
<noscript>
y crear mis elementos de lienzo en Javascript. Por lo general, querrá verificar la compatibilidad con el lienzo en Javascript de todos modos, entonces, ¿por qué querría poner un mensaje de respaldo del lienzo en su HTML?Todos 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. Consulte también la lista de correo de WHATWG: http://www.mail-archive.com/[email protected]/msg19969.html
fuente
Siempre puede hacerlo
var canvas2 = document.createElement("canvas");
y no agregarlo al DOM en absoluto.Solo digo que, dado que ustedes parecen tan obsesionados con
display:none;
él, me parece más limpio e imita la idea de doble búfer de manera más precisa que solo tener un lienzo extrañamente invisible.fuente
Más de dos años después:
No es necesario implementar "manualmente" el almacenamiento en búfer doble. El Sr. Geary escribió sobre esto en su libro "HTML5 Canvas" .
¡Para reducir eficazmente el uso del parpadeo
requestAnimationFrame()
!fuente
Josh preguntó (hace un tiempo) cómo sabe el navegador "cuándo termina el proceso de dibujo" para evitar el parpadeo. Habría comentado directamente en su publicación, pero mi reputación no es lo suficientemente alta. Además, esta es solo mi opinión. No tengo datos que lo respalden, pero me siento bastante seguro y puede ser útil para otras personas que lean esto en el futuro.
Supongo que el navegador no "sabe" cuando terminas de dibujar. Pero al igual que la mayoría de los javascript, siempre que su código se ejecute sin ceder el control al navegador, el navegador está esencialmente bloqueado y no podrá / no podrá 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á su lienzo hasta que haya terminado.
Si configura una situación en la que su representación abarca múltiples llamadas setTimeout / setInterval / requestAnimationFrame, donde borra el lienzo en una llamada y dibuja elementos en su lienzo en las siguientes llamadas, repitiendo el ciclo (por ejemplo) cada 5 llamadas, yo Estaría dispuesto a apostar a que verá un parpadeo, ya que el lienzo se actualizará después de cada llamada.
Dicho esto, no estoy seguro de que pueda confiar en eso. Ya estamos en el punto en 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, según tengo entendido). No me sorprendería si no pasara mucho tiempo antes de que los navegadores comenzaran a ejecutar su javascript en un hilo separado de la interfaz de usuario y sincronizaran cualquier acceso a los elementos de la interfaz de usuario, lo que permite que la interfaz de usuario se actualice / responda durante la ejecución de javascript que no estaba accediendo a la interfaz de usuario. Cuando o si eso sucede (y entiendo que hay muchos obstáculos que tendrían que superarse, como que los controladores de eventos se inicien mientras todavía está ejecutando otro código), probablemente veremos parpadeo en la animación del lienzo que no está usando una especie de doble búfer.
Personalmente, me encanta la idea de dos elementos de lienzo colocados uno encima del otro y alternando que se muestran / dibujan en cada marco. Bastante poco intrusivo y probablemente se puede agregar fácilmente a una aplicación existente con unas pocas líneas de código.
fuente
Para los incrédulos, aquí hay un código parpadeante. Tenga en cuenta que estoy despejando explícitamente para borrar el círculo anterior.
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);
<!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> </body></html>
fuente
¡No hay parpadeo en los navegadores web! Ya utilizan el almacenamiento en búfer dbl para su renderizado. El motor Js hará todo su renderizado antes de mostrarlo. Además, guardar y restaurar el contexto solo apila datos de matriz de transformación y demás, no el contenido del lienzo en sí. Por lo tanto, no necesita ni desea el almacenamiento en búfer dbl.
fuente
En lugar de rodar el suyo, probablemente obtendrá el mejor rendimiento utilizando una biblioteca existente para crear animaciones JavaScript limpias y sin parpadeos:
Aquí hay uno popular: http://processingjs.org
fuente
necesita 2 lienzos: (observe el índice z 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>
puede notar que el primer lienzo es visible y el segundo está oculto, la idea es dibujar sobre el oculto, luego ocultaremos el visible y haremos visible el lienzo oculto. cuando está oculto, claro lienzo oculto
<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;
fuente
Opera 9.10 es muy lento y muestra el proceso de dibujo. Si desea que un navegador no utilice doble búfer, pruebe Opera 9.10.
Algunas personas sugirieron que los navegadores determinan 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 para mí es un misterio cómo se logra. ¿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, de modo que si pasa un intervalo de más de 5 ms sin ejecutar una instrucción de dibujo de lienzo, se supone que puede intercambiar búferes de manera segura?
fuente
En la mayoría de las situaciones, no necesita hacer esto, el navegador lo implementa por usted. ¡Pero no siempre es útil!
Aún debe implementar esto cuando su dibujo sea muy complicado. La mayor parte de la frecuencia de actualización de la pantalla es de aproximadamente 60 Hz, lo que significa que la pantalla se actualiza cada 16 ms. La tasa de actualización del navegador puede acercarse a este número. Si su forma necesita 100 ms para completarse, verá una forma incompleta. Entonces puede implementar el doble búfer 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é parpadeo. Pero si lo configuro en 20 ms, el parpadeo ocurre.fuente