w3schools examples ejemplos code javascript performance optimization canvas

javascript - examples - html code



Sugerencias de optimización y rendimiento de canvas de HTML5, mejores prácticas de codificación y trucos (4)

Redibujar regiones

La mejor técnica de optimización de lienzo para animaciones es limitar la cantidad de píxeles que se borran / pintan en cada cuadro. La solución más fácil de implementar es restablecer todo el elemento del lienzo y volver a dibujar todo, pero es una operación costosa que debe procesar su navegador.

Reutilice tantos píxeles como sea posible entre fotogramas. Lo que eso significa es que cuantos menos píxeles se deben procesar en cada cuadro, más rápido se ejecutará el programa. Por ejemplo, al borrar píxeles con el método clearRect (x, y, w, h), es muy beneficioso borrar y volver a dibujar solo los píxeles que han cambiado y no todo el lienzo.

Sprites procedurales

Generar gráficos procesalmente suele ser el camino a seguir, pero a veces no es el más eficiente. Si dibuja formas simples con rellenos sólidos, entonces dibujarlos de forma procedimental es la mejor forma de hacerlo. Pero si está dibujando entidades más detalladas con trazos, rellenos de degradado y otro maquillaje sensible al rendimiento, sería mejor usar sprites de imagen.

Es posible escapar con una combinación de ambos. Dibuje las entidades gráficas proceduralmente en el lienzo una vez cuando se inicia su aplicación. Después de eso, puede reutilizar los mismos sprites pintando copias de ellos en lugar de generar la misma sombra paralela, gradiente y trazos repetidamente.

Pila de estado y transformación

El lienzo se puede manipular a través de transformaciones como la rotación y la escala, lo que da como resultado un cambio en el sistema de coordenadas del lienzo. Aquí es donde es importante conocer la pila de estado para la cual hay dos métodos disponibles: context.save () (empuja el estado actual a la pila) y context.restore () (revierte al estado anterior). Esto le permite aplicar la transformación a un dibujo y luego restaurar al estado anterior para asegurarse de que la siguiente forma no se vea afectada por ninguna transformación anterior. Los estados también incluyen propiedades tales como los colores de relleno y trazo.

Compositing

Una herramienta muy poderosa a la mano cuando se trabaja con lienzo es la composición de modos que, entre otras cosas, permiten el enmascaramiento y la estratificación. Hay una amplia gama de modos compuestos disponibles y todos se configuran a través de la propiedad globalCompositeOperation del contexto canvas. Los modos compuestos también forman parte de las propiedades de pila de estado, por lo que puede aplicar una operación compuesta, apilar el estado y aplicar uno diferente, y restaurar de nuevo al estado anterior al que creó el primero. Esto puede ser especialmente útil.

Anti-Aliasing

Para permitir dibujos subpíxel, todas las implementaciones de canvas de navegador emplean anti-aliasing (aunque esto no parece ser un requisito en la especificación HTML5). El antialiasing puede ser importante para tener en cuenta si desea dibujar líneas nítidas y observar que el resultado se ve borroso. Esto ocurre porque el navegador interpolará la imagen como si realmente estuviera entre esos píxeles. Resulta en una animación mucho más suave (puede moverse genuinamente a la mitad de un píxel por actualización) pero hará que sus imágenes se vean borrosas.

Para solucionar este problema, deberá redondear a valores enteros enteros o compensar en medio píxel, dependiendo de si está dibujando rellenos o trazos.

Usar números enteros para drawImage () posiciones xey

Si llama a drawImage en el elemento Canvas, es mucho más rápido si redondea la posición xey a un número entero.

Aquí hay un caso de prueba en jsperf que muestra cuánto más rápido se usa números enteros en comparación con el uso de decimales.

Así que redondee su posición xey a números enteros antes de renderizar.

Más rápido que Math.round ()

Otra prueba jsperf muestra que Math.round () no es necesariamente el método más rápido para redondear números. Usar un truco bit a bit realmente resulta ser más rápido que el método incorporado.

Optimización de Canvas Sprite

Despejando el lienzo

Para borrar todo el lienzo de cualquier pixel existente, se usa normalmente el contexto .clearRect (x, y, w, h), pero hay otra opción disponible. Siempre que se establece el ancho / alto del lienzo, incluso si se establecen en el mismo valor repetidamente, el lienzo se restablece. Es bueno saberlo cuando trabaje con un lienzo de tamaño dinámico, ya que notará que los dibujos desaparecen.

Distribución de Computación

El generador de perfiles de Chrome Developer Tools es muy útil para descubrir cuáles son los cuellos de botella de su rendimiento. Dependiendo de su aplicación, es posible que necesite refactorizar algunas partes de su programa para mejorar el rendimiento y cómo los navegadores manejan partes específicas de su código.

Técnicas de optimización

¿CONOCES ALGUNAS MÁS MEJORES PRÁCTICAS PARA LA CANVAS?

Agregue a este hilo lo que sabe, ha aprendido o ha leído en línea sobre todas las mejores prácticas de Canvas, consejos / trucos para el rendimiento

Dado que Canvas sigue siendo muy nuevo en Internet, y no hay señales de que envejezca en el futuro, no hay demasiadas "mejores prácticas" documentadas ni otros consejos realmente importantes que se deben "conocer" para desarrollar con en cualquier lugar en particular. Cosas como esta están dispersas y muchas veces en sitios menos conocidos.

Hay muchas cosas que la gente necesita saber y aún hay mucho de lo que aprender.

Quería compartir algunas cosas para ayudar a las personas que están aprendiendo Canvas y tal vez a algunos que ya lo conocen bastante bien y espero obtener algunos comentarios de otros sobre lo que consideran son algunas de las mejores prácticas u otros trucos y consejos para trabajar con Canvas en HTML5 .

Quiero comenzar con uno que, personalmente, considero que es bastante útil pero sorprendentemente poco común para los desarrolladores.

1. Sangra tu código

Tal como lo haría en cualquier otro momento, en cualquier otro idioma, cualquiera que sea el caso. Ha sido una buena práctica para todo lo demás, y he descubierto que en una aplicación de lienzo compleja, las cosas pueden ser un poco confusas cuando se trata de varios contextos diferentes y estados guardados / restaurados. Sin mencionar que el código es simplemente más legible y, en general, más limpio.

Por ejemplo:

... // Try to tell me this doesn''t make sense to do ctx.fillStyle = ''red''; ctx.fill(); ctx.save(); if (thing < 3) { // indenting ctx.beginPath(); ctx.arc(2, 6, 11, 0, Math.PI*2, true); ctx.closePath(); ctx.beginPath(); ctx.moveTo(20, 40); ctx.lineTo(10, 200); ctx.moveTo(20, 40); ctx.lineTo(100, 40); ctx.closePath(); ctx.save(); ctx.fillStyle = ''blue'' ctx.fill(); ctx.restore(); } else { // no indenting ctx.drawImage(img, 0, 0, 200, 200); ctx.save(); ctx.shadowBlur(); ctx.beginPath(); ctx.arc(2, 60, 10, 0, Math.PI*2, false); ctx.closePath(); ctx.fillStyle ''green''; ctx.fill(); ctx.restore(); } ctx.restore(); ctx.drawRect(); ctx.fill(); ...

¿No es más fácil y más fácil leer la declaración IF y saber qué está sucediendo inmediatamente que la declaración ELSE en esto? ¿Puedes ver lo que estoy diciendo aquí? Creo que este debería ser un método que los desarrolladores deberían seguir practicando de la misma manera que lo harían cuando escriben simplemente javascript o cualquier otro lenguaje.

Utilice requestAnimationFrame en lugar de setInterval / setTimeout

setInterval y setTimeout nunca se usaron como temporizadores de animación, solo son métodos genéricos para llamar funciones después de un retraso de tiempo. Si configura un intervalo de 20 ms en el futuro, pero su cola de funciones tarda más que eso en ejecutarse, su temporizador no se disparará hasta que se hayan completado estas funciones. Eso podría ser un tiempo, lo cual no es ideal en lo que a animación se refiere. RequestAnimationFrame es un método que le dice al navegador que está teniendo lugar una animación, por lo que puede optimizar los repintados en consecuencia. También acelera la animación para las pestañas inactivas, por lo que no matará la batería de tu dispositivo móvil si la dejas abierta en segundo plano.

Nicholas Zakas escribió un artículo enormemente detallado e informativo sobre requestAnimationFrame en su blog, que bien vale la pena leer. Si quieres algunas instrucciones de implementación rápidas y duras, entonces Paul Irish ha escrito una solicitud de ajuste de AnnimationFrame : esto es lo que he usado en cada una de las aplicaciones de Canvas que he hecho hasta hace poco.

ACTUALMENTE

Incluso mejor que utilizar requestAnimationFrame en lugar de setTimeout y setInterval, Joe Lambert ha escrito un shim nuevo y mejorado llamado requestInterval y requestTimeout, que explica qué problemas existen cuando se utiliza requestAnimFrame. Puedes ver la esencia del guion .

ACTUALMENTE x2

Ahora que todos los navegadores han alcanzado la especificación para esto, ha habido una actualización del requestAnimFrame () polyfill , uno que probablemente seguirá siendo el que usará para cubrir todos los proveedores.

Usa más de un lienzo

Una técnica para juegos pesados ​​de animación sobre la que @nicolahibbert escribió en su publicación sobre la optimización de los juegos de Canvas menciona que puede ser mejor usar varios lienzos superpuestos unos encima de otros en lugar de hacer todo en un solo lienzo. Nicola explica que "dibujar demasiados píxeles en el mismo lienzo al mismo tiempo hará que la velocidad de fotogramas caiga por el suelo. Por ejemplo, tome Breakout. Trate de dibujar los ladrillos, la pelota, la paleta, cualquier potenciador o armamento. , y luego cada estrella en el fondo - esto simplemente no funcionará, lleva demasiado tiempo ejecutar cada una de estas instrucciones a la vez. Al dividir el campo de estrellas y el resto del juego en lienzos separados, puede garantizar un nivel decente cuadros por segundo."

Renderizar elementos fuera de la pantalla

He tenido que hacer esto para algunas aplicaciones que he hecho, incluida la aplicación de Facebook del Proyecto Genoma Olímpico de Samsung . Es extremadamente útil saber y usar si es necesario o no. Disminuye enormemente el tiempo de carga, además de que puede ser una técnica realmente útil para cargar imágenes fuera de la pantalla, ya que a veces pueden tomar un tiempo.

var tmpCanvas = document.createElement(''canvas''), tmpCtx = tmpCanvas.getContext(''2d''), img = document.createElement(''img''); img.onload = function() { tmpCtx.drawImage(thumbImg, 0, 0, 200, 200); }; img.src = ''/some/image/source.png'';

Tenga en cuenta que el src de la imagen se establece después de que se carga. Esto es una cosa clave para recordar hacer también. Una vez que las imágenes terminen de cargarse y dibujarse en estos lienzos temporales, puede dibujarlos en su lienzo principal utilizando el mismo ctx.drawImage (), pero en lugar de poner la imagen como primer argumento, use ''tmpCtx.canvas'' para hacer referencia al lienzo de la temperatura.

Otros consejos, trucos y recursos

Canvas tiene una retrospectiva

El segundo contexto tiene una referencia posterior a su elemento DOM asociado:

var ctx = doc.getElementById(''canvas'').getContext(''2d''); console.log(ctx.canvas); // HTMLCanvasElement

Me encantaría escuchar más de otras personas sobre esto. Estoy trabajando en hacer una lista de cosas que deberíamos estandarizar para agregar una nueva sección a los estándares de código de front-end y mejores prácticas de mi empresa. Me encantaría recibir tantos comentarios sobre esto como pueda.


Aquí están mis consejos

1) Use clearRect para borrar el lienzo en lugar de canvas.width = canvas.width, porque luego restablece los estados del lienzo

2) Si está utilizando eventos de mouse en el lienzo, use la siguiente función, es confiable y funciona en la mayoría de los casos.

/** returns the xy point where the mouse event was occured. @param ev The event object. */ function getXY(ev){ return getMousePosition(ev, ev.srcElement || ev.originalTarget); } /** returns the top-left point of the element @param elem The element */ function getElementPos(elem){ var obj = elem; var top = 0; var left = 0; while (obj && obj.tagName != "BODY") { top += obj.offsetTop-obj.scrollTop; left += obj.offsetLeft -obj.scrollLeft ; obj = obj.offsetParent; } return { top: top, left: left }; }; /** returns the xy point where the mouse event was occured inside an element. @param ev The event object. @param elem The element */ function getMousePosition(evt, elem){ var pageX, pageY; if(typeof(window.pageYOffset)==''number'') { pageX=window.pageXOffset; pageY=window.pageYOffset; }else{ pageX=document.documentElement.scrollLeft; pageY=document.documentElement.scrollTop; } var mouseX = evt.clientX - getElementPos(elem).left + pageX; var mouseY = evt.clientY - getElementPos(elem).top + pageY; return { x: mouseX, y: mouseY }; };

3) Utiliza ExplorerCanvas si quieres admitir IE7

4) En lugar de despejar todo el lienzo, limpie solo la parte que se necesita limpiar. Es bueno para el rendimiento.


Aquí hay algunos consejos y sugerencias que incluí en una lista que valía la pena compartir.

  • No incluya jQuery a menos que necesite hacer algo más que simplemente seleccionar <canvas> .

    Me las he arreglado para arreglármelas sin casi todo lo que hice en lienzo

  • Cree funciones abstractas y desacople su código . Funcionalidad separada de apariencia o estado de dibujo inicial.

    Haga que las funciones comunes sean reutilizables tanto como sea posible. Idealmente, debe usar un patrón de módulo, que puede crear un objeto utils que contenga funciones comunes.

  • Utilice nombres de variables de letras simples y dobles cuando tenga sentido ( x, y, z ).

    El sistema de coordenadas en Canvas agrega más letras individuales que comúnmente se declaran como variables. Lo cual puede llevar a crear múltiples variables simples / dobles ( dX, dY, aX, aY, vX, vY ) como parte de un elemento.

    Te sugiero que escribas o abbr. la palabra ( dirX, accelX, velX ) o ser descriptivo, de lo contrario las cosas podrían ser bastante confusas para usted más tarde, confía en mí.

  • Crea funciones de constructor que se pueden invocar según sea necesario para hacer elementos del juego. Puede agregar métodos y propiedades personalizadas dentro del constructor y crear cualquier número que pueda necesitar y todos ellos tendrán sus propias propiedades y métodos.

    Ejemplo de una función constructor Ball que hice:

    // Ball constructor var Ball = function(x, y) { this.x = x; this.y = y; this.radius = 10; this.color = ''#fff''; // Direction and min, max x,y this.dX = 15; this.dY = -15; this.minX = this.minY = 20 + this.radius; this.maxX = this.radius - (canvasWidth - 20); this.maxY = this.radius + canvasHeight; this.draw = function(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, twoPI, true); ctx.closePath(); ctx.save(); ctx.fillStyle = this.color; ctx.fill(); ctx.restore(); }; };

Creando la pelota

ball = new Ball(centerX, canvasHeight - paddle.height - 30); ball.draw(ctx);

  • Una buena base para trabajar es crear 3 funciones: init () - hacer todo el trabajo inicial, y configurar los vars base y los manejadores de eventos, etc ... draw () - llamado una vez para comenzar el juego y dibuja el primer marco de el juego, incluida la creación de elementos que pueden estar cambiando o que necesitan construcción. update () - llamado al final de draw () y dentro de sí mismo a través de requestAnimFrame. Actualiza las propiedades de los elementos cambiantes, solo haz lo que necesites hacer aquí.

  • Haga la menor cantidad de trabajo dentro del ciclo, realizando actualizaciones de las partes o elementos cambiantes . Crea los elementos del juego para que funcione cualquier otra interfaz de usuario fuera del ciclo de animación.

    El bucle de animación a menudo es una función recursiva, lo que significa que se llama de manera rápida y repetida durante la animación para dibujar cada fotograma.

    Si hay muchos elementos que se animan a la vez, es posible que desee crear primero los elementos utilizando una función constructora si aún no lo ha hecho, y luego dentro del constructor crear un método ''temporizador'' que soliciteAnimFrame / setTimeout utilizándolo de la forma en que normalmente dentro de cualquier bucle de animación, pero afecta solo a este elemento.

    Puedes hacer que cada elemento del juego tenga su propio temporizador, dibujar y animar métodos en el constructor.

    Hacer esto le da una separación completa de control para cada elemento y un gran ciclo de animación no será necesario en absoluto ya que el bucle se divide en cada elemento y usted comienza / se detiene a voluntad.

U otra opción:

  • Cree una función de constructor Timer () que puede usar y dar a cada elemento de animación individualmente, minimizando así la carga de trabajo dentro de los bucles de animación

Después de haber trabajado en una aplicación de Facebook recientemente lanzada que usa Canvas y usuarios, la información de perfil de Facebook (la cantidad de datos que debe acomodar es masiva para algunos) para que usted y sus amigos también usen la aplicación, a los atletas olímpicos como un 6 grados de tipo de separación, he aprendido mucho en mis extensos esfuerzos para hacer todo lo posible por aumentar el rendimiento dentro de la aplicación.

Literalmente pasé meses y días trabajando simplemente para volver a factorizar el código que ya conocía tan bien y creí que era la forma más óptima de hacer las cosas.

Use elementos DOM siempre que sea posible

El hecho es que los navegadores aún no están listos para manejar aplicaciones en ejecución más intensivas en Canvas, especialmente si se requiere que desarrolles la aplicación con soporte para IE 8. En ocasiones, el DOM es más rápido que la implementación actual del Canvas API en el momento de escribir esto. Al menos lo he encontrado mientras trabajaba en una página única enormemente compleja que animaba aplicaciones html5 y canvas para Samsung.

Pudimos hacer bastante bien para mejorar el rendimiento de las cosas al tiempo que usamos Canvas para hacer un trabajo complejo para recortar imágenes en círculos, lo que probablemente habría sido correcto para seguir con nuestra forma de hacerlo.

Días antes del lanzamiento, decidimos probar una técnica diferente, y en lugar de crear lienzos temporales fuera de la pantalla que se colocaron en el lienzo visible una vez recortados en círculos, etc., añadimos los elementos del DOM de la imagen en el lienzo, usando la x y coordenadas que hemos estado utilizando para colocar los lienzos temporales antes.

Para recortar las imágenes en círculos, bueno, eso fue simple, solo usamos la propiedad CSS3 border-radius para hacerlo, que fue mucho menos trabajo que la compleja serie de cambios de estado y, a la vez, ingeniosa y creativa, pero con un uso excesivo del .clip ( ) método.

Una vez que se colocan en el DOM, se produce la animación de las imágenes y los nodos DOM para cada imagen se animan como entidades separadas del lienzo. Unos que podemos tener un control total sobre el estilo fácilmente a través de CSS.

Esta técnica es similar a otro método para hacer este tipo de trabajo que también es bastante bueno saber, lo que implica superponer Lienzos uno encima del otro, en lugar de dibujarlos en un contexto.