Optimiser les Canvas

L'élément <canvas> est l'un des standards les plus utilisés pour le rendu graphique 2D sur le web. Il est surtout utilisé dans les jeux et les visualisations complexes. Cependant, les sites et applications web poussent les canvas à leurs limites, et les performances commencent à en pâtir. Cet article propose des suggestions pour optimiser votre utilisation de l'élément canvas, et pour être certain que votre site ou application web fonctionne bien.

Conseils sur les performances

Ceci est une liste de conseils pour améliorer les performances

Pré-rendre les primitifs similaires ou répéter les objects dans un canvas hors-champ

Si vous avez besoin d'ajouter un dessin complexe identique à chaque image rendue, préférez l'utilisation d'un canvas hors-champ, le rendre une fois (ou à chaque fois qu'il change) sur ce canvas, puis dessinez-le sur le canvas principal à chaque image rendue.

js
myEntity.offscreenCanvas = document.createElement("canvas");
myEntity.offscreenCanvas.width = myEntity.width;
myEntity.offscreenCanvas.height = myEntity.height;
myEntity.offscreenContext = myEntity.offscreenCanvas.getContext("2d");

myEntity.render(myEntity.offscreenContext);

Abandonnez les coordonnées décimales et utilisez des entiers à la place

Un rendu de sous-pixel est opéré quand on dessine des objets sur un canvas sans valeur entière.

js
ctx.drawImage(myImage, 0.3, 0.5);

Cela pousse le navigateur à faire des calculs supplémentaires pour créer un effet d'anti-crénelage. Pour empêcher cela, il faut s'assurer d'arrondir les coordonnées utilisées pour drawImage().

Ne pas redimensionner d'images avec drawImage

Préférez mettre en cache plusieurs dimensions de votre image dans un canvas hors-champ au lieu de les redimensionner constamment avec drawImage().

Utiliser des canvas empilés pour les scènes complexes

Pour des scènes complexes, on peut souvent remarquer que quelques éléments changent souvent tandis que d'autres ne changent jamais. Une optimisation possible dans ce cas consiste à utiliser plusieurs calques sous forme de canvas empilés.

Par exemple, on peut créer un calque UI, dessiné au-dessus de tous les autres uniquement lorsque l'utilisateur accède à un menu. En dessous, un calque jeu où les entités du jeu sont souvent mises à jour. Et, à l'arrière, un calque de fond rarement modifié.

html
<div id="stage">
  <canvas id="ui-layer" width="480" height="320"></canvas>
  <canvas id="game-layer" width="480" height="320"></canvas>
  <canvas id="background-layer" width="480" height="320"></canvas>
</div>

<style>
  #stage {
    width: 480px;
    height: 320px;
    position: relative;
    border: 2px solid black;
  }
  canvas {
    position: absolute;
  }
  #ui-layer {
    z-index: 3;
  }
  #game-layer {
    z-index: 2;
  }
  #background-layer {
    z-index: 1;
  }
</style>

Du CSS pour les grandes images de fond

Si comme pour la plupart des jeux, vous utilisez une image de fond statique, préférez utiliser un simple <div> en dessous du canvas avec les propriétés CSS background appropriées. Cela vous évitera de redessiner une grande image dans le canvas à chaque tick.

Redimensionner les canvas avec CSS transform

Les transformations CSS sont plus rapides car elles utilisent le GPU. Le mieux est d'utiliser un canvas plus grand et de réduire sa taille. Pour Firefox OS, les dimensions sont de 480 x 320 px.

js
var scaleX = window.innerWidth / canvas.width;
var scaleY = window.innerHeight / canvas.height;

var scaleToFit = Math.min(scaleX, scaleY);
var scaleToCover = Math.max(scaleX, scaleY);

stage.style.transformOrigin = "0 0"; //scale from top left
stage.style.transform = "scale(" + scaleToFit + ")";

Utiliser l'attribut moz-opaque (Gecko only)

Si le canvas n'a pas besoin de transparence, ajouter l'attribut moz-opaque dans la balise canvas. Cette information peut être utilisée par le navigateur pour optimiser le rendu.

html
<canvas id="mycanvas" moz-opaque></canvas>

D'autres conseils

  • Regrouper les appels canevas (par exemple, dessiner un chemin de plusieurs lignes plutôt que plusieurs lignes séparées).
  • Éviter de refaire un rendu si ce n'est pas nécessaire.
  • Rendre uniquement les différences, pas tout le canvas.
  • Éviter la propriété shadowBlur (en-US) quand c'est possible.
  • Empêcher le rendu de texte quand c'est possible.
  • Essayer différents moyens d'effacer le canvas : (clearRect() vs. fillRect() vs. redimensionner le canevas).
  • Avec les animations, utiliser window.requestAnimationFrame() plutôt que window.setInterval() (en-US).
  • Faire attention aux bibliothèques physiques lourdes.
  • Tester les performances avec JSPerf.

Voir aussi