Introduction aux APIs graphiques d’HTML5: SVG & Canvas (2/2)

Nous allons voir ici les scénarios clés d’utilisation de canvas ou SVG après avoir vu les bases dans l’article précédent.

Cet article fait donc parti de cette série :

1 – les bases de SVG et de Canvas
2 – les scénarios clés d’utilisation de ces 2 jeux d’APIs

En complément, il existe un cours MVA de 40 min en français et en vidéo reprenant la base de cet article (y ajoutant d’ailleurs en plus un peu de WebGL) : Graphismes HTML5 grâce à SVG, Canvas 2D et WebGL (module 2).

Cours MVA - Graphismes HTML5 grâce à SVG, Canvas 2D et WebGL

Le but du jeu étant de vous donner quelques éléments de réponse et des orientations d’usage pour vous aider à choisir le mode potentiellement le plus adapté à vos besoins.

Tableau récapitulatif des grandes différences

Commençons tout d’abord par rappeler les grandes différences entre <canvas> et SVG :

<canvas> SVG
Niveau d’abstraction Basé sur les pixels (PNG dynamique) Basé sur des formes & vecteurs
Eléments Un unique élément HTML similaire à <img> dans son comportement De nombreux éléments graphiques qui font alors partis du Document Object Model (DOM)
Interaction Modifiable uniquement par Script Modifiable par Script et CSS
Modèle évènementiel Interaction utilisateur très granulaire (x,y de la souris) Interaction utilisateur abstraite et gérée par le DOM
Performance Les performances sont meilleures avec des petites surfaces et/ou un nombre important d’objets à l’écran (>10k) Les performances sont meilleures avec un nombre inférieur d’objets (<10k) et/ou sur de plus larges surface de dessin

Pour ceux qui connaissent la programmation sur Windows Phone 7, vous savez que vous avez 2 choix possibles pour vos interfaces: XNA ou Silverlight (avant l’arrivée de Mango qui peut les mélanger). Le parallèle pour moi est assez évident, XNA de part son mode “fire & forget” ressemble à Canvas et Silverlight par son côté vectoriel basé sur XAML à SVG. Les 2 ont donc bien évidement des avantages et des inconvénients.

Quand utiliser <canvas> et quand utiliser SVG: les scénarios évidents

Nous allons maintenant voir les bénéfices et les limitations techniques de chacune des technologies en tentant d’y inclure une forme de bon sens lorsque l’une est plus appropriée que l’autre. Tout d’abord, vous devez comprendre que <canvas> ou SVG peuvent réaliser des choses très similaires la plupart du temps. Nous allons donc tenter de mettre en exergue les scénarios ou <canvas> est particulièrement meilleur que SVG et vice-versa. Pour d’autres cas, nous verrons que le recouvrement rend difficile la prise d’un choix tranché. Alors, qu’allez-vous prendre, la pilule bleue ou la pilule verte ? (Ou les 2 ! Sourire)

HTML5Graphics_008_Canvas_ou_SVG

Les documents vectoriels de haute qualité

Les documents nécessitant une très haute qualité graphique ont toujours été l’un des points fort de SVG qu’ils soient très détaillés pour une visualisation ou impression, seul ou embarqué au sein d’une page Web. Par ailleurs, la nature déclarative de SVG se prête bien à sa génération depuis des outils disposant d’interface riche ou depuis des données provenant d’une base de données. La génération du XML pouvant alors se faire soit coté serveur soit côté client via JavaScript.

Le type de documents que l’on voit souvent produits ensuit au format SVG peuvent être:

– Des plans de construction de bâtiments
– Des schémas électriques ou aéronautiques
– Des diagrammes d’organisations hiérarchiques
– De la cartographie
– Des diagrammes biologiques

Bref, vous voyez l’idée. Le fait de pouvoir zoomer dans ce type de document sans perte de définition rend naturellement SVG attractif. Etudions ensemble 2 exemples d’applications de cette liste.

Si vous lancez dans un navigateur récent la démo Real-world Diagrams issue de notre site Internet Explorer Test Drive, vous tomberez sur une page combinant 3 diagrammes SVG. Si l’on zoom sur l’équivalent du rectangle noir ci-dessous :

20110422-towtucas-image2

On obtient le résultat suivant :

20110422-towtucas-image3

Cela nous permet tout simplement d’extraire davantage d’informations grâce au côté vectoriel de SVG. Par ailleurs, le S de SVG veut dire Scalable. Dans un contexte d’impression, on pourrait tenter de le traduire en français par “pouvant s’étirer”. Du coup, les scénarios d’impression prennent vraiment tout leur sens et vous pouvez imaginer sortir de très grands formats de tirage tout en conservant une parfaite qualité. J’en veux pour exemple ce magnifique diagramme : Microsoft Dynamics Customer Model qui combine SVG et les fonts WOFF sur lequel vous pouvez vous amuser à zoomer et avec lequel vous pouvez imaginer de très belles impressions.

DiagrammeSVGCRM

Le 2ème exemple est sur la cartographie. Vous ne le savez peut-être pas mais Bing Maps et Google Maps utilisent déjà SVG.

Lancez dans votre navigateur cette recherche dans Bing Maps : Itinéraire de Microsoft France à la tour Eiffel . Vous remarquez que lorsque vous zoomez sur la carte pour suivre le tracé, il n’y a pas de perte de définition du tracé en lui-même.

Si vous lancez la barre de développement d’IE9+ avec la touche F12 et que vous visez le rectangle contenant le tracé, vous obtiendrez le résultat suivant :

HTML5Graphics_009_BingMaps

Et surprise! On découvre la présence du tag SVG déclarant le path qui va bien pour suivre l’itinéraire qui vous intéresse :

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
    <path fill="none" fill-opacity="1" stroke="#ffffff" stroke-dasharray="solid" stroke-linecap="butt" stroke-opacity="0" stroke-width="30" d="M 291 303 L 306 286 L 316 263 L 324 252 L 331 250 L 354 209 L 360 207 L 360 202 L 363 196 L 433 117 L 449 116 L 465 95" />
    <path fill="none" fill-opacity="1" stroke="#3333ff" stroke-dasharray="solid" stroke-linecap="butt" stroke-opacity="0.713726" stroke-width="4" d="M 291 303 L 306 286 L 316 263 L 324 252 L 331 250 L 354 209 L 360 207 L 360 202 L 363 196 L 433 117 L 449 116 L 465 95" />
</svg>

Bref, pour toutes ces raisons, on peut sereinement placer ces usages à droite de notre cadran :

HTML5Graphics_008_Canvas_ou_SVG_2

SVG comme format d’image

Un autre scénario d’utilisation classique du format SVG est pour le rendu d’image simple que ce soit au sein d’application ou de pages web. Cependant, comme SVG est chargé en mémoire dans le DOM et son XML analysé avant d’en créer une image, les performances peuvent être légèrement dégradées par rapport à une image classique PNG ou JPG. Malgré tout, cela reste infinitésimal comparé au cout total du rendu complet d’une page Web. Par ailleurs, l’intérêt du côté vectoriel et de la qualité du rendu associé compense plus que largement cette légère perte de performance.

Prenons l’exemple ci-dessous. Ces 2 images représentent potentiellement des pastilles associées via CSS à des éléments de type <li>. Ces 2 images sont identiques en rendu et ne diffèrent que de 1 Ko (SVG étant légèrement inférieur en taille bien que non compressé) :

BulletsPoints1

Le SVG est à gauche et le format PNG à droite. Visuellement, on note donc que peu de différences. Par contre, imaginons que l’on décide de réutiliser le même élément sur des écrans plus larges ou disposant d’un DPI plus élevé (ou PPP pour points par pouce en français), cela va potentiellement poser problème côté PNG. En effet, lorsque l’on va zoomer sur une image PNG, on va rapidement obtenir un résultat pixélisé :

BulletsPoints2

C’est toujours le même élément SVG de dessiné à gauche, le fait de zoomer ne change en rien sa taille ou sa consommation mémoire. Par contre, il va falloir envisager de générer une image PNG à une plus haute résolution si l’on veut désormais obtenir un résultat comparable à droite. Cela va donc augmenter la taille de l’élément PNG à télécharger. SVG apparait alors dans ce scénario comme un bon candidat au remplacement des éléments de type image même pour des éléments extrêmement simplistes comme cette puce de liste.

Cadran2

Canvas pour la manipulation de pixels

Allons maintenant voir de l’autre côté du cadran avec Canvas. Comme Canvas nous permet de dessiner et manipuler une surface basée sur des pixels, plusieurs expérimentations mettant en œuvre des algorithmes plus ou moins complexes ont vu le jour sur le web. Vous pouvez par exemple trouver des exemples impressionnants de Ray tracers ou de génération de fractales. Cela me rappelle donc ma jeunesse où je passais des heures (des nuits que dis-je !) à calculer des scènes sous POV-Ray ou avec Fractint. On peut également imaginer traiter des pixels existant pour effectuer des filtres dessus.

Générer des pixels depuis 0

La première approche consiste à générer une image complètement depuis 0 par code au sein du canvas. C’est ce que l’on peut faire par exemple avec un lanceur de rayons. L’exemple ci-dessous a été écrit par Adam Burmister. Le but du jeu étant donc de simuler le trajet effectué par la lumière par pixel pour obtenir de jolis effets de réfraction, réflexion, etc.

raytracingcanvasJS

L’auteur lui-même vous averti cependant avec la phrase suivante : “This is very CPU intensive. Your browser may appear to stop responding” qui pourrait être traduite par “Cela consomme beaucoup de ressource CPU. Votre navigateur pourrait sembler ne plus répondre”. HTML5Fractal

Ainsi, alors que les APIs de Canvas permettent effectivement de générer de telles images, cela n’apparait pas pour autant être une très bonne idée. L’auteur résume d’ailleurs ses expérimentations par la phrase suivante : “Ray-Tracing [is] The Worst Application of JavaScript Ever” ou “le lancer de rayons [est] la pire des applications JavaScript imaginable”.

Dans le même genre d’idées, on retrouve la génération des fameuses fractales de Mandelbrot en Canvas/JS comme cet exemple : Mandelbrot Set in HTML 5 Canvas et comme vous pouvez le voir sur la copie d’écran de droite. Vous pouvez jouer à zoomer dans les zones en traçant de nouveaux rectangles à la souris au sein du canvas.

Cependant, aussi bons soient devenus les moteurs JavaScript des derniers navigateurs, il n’y a vraiment aucun intérêt à faire de la génération d’images artificielles en ray-tracing en JavaScript/canvas plutôt qu’en C++/C#. Mieux vaut en effet être le plus bas possible et le plus rapide possible lorsqu’il s’agit de générer des séquences d’images virtuelles. C’est pour cela que je me pose souvent l’intérêt de la présence de mini ray-tracers dans les benchmarks JavaScript. L’application concrète de ces expérimentations dans un projet réel est proche de 0. Malgré tout, je trouve l’exemple particulièrement parlant sur les possibilités de canvas versus SVG. Il en est peut-être autrement des fractales où il peut y avoir un intérêt dans le milieu scientifique ou de l’éduction de pouvoir générer des fractales et de jouer avec dynamiquement au sein d’un navigateur ou d’une plateforme supportant HTML5.

Retoucher des pixels existants

Autre intérêt de pouvoir manipuler les pixels, outre de pouvoir les créer depuis 0, c’est de pouvoir retoucher ou jouer avec des pixels existants. 2 scénarios me viennent en tête : un amusant ne servant à rien et un autre pouvant servir concrètement aux applications de demain.

Le 1er amusant consiste à simuler les effets d’écran bleus/verts inaugurées par le film à qui nous devons tout aujourd’hui. Je veux bien entendu parler de Star Wars ! L’idée est donc d’identifier une couleur d’arrière-plan (un fond vert ou bleu habituellement) et de remplacer cette couleur par un pixel venant d’autre part. Cela permet ainsi assez simplement d’afficher un fond fictif derrière un personnage.

Or, comme on peut peintre le canvas avec des images venant d’une vidéo HTML5, on peut imaginer combiner 2 vidéos en temps réel en utilisant cette technique de remplacement de pixels. Le code suivant permet ainsi de remplacer les pixels vert d’un des canvas avec les pixels de l’autre canvas faisant exactement la même taille :

function GreenScreenAtoB(a, b) {
    var aImageData = a.getImageData(0, 0, a.canvas.width, a.canvas.height);
    var bImageData = b.getImageData(0, 0, b.canvas.width, b.canvas.height);
    var aPixels = aImageData.data;
    var bPixels = bImageData.data;

    if (aPixels.length != bPixels.length) {
        window.alert("Vos Canvas n'ont pas exactement le même nombre de pixels!");
        return bImageData;
    }

    var pixelCount = bPixels.length;
    for (var pixelIndex = 0; pixelIndex < pixelcount; pixelIndex += 4) {
        // On récupère les composantes RGBA de chacun des pixels de b
        var r = bPixels[pixelIndex + 0];
        var g = bPixels[pixelIndex + 1];
        var b = bPixels[pixelIndex + 2];
        var a = bPixels[pixelIndex + 3];

        // Et si le pixel de b est vert, on le remplace avec le pixel venant de a
        if (r == 0 && g == 255 && b == 0 && a == 255) {
            bPixels[pixelIndex + 0] = aPixels[pixelIndex + 0];
            bPixels[pixelIndex + 1] = aPixels[pixelIndex + 1];
            bPixels[pixelIndex + 2] = aPixels[pixelIndex + 2];
            bPixels[pixelIndex + 3] = aPixels[pixelIndex + 3];
        }
    }

    return bImageData;
}

Vous pouvez jouer avec un exemple utilisant 2 vidéos h264 ici : Star Wars Green Effect . Il vous faudra donc un navigateur comme IE9 ou Safari pour le jouer et cela vous donnera ce genre de résultat :

StarWarsGreenEffect

Cependant, comme pour les ray-tracers, l’intérêt de cette démonstration est relativement pauvre en production. Mais grâce à WebGL et ses shaders, on peut maintenant envisager de sous-traiter ces filtres au GPU en temps réel sans aucun problème.

Le 2ème scénario qui me vient en tête est bien plus intéressant dans ses applications : c’est la retouche d’image. On peut ainsi imaginer faire un effet anti-yeux rouges par exemple. Vous pouvez tester cette démonstration écrite par Daniel Glazman ici : Exemple anti-yeux rouges qui met en œuvre ce principe.

Mais on peut aller encore plus loin sur la manipulation d’images comme le montre l’exemple ci-dessous :

Ce dernier est adapté de l’article suivant : Canvas Direct Pixel Manipulation et ouvre de belles perspectives je pense. On peut imaginer récupérer l’histogramme d’une image, faire des effets de reflet dynamiquement sur un élément… voir même créer un éditeur en ligne d’images comme celui de DeviantArt Muro. A vous maintenant de laisser libre court à votre imagination ! Clignement d'œil

Bref, vous aurez compris que la grande force de <canvas> sur SVG est sa capacité à manipuler les pixels. Lorsque cela fait sens et que les performances ne sont pas trop bridées par le moteur JavaScript, <canvas> est clairement la solution qu’il vous faut :

Cadran3

Les scénarios qui se recoupent et les scénarios hybrides

La plupart des scénarios intéressants ne montrent pas souvent un gagnant évident entre canvas et SVG. J’ai par exemple en tête 2 scénario qui pourraient vous intéresser en HTML5 : la génération de cartes/graphiques interactifs ou les jeux en 2D.

Cartes et diagrammes interactifs : plutôt SVG

Comme nous l’avons déjà dit plus haut, SVG se montre particulièrement intéressant pour l’affichage d’organigrammes, de cartes ou de courbes en haute résolution. D’autant plus qu’il existe, comme nous le verrons dans l’article suivant, de nombreux outils permettant sa génération.

Par ailleurs, par rapport à Canvas, SVG est plus adapté à une interaction utilisateur grâce à l’usage du DOM et de CSS derrière comme nous l’avons vu dans l’article précédent. SVG est également plus adapté aux scénarios d’accessibilité.

Comme nous l’avons déjà fait précédemment, le mieux pour comprendre est d’illustrer les différences à travers un exemple concret. Prenons ainsi une carte détaillée de l’Alaska disponible dans le domaine publique grâce à Wikimedia Commons :

En SVG, l’état d’Alaska est représenté par un élément de type <path> constitué d’environ 162 500 caractères de données géométriques :

<path id="AK" fill="#cdc3cc" d="M 777.5514,1536.1543 C 776.4904,1535.0933 776.7795,1530.0041 777.9416,
1529.2859 C 781.3258,1527.1943 787.2657,1532.4522 784.8317,1535.3849 …" />

Pour Canvas, la même forme pourrait être dessinée à travers la suite d’instructions JavaScript suivante :

function drawAlaska() {
    var canvas = document.getElementById("myCanvas");
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(777.5514, 1536.1543);
    ctx.bezierCurveTo(776.4904, 1535.0933, 776.7795, 1530.0041, 777.9416, 1529.2859);
    ctx.bezierCurveTo(781.3258, 1527.1943, 787.2657, 1532.4522, 784.8317, 1535.3849);
    //
    // Imaginez ici 2 875 directives supplémentaires...
    //
    ctx.bezierCurveTo(1689.8261, 12.13753, 1689.1395, 12.17333, 1685.8848, 10.52683);
    ctx.closePath();
    ctx.fillStyle = "#cdc3cc";
    ctx.fill();
}

Cela représente en fait 2 878 directives de dessins (moveTo, lineTo et bezierCurveTo) pour fabriquer cette carte détaillée de l’Alaska à une résolution fixe. Il y a donc de fortes chances que la version Canvas soit plus lourdes en poids. Bien sûr, si l’on dessine la carte dans une résolution inférieure, il faudrait moins de lignes de code et donc un fichier plus petit.

Les applications de cartes basées sur SVG proposent en général des expériences interactives comme des changements levés sur le survol de la souris, la sélection d’un élément, le passage d’un élément à l’autre et la mise à l’échelle. Ces opérations sont très simples à mettre en œuvre comme nous l’avions déjà vu. Par exemple, pour réagir sur un click de souris, voici l’unique chose à faire :

 <path id="AK" fill="#cdc3cc" onmousedown="window.alert('Alaska');" d="M 777.5514,1536.1543 …" /> 

Ou pour réagir au survol :

path#AK:hover { fill: yellow; }

Allez pour définitivement comprendre la différence sur les 2 modes, n’hésitez pas à jouer avec l’exemple ci-dessous qui montre, à travers cette carte des Etats-Unis, la différence entre la taille du code JavaScript à produire pour Canvas et la taille du path SVG pour chacun des états. Je me suis amusé à prendre une partie des spécifications des paths SVG du W3C pour analyser dynamiquement le contenu SVG et le transformer dans l’équivalent Canvas :

Note : vous noterez que si vous testez cet exemple sous IE9+ et ensuite sous Chrome/Firefox que le contenu des données affichées ici sous Path Data n’est pas le même (faites le test avec le même état sélectionné entre 2 navigateurs). Le code JS de parsing n’est donc pas tout à fait le même pour IE et FF pour générer le pseudo-code Canvas.

Un exemple plus intéressant de ce type d’interaction avec une carte peut être vu sur notre site Test Drive ici : Atlas zur Europawahl 2004 in Deutschland . C’est une visualisation des résultats des élections Européenne de 2004 en Allemagne.

Faire la même chose en Canvas vous obligerait à coder vous-même le “hit detection” en utilisant les coordonnées de position de la souris au moment du click ou du survol. En effet, vous n’avez alors plus le contexte lié à la forme que vous avez dessiné. Il existe bien sûr des librairies permettant de vous fournir de ce genre de services mais elles existent aussi pour SVG et disposent en général de bien meilleures performances.

Cadran4

Affichage de données en temps réel : plutôt Canvas

Nous avons déjà vu qu’il était plus complexes de gérer les interactions avec les éléments dessinés au sein du <canvas> versus SVG. Mais si aucune interaction n’est nécessaire, Canvas apparait souvent comme un meilleur candidat pour l’affichage de données en temps réel.

CanvasMeteoPrenons par exemple le cas d’affichage de données météo. Les techniques que l’on connait aujourd’hui consistent souvent à générer des images depuis le serveur sur un intervalle particulier puis de pousser l’image vers le client ou alors générer aussi rapidement que possible l’image côté client via l’utilisation de plug-ins. Bien que SVG pourrait permettre une alternative intéressante à la génération d’image rasterisée côté serveur pour économiser de la bande passante, <canvas> apparait malgré tout comme une option évidente pour ce scénario (tout comme pour les autres scénarios nécessitant l’affichage de données temps réel). En effet, comme vous pouvez le voir sur la copie d’écran de gauche affichant une carte météo, il n’y a pas forcément une très grande surface sur laquelle nous allons dessiner et le nombre d’objets (ou points) à dessiner peut être particulièrement élevé. Avec les APIs de <canvas>, cela peut être affiché (puis rafraichit) à une très grande vitesse sans aucune incidence sur le DOM. On pourrait de l’autre côté imaginer le même rendu en SVG en utilisant de nombreuses ellipses. Mais le cout de chargement de toutes ces formes dans le DOM ainsi que leurs modifications pour les animations serait particulièrement élevé en comparaison du <canvas>.

CanvasDonneesDynamiques Ainsi s’il vous faut dessiner un nombre important de formes, spécialement des formes très différentes et non géométriques, afin de générer des images destinées à une analyse de données, cela devraient vous faire pencher la balance du côté de l’utilisation de Canvas. Outre la météo, on peut ainsi imaginer des scénarios autour de l’astronomie, la biologie moléculaire ou l’affichage des modulations de fréquences d’un son comme on a pu le voir il y a quelques temps déjà avec l’application basée sur des sons de Daft Punk Anatomy of a Mashup.

Cadran5

Les jeux vidéo 2D : léger avantage à canvas

HTML5 se prête bien également à ce que l’on appelle communément le “casual gaming”. Des petits jeux vidéo souvent relativement “simples” techniquement mais diablement efficaces et visant un public le plus large possible. J’ai d’ailleurs une série d’articles sur ce sujet vous permettant d’apprendre à faire un jeu de plateforme avec canvas: Jeux HTML5: animation de sprites dans l’élément Canvas grâce à EaselJS

Mais alors, comment faire son choix entre canvas et SVG lors de la conception d’un jeu vidéo ?

Tout d’abord, historiquement, les librairies de jeux ont souvent été basées sur des APIs graphiques de bas niveau. Les développeurs de jeux vidéo vont donc être tentés par <canvas> pour y adapter leur savoir faire. Cependant, d’autres composants désormais également très importants comme les moteurs physiques travaillent à des niveaux d’abstraction plus élevés et n’ont finalement que faire des détails d’implémentations techniques du mode de dessin.  De leur côté, ils attendent ainsi certaines informations sur les géométries comme leurs bords, vitesses, tailles et positions et renvoient en échange des informations permettant d’adapter la vitesse (gestion de la gravité par exemple), de gérer les collisions et de mettre à jour les positions. Cela vous donne alors l’impression de “réalisme” sur les mouvements des objets et leurs interactions.

Si vous souhaitez jeter un œil sur des logiques de jeux démontrant qu’il est parfois difficile de choisir entre SVG et canvas, vous pouvez tester les 2 exemples suivant : SVG-oids et canvas-pinball. Ces 2 petits jeux ont été écrits par le même auteur. Il avait commencé par écrire le 1er jeu d’astéroïdes en SVG et il était parfaitement content du résultat. Lorsqu’il s’est ensuite attaqué au petit jeu de jeu de flipper, il avait également commencé par utiliser SVG du coup. Puis, finalement, il a changé son fusil d’épaule pour partir sur du <canvas> (pour des raisons que j’ignore). Cela veut simplement dire que pour ce genre de jeu, l’un ou l’autre fera parfaitement l’affaire.

Un exemple peut-être plus parlant est l’analyse de 2 jeux utilisant le même moteur physique (Box2D) mais utilisant 2 modes différents de dessin. Ce sont canvas-pinball et SVG-Dice. Bien que ces deux jeux n’ont strictement rien à voir sur leur logique, ils utilisent le même moteur pour gérer les positions, collisions, vitesses et autre aspects physiques liés à nos composants de jeu.

Pour le jeu de flipper, le gestionnaire d’animation de haut niveau redessine la scène en utilisant les APIs JavaScript du Canvas :

if (animationsInProgress) {
     ctx.save();
     ctx.lineWidth = 2.0;
     ctx.beginPath();
     ctx.moveTo(89, 322);
     ctx.lineTo(101, 295);
     ….
     ctx.stroke();
     ctx.restore();
     ctx.moveTo(tVp.x, tVp.y);
}

Pour le jeu de dés en SVG, le gestionnaire d’animation utilise des transformations sur des groupes pour repositionner des éléments graphiques existant à travers le DOM :

if (animationsInProgress) {
     this.rotation += (this.circleBody.m_linearVelocity.x/20);
     var transFormString = "translate(" + 
        Math.round(this.circleBody.m_position.x) + "," + 
        Math.round(this.circleBody.m_position.y) + ") scale (" + 
        this.scale.toString() + ") rotate(" +
        Math.round(this.rotation).toString() + "," +
        Math.round(this.xTrans).toString() + "," +
        Math.round(this.yTrans).toString() + ")";
        this.die2.setAttribute("transform", transFormString);
}

Ainsi, alors que le 1er redessine en permanence et repositionne les différentes géométries, l’autre se contente uniquement de les repositionner mais les maintient pour cela en mémoire. Cela a forcément un coup. Ce coup est néanmoins relativement faible pour des jeux de type “casual gaming”. Malgré tout, l’idée du mode immédiat fournit par <canvas> avec son jeu d’APIs bas niveau est souvent trop tentant pour les développeurs habitués à créer des jeux vidéos.

Par contre, je tiens à noter un avantage intéressant pour SVG : son côté vectoriel à nouveau et donc sa possibilité de s’adapter dynamiquement à une résolution précise. Prenez par exemple ce petit jeu Pacman écrit en SVG : Browser Hunt. Si vous vous amusez à redimensionner la fenêtre de jeu, vous verrez que le contenu s’adapte automatiquement grâce à utilisation du viewbox. Ceux d’entre vous qui sont développeurs XAML connaissent ce mécanisme de viewbox. Et bah, c’est tout simplement la même logique appliquée ici à notre scène SVG.

Autre facteur : les compétences existantes et le niveau de performance intrinsèque

Pour continuer autour du jeu, il y existe aussi des éléments extérieurs qui vont influencer le choix de la technologie qui sont souvent indépendants des fonctionnalités. Entre SVG et Canvas, il y a 2 de ces éléments que je vois ressortir : vos connaissances actuelles et vos attentes sur la performance.

En effet, les connaissances et jeux de compétences des développeurs jouent un rôle important dans le choix d’une nouvelle technologie. Comme nous l’avons déjà dit, pour la création d’un jeu, si vos développeurs ont une connaissance profonde des APIs graphiques de bas niveau (DirectX, OpenGL ou XNA) et disposent d’une connaissance limitée des technologies Web, Canvas va certainement s’imposer de lui-même.

La performance dispose d’un rôle clé également. Nous en reparlerons ainsi dans le dernier article car l’arrivée de technologies permettant “l’accélération matérielle” via le GPU changent également la donne. Il faut alors prendre le temps de comparer les caractéristiques des 2 technologies en matière de performance pure. Regardons par exemple ce graphique basée sur quelques études internes à Microsoft :

HTML5Graphics_009_Performance

En général, plus la taille/résolution d’écran augmente, plus les performances de Canvas vont se dégrader puisque la quantité de pixels à rafraichir augmente. De son côté, SVG a tendance à dégrader les performances au fur et à mesure que l’on augmente le nombre d’objets à l’écran car cela a une incidence directe sur le DOM qui prend le relais derrière. Alors bien sûr, ces mesures sont relativement grossières et non représentatives de tous les scénarios. Par ailleurs, il y a des variations énormes en fonction des plateformes (PC, Tablet, Téléphone), de la qualité de l’accélération matérielle (et du GPU) ainsi que de la vitesse du moteur JavaScript. Bref, le choix d’une des 2 technologies pour sa performance devra forcément passer par une étape de prototypage pour pouvoir évaluer la techno en fonction du scénario que vous avez en tête. Malgré tout, je trouve que ce graphique donne déjà une bonne idée des performances inhérentes au fonctionnement même de chacune des 2 technologies. Par ailleurs, ces informations sont valides pour toute application devant animer des informations graphiques en dehors de l’écriture de jeux vidéo.

Cadran6

Au fait : rien ne vous empêche d’utiliser les 2 !

Nous avons vu tout le long de cet article comment positionner l’une ou l’autre des technologies en fonction de nos besoins. Cependant, rien n’empêche de combiner ces 2 approches ! L’usage hybride dispose en effet de nombreux bénéfices puisque nous allons profiter des avantages de chacun. Ainsi, pour bénéficier d’un “hit detection” et d’une interaction utilisateur simplifiés, vous pouvez mettre en place un calque constitué de géométries SVG avec en dessous un élément <canvas> permettant de fournir des animations temps réel plus adaptées.

Il y a ainsi un nombre grandissant d’expériences voyant le jour où cette combinaison est efficace. Quand vous vous retrouvez dans un scénario nécessitant à la fois des animations graphiques dynamiques (Canvas) et une interaction utilisateur riche (SVG), chacune des 2 technologies peut être utilisée et doit être utilisée.

Cela est illustré par exemple par le petit jeu réalisé par mon copain David Catuhe: How to write a small game using HTML5 and JavaScript–BrikBrok

Par contre, petit regret pour part, on ne peut pas afficher une forme SVG au sein d’un canvas sans en perdre sa nature intrinsèque : interaction via CSS ou remontée des events via le DOM. On peut utiliser un élément de type <img> alimenté par du SVG comme source de la méthode drawImage() de <canvas> mais cela a pour unique but de “rasteriser” le contenu SVG. Dommage, cela aurait offert de beaux scénarios également !

Cela conclue notre tour d’horizon des possibilités offertes par SVG et <canvas>. Vous noterez que je n’ai pas abordé dans mes différents scénarios l’idée d’utiliser SVG pour produire entièrement une interface utilisateur. En effet, vu que j’ai fait un parallèle avec Silverlight et XAML en début d’article, il serait légitime de se demander si on ne pourrait pas réaliser une interface purement basée sur SVG comme on le fait en XAML. En tout cas, je me suis moi-même posé cette question. Il y a quelques années d’ailleurs, certains annonçaient que SVG était LA technologie à retenir pour nos UIs. Par exemple, il me semble qu’il existe au moins un frontal pour une version de Linux entièrement basé sur SVG. Des contrôles tels que les sliders, checkbox, bouton avec coins arrondis et toute autre forme de contrôles non rectangulaires sont possibles grâce à la nature vectorielle de SVG.

Bah alors ? Pourquoi ne pas y aller ? Tout simplement parce qu’entre temps, HTML et CSS ont très bien évolués et offrent une palette riche de possibilités : coins arrondis, gradients, transitions, formulaires, etc. Ainsi une grande majorité des nouveaux contrôles peuvent être réalisés avec le modèle de boite standard HTML ce qui, à mon sens, est préférable à l’alternative SVG. Par ailleurs, le positionnement de nos éléments s’est vu grandement améliorés dans CSS3 comme le modèle Flexbox ou notre proposition récente de positionnement par grille avec CSS3 Grid inauguré dans Internet Explorer 10.

Voilà. Je pense avoir fait un tour assez vaste des scénarios d’usage des 2 technologies. J’espère que tout cela vous aidera à mieux les positionner et à mieux vous guider dans votre choix. Si vous avez une expérience différente sur l’utilisation de ces modes graphiques, n’hésitez pas à venir la partager en commentaire de cet article. Je suis bien entendu preneur de vos retours.

Pour ma part, je tenais à remercier Patrick Dengler de Microsoft Corp, membre du groupe de travail SVG au W3C, qui m’a bien aidé dans l’écriture de cet article.

Si vous souhaitez aller un peu plus loin, je vous invite à suivre le module 3 du cours MVA où nous verrons quelques librairies et outils bien pratiques comme raphael.js, highcharts, d3.js ou bien three.js et babylon.js :

Cours MVA - Graphismes HTML5 grâce à SVG, Canvas 2D et WebGL

A bientôt pour de nouvelles aventures !

N’hésitez pas à me contacter sur twitter si vous souhaitez discuter de cet article.

12 thoughts on “Introduction aux APIs graphiques d’HTML5: SVG & Canvas (2/2)

  1. Effectivement, très bonne suite d'articles (parties 1 et 2) permettant de se faire une bonne idée des différences, faiblesses et avantages de ces deux technologies. Merci et bravo pour ce comparatif.

  2. Bravo pour la synthèse, c'est exactement ce que je cherchais. Je voulais te poser une question. J'ai trouvé cet exemple :

    codepen.io/…/dqtxs

    qui est ce qui s'approche le plus de ce que je veux faire, à savoir, dans la page d'accueil de mon site, avoir un vaisseau qui traverse l'écran en permanence de gauche à droite et qui traîne une banderolle animée avec un slogan. Saurais-tu, m'expliquer le code ? Car ce que je souhaite, c'est mettre un jpg à la place de l'avion de l'exemple, et surtout, savoir comment modifier la trajectoire pour qu'elle s'adapte à ma page d'accueil et, enfin, comme je le disais, que le déplacement soit permanent.

  3. Infiniment merci et bravo pour cet énorme effort d'explication qui n'a rien laissé au hasard.

    Personnellement, je découvre avec toi ces techniques et franchement ça donne du goût.

    Merci

Leave a Reply

Your email address will not be published. Required fields are marked *