Effacer un canevas, utiliser un trick ou pas ?

Je constate en lisant des articles ou des réponses sur les forums de questions que le flou subsiste sur la meilleure façon d’effacer un canevas.

Il existe en fait deux méthodes pour effacer complètement un canevas:

  1. context.clearRect(x,y,width, height): c’est la méthode “officielle” pour effacer tout ou partie d’un canevas
  2. canvas.width = canvas.width : le fameux trick qui efface complètement le canevas.

Quand on cherche sur le net, on trouve (comme souvent) tout et son contraire. Par exemple sur StackOverflow… Et puis il faut dire que si ce hack a la vie dure, c’est aussi parce qu’on le trouve sur des sites de référence, comme celui du WHATWG (à une époque en tous cas) ou bien sur w3schools. Je vous propose de faire un petit point sur ce problème afin de vous permettre de faire un choix en toute connaissance de cause.

Comparaison des deux méthodes

Le hack joue sur le fait que lorsque l’on redimensionne un canevas, le contexte est complètement réinitialisé. Cela veut dire que l’état courant du canevas est perdu. Pour rappel cet état contient les informations suivantes:

  • La matrice de transformation
  • La fenêtre de travail
  • Toutes les propriétés de style

Par contre, la méthode clearRect(), elle, ne modifie pas du tout l’état. Cela veut dire que l’on devra, si l’on a appliqué une transformation, réinitialiser la transformation courante à l’identité avant d’appliquer le clearRect(),faute de quoi on risque de ne pas effacer ce que l’on voulait:

context.save();
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, 512, 512);
context.restore();

Cela me suggère une première réflexion à propos de la pérennité du hack. Que se passera-t-il si un jour la spécification dit que finalement le changement de dimension n’a plus cette conséquence sur le contexte ? On aurait déjà vu des choses plus étonnantes dans le processus de standardisation de HTML5…

Mais au bout d’un moment, qu’est-ce-qu’on s’en fout ?

Oui je comprends que vous puissiez vous dire que, mis à part le cataclysme que je viens d’évoquer, tout ça n’a aucune importance. Et franchement, en dehors d’une utilisation intensive de l’effacement (pour des animations par exemple), pourquoi pas ? Si vous devez effectuer l’opération toutes les n secondes ou minutes, il est en effet inutile de se préoccuper de savoir si l’effacement du canevas va prendre 1ms ou bien 10.

Par contre, quand on s’attaque au domaine de l’animation, chaque milliseconde compte et il va falloir utiliser les méthodes optimales. Comment s’y prendre dans ce cas ?

La seule règle valable quand on développe en JavaScript, dans un environnement qui évolue constamment, c’est de cibler les navigateurs et de tester les performances sur la cible déterminée. Et puis il faut essayer d’anticiper dans la mesure du possible. Dans le cas qui nous intéresse, on a d’un côté une méthode détournée qui a peu de chance d’être beaucoup optimisée au fil du temps et de l’autre une méthode qui, parce qu’elle est quand même là pour çà, risque d’intéresser beaucoup plus les implémenteurs. Ma conclusion est que j’ai envie de miser sur la seconde, sauf si j’ai vraiment de bonnes raisons d’utiliser le hack.

Ma réalité m’a rattrapé

Pour vous décider, vous pouvez essayer avec ce cas de test sur jsPerf, qui existait déjà et auquel j’ai ajouté un test pour le cas plus compliqué où l’on souhaite sauvegarder l’état avant de faire le clearRect(). En voici les résultats pour le moment:

Résultat des tests jsPerf

On constate que:

Sur Android ICS, le hack est effectivement beaucoup, beaucoup plus rapide…
Bien que tous les deux basés sur WebKit, Chrome et Safari donnent des résultats opposés.
Opera est bizarrement à la ramasse, quelle que soit la méthode employée.
En tous cas, vous avez les billes pour savoir pourquoi vous choisissez telle ou telle méthode ! Alors, quelle sera votre conclusion ?

comments powered by Disqus