2012-06-22 24 views
5

Empecé a jugar con el lienzo de HTML5 y esperaba hacer un par de juegos con él. Sin embargo, tan pronto empecé haciendo las coordenadas del ratón a la misma, se muele a un alto cerca de:Redibujar el lienzo de HTML5 increíblemente lento

http://jsfiddle.net/mnpenner/zHpgV/

Todo lo que hice fue hacer 38 líneas y un poco de texto, debe ser capaz de manejar eso, no?

¿Estoy haciendo algo mal? Me gustaría poder renderizar al menos 30 FPS, pero para algo como esto esperaría que sea capaz de dibujar miles de veces.

¿O acabo de utilizar la herramienta incorrecta para el trabajo? ¿WebGL está preparado para la tarea? ¿Por qué sería uno mucho más lento que el otro?

String.prototype.format = function() { 
 
    var args = arguments; 
 
    return this.replace(/\{(\d+)\}/g, function(m, n) { 
 
     return args[n]; 
 
    }); 
 
}; 
 
var $canvas = $('#canvas'); 
 
var c = $canvas[0].getContext('2d'); 
 
var scale = 20; 
 
var xMult = $canvas.width()/scale; 
 
var yMult = $canvas.height()/scale; 
 
var mouseX = 0; 
 
var mouseY = 0; 
 
c.scale(xMult, yMult); 
 
c.lineWidth = 1/scale; 
 
c.font = '1pt Calibri'; 
 

 
function render() { 
 
    c.fillStyle = '#dcb25c'; 
 
    c.fillRect(0, 0, scale, scale); 
 
    c.fillStyle = '#544423'; 
 
    c.lineCap = 'square'; 
 
    for (var i = 0; i <= 19; ++i) { 
 
     var j = 0.5 + i; 
 
     c.moveTo(j, 0.5); 
 
     c.lineTo(j, 19.5); 
 
     c.stroke(); 
 
     c.moveTo(0.5, j); 
 
     c.lineTo(19.5, j); 
 
     c.stroke(); 
 
    } 
 
    c.fillStyle = '#ffffff'; 
 
    c.fillText('{0}, {1}'.format(mouseX, mouseY), 0.5, 1.5); 
 
} 
 
render(); 
 
$canvas.mousemove(function(e) { 
 
    mouseX = e.clientX; 
 
    mouseY = e.clientY; 
 
    render(); 
 
});
<canvas id="canvas" width="570" height="570"></canvas>

Respuesta

7

Aquí está el código mejorado.

http://jsfiddle.net/zHpgV/3/

He aquí un desglose de las cosas que se deben tener en cuenta que he cambiado:

  • continua añadiendo a un camino en lugar de detenerse y la creación de un nuevo camino con beginPath. Este es de lejos el mayor asesino de rendimiento aquí. Estás terminando con un camino con miles y miles de segements de línea que nunca se borran.
  • Continuamente haciendo la misma ruta una y otra vez cuando solo tiene que hacerse una vez, durante la inicialización. Es decir, lo único que debe llamar al interior de render es stroke. No es necesario que vuelva a llamar al lineTo/moveTo, y ciertamente no de forma continua. Ver nota 1
  • Acariciando dos veces por una ruta
  • Acariciando dentro de un bucle for
  • Nuevo trazado de un fondo en lugar de establecer fondo CSS
  • Ajuste de la tapa de la línea una y otra

Nota 1 : Si planea tener más de una ruta en su aplicación, probablemente debería almacenar en caché rutas como esta ya que nunca cambian. Tengo un tutorial sobre cómo hacer eso here.

Por supuesto, si está haciendo todo esto para simplemente hacer un fondo, debe guardarse como un png y debe usar una imagen de fondo CSS.

así: http://jsfiddle.net/zHpgV/4/

Entonces, de repente su rutina renderizado es más bien pequeño:

function render() { 
    c.clearRect(0, 0, scale, scale); 
    c.fillText('{0}, {1}'.format(mouseX, mouseY), 0.5, 1.5); 
} 
+0

¡No sabía que los caminos funcionaban así! Creo que tener un objeto 'path' hubiera sido más intuitivo. Ahora tiene sentido por qué era tan lento, ¡gracias! – mpen

+3

Ahora hay un objeto de ruta en la especificación HTML5 Canvas y podrá hacer una ruta y llamar 'drawPath' en el futuro. Pero ningún navegador lo ha implementado todavía y pueden pasar meses antes de que puedas utilizarlo. ¡Comprar algún día! –

7

Usted no tiene que sacar toda la red en cada fotograma de la animación. Póngalo en otro lienzo subyacente (es común llamarlos "capas", pero son solo elementos de lienzo separados), por lo que solo podrá volver a dibujar las coordenadas.

<div id="canv"> 
<canvas id="bgLayer" width="500" height="500" style="z-index: 0"></canvas> 
<canvas id="fgLayer" width="500" height="500" style="z-index: 1"></canvas> 
</div> 

Aquí es the example He estado jugando con la lona en capas. La tabla dibujada en el lienzo inferior, las bolas se dibujan en el lienzo superior. Es solo un campo de juegos, por lo que hay mucho que arreglar y optimizar, por ejemplo, dibujar cada bola una sola vez en otro lienzo oculto y usar getImageData/putImageData para mejorar el rendimiento. También se recomienda utilizar requestAnimationFrame para actualizar el lienzo. Su ejemplo se basa en cada movimiento del mouse en su lugar, esto es mucho más a menudo necesario (cuando el mouse se mueve, por supuesto).

Hay una buena article para mejorar el rendimiento del lienzo. Además, hay un gran SO post sobre este tema.

+0

Me tomó un tiempo para averiguar que significa literalmente la capa de los elementos de lona. Pensé que las "capas" eran un concepto dentro del contexto del lienzo. Es una buena idea. ¡Gracias por los consejos! – mpen

+0

Lo siento, lo cambié para evitar confundir a alguien más. –

+1

Esos son buenos comentarios, pero hay otro problema que no encuentro en este caso. Dibujo cosas mucho más complejas con animaciones muy rápidas sin siquiera molestarme por el doble almacenamiento en memoria intermedia. –

9

Como dije en los comentarios, me sorprendió la lentitud de este código ya que dibujo cosas mucho más complejas con animaciones muy rápidas sin siquiera molestarme por el doble almacenamiento en memoria intermedia.

Así que miré un poco más y encontré un error como se esperaba.

El problema principal es la acumulación de la ruta de dibujo.

Agregue c.beginPath(); cada vez que dibuja una ruta.

Aquí hay un fast rendering of the same thing, para demostrar que ahora vuela.

El dibujo de la lona es rápido y se puede utilizar para animaciones.