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

OverviewHTML5GraphicswithCanvasandSVG

Au programme de cette série d’articles, nous allons découvrir :

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

Ce premier article traitera donc des bases de SVG et de Canvas. 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 les bases de WebGL et des shaders) : Graphismes HTML5 grâce à SVG, Canvas 2D et WebGL (module 1).

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

Note : vous pouvez tester la plupart des exemples directement au sein de cet article si vous utilisez IE9+, Firefox, Chrome, Opera ou Safari.

Les bases de SVG

Les scènes statiques

Commençons donc par la base de la base de SVG. SVG (pour Scalable Vector Graphics) permet comme son nom l’indique d’afficher des objets graphiques vectoriels. Les objets sont décrits dans un langage XML qui vont enrichir le DOM comme les autres éléments HTML. Ainsi, l’interaction avec les éléments créés dans le DOM par SVG se fait de la même façon qu’avec les autres éléments HTML: évènements, application de CSS, accessibilité via ARIA, etc. Par ailleurs, le fait d’avoir cette persistance en mémoire des objets fait que l’on considère cette technologie comme “retained mode”.

Vous avez un ensemble de primitives pour dessiner (rectangle, cercle, ellipse, etc.) ou alors via des Paths. Vous pouvez également effectuer des transformations, translations, afficher du texte. Bref, il y a de quoi faire vous allez voir.

Si vous êtes développeur Silverlight, SVG vous rappellera furieusement XAML si nous devions faire un parallèle. Malgré tout, le moteur XAML de Silverlight reste quand même plus riche et plus orienté contrôle utilisateur/scénario data (moteur de binding, templating, visual state manager, etc.).

Regardons un des exemples les plus simples qui soit utilisant la méthode de fragments SVG intégrés dans le code HTML5 (ou SVG in-line). Ce markup :

 <!DOCTYPE html >
<html xmlns="https://www.w3.org/1999/xhtml">
    <head>
        <style type="text/css" media="screen">

        </style>
   
        <script type="text/javascript">
        </script>          
    </head>
    <body>
        <svg height="110px" width="110px">    
            <rect id="myRect" height="100px" width="100px" fill="blue"/>
        </svg>
    </body>
</html>

Nous donne le résultat suivant (si votre navigateur supporte SVG) :

Ou vous pouvez tester cet exemple avec cette page : Exemple 1 SVG. La 1ère ligne de markup SVG demande à réserver une zone de dessin de 110 pixels par 110 pixels et de dessiner un rectangle bleu de 100x100. Bon, pour l’instant, vous me suivez toujours j’imagine. Clignement d'œil

Il y a différentes façon d’intégrer du SVG dans sa page Web :

  • Les fragments SVG intégrés dans le code HTML5, sans utiliser d'objet étranger (c'est-à-dire, intégrer une balise <svg> dans votre code HTML). C’est l’exemple que nous venons de voir ensemble juste au-dessus.
  • Le format SVG en tant que type de document complet (avec une extension de fichier .svg)
  • Le format SVG dans le code XML ou XHTML (similaire à la méthode HTML5, uniquement avec les fichiers XML ou XHTML)
  • Le format SVG en tant qu'image CSS. Vous pouvez par exemple utiliser cet exemple : SVG Gradient Background Maker si vous souhaitez ajouter un gradient SVG en tant qu’image de fond à un élément par CSS.
  • Le format SVG utilisant l'élément object, comme dans l'exemple suivant (remarquez les attributs type, height et width) :
 <object data="rect2.svg" width="100%" height="400px" type="image/svg+xml"></object>
  • Le format SVG utilisant les éléments img, embed, iframe, ou frame, comme dans l'exemple suivant :
 <embed id="Smiley" src="smiley.svg" type="image/svg+xml">

Internet Explorer 9+ prend en charge l’ensemble de ces méthodes.

Alors bien sûr on peut aller bien plus loin que l’exemple que nous venons de voir comme le montre cette page: Exemple 2 SVG. Dans IE9, cela donne ce résultat:

HTML5Graphics_002_ComplexSVGScene

1ère chose intéressante à noter sur cette copie d’écran, si vous faites “CTRL+F” pour chercher dans la page sur “tex”, vous voyez bien le texte dessiné sur un path (le rail du train) correctement sélectionné. Cela veut également dire qu’un moteur de recherche est susceptible de pouvoir indexer votre contenu malgré son côté vectoriel (chose plus difficile avec du Flash ou Silverlight). Par exemple, si vous tapez ces mots clé dans Google : “The Past, Present,and Future of Vector Graphics for the Web ARCADE Funhouse”, vous allez tomber sur cette page: SVG Theme Park qui est la source de mon exemple 2.

Ensuite, si vous lancez la barre de développement de votre navigateur préféré avec la touche F12 (ou si vous utilisez un outil équivalent comme FireBug), vous pourrez bien vérifier que chacun des éléments actuellement dessiné à l’écran est bien présent dans le DOM. Voici un exemple avec IE9 :

HTML5Graphics_003_ComplexSVGSceneF12

Sur cette copie d’écran, on voit que j’ai ciblé l’élément qui correspond à une sorte de piscine et que je suis tombé sur le path correspondant avec l’ID “pool_water_1_ ”. Amusez vous à balader la souris en mode ciblage et vous verrez alors des petits rectangles bleus au dessus de chacune des zones du dessin.

Par ailleurs, si vous zoomez dans la scène, vous observerez que vous n’avez aucune perte de définition grâce au côté vectoriel de SVG. Par exemple, si je reprends l’exemple précédent, je peux zoomer sur la piscine en conservant une excellente qualité graphique :

HTML5Graphics_005_SVGZoom

A ce stade, vous allez surement vous dire aussi: “mais comment on fait pour générer une telle scène? On se tape quand même pas tout le SVG à la main ? ”. Non, rassurez-vous ! Il existe des outils pour vous aider, on en parlera plus tard.

Ajout de l’interactivité

Il y a plusieurs façons d’ajouter de l’interactivité à des éléments SVG. Commençons par la plus simple: le branchement de code sur des évènements du DOM. Regardez le code de la page suivante (que vous pouvez tester ici : Exemple 3 SVG) :

 <!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript">
        // This function is called when the circle is clicked.
        function clickMe() {
            // Display the alert.
            alert("You clicked the SVG UI element.");
        }
    </script>
  </head>
  <body>
    <h1>
      SVG User Interface
    </h1>
    <!-- Create the SVG pane. -->
    <svg height="200" width="200">
      <!-- Create the circle. -->
      <circle cx="100" cy="100" r="50" fill="gold" id="uIElement" onclick="clickMe();"
      />
    </svg>
    <p>
      Click on the gold circular user interface element.
    </p>
  </body>
</html>

Cela donne le résultat suivant (si votre navigateur supporte SVG). Cliquez sur le cercle jaune pour lever l’évènement de click :

On dessine simplement un cercle jaune d’un rayon de 50 dont le centre est décalé de 100 pixels à gauche et 100 pixels vers le bas par rapport au coin supérieur gauche de la zone de dessin. Ensuite, on s’abonne à l’évènement onclick() sur lequel on branche la fonction “clickMe() ”. Le “hit testing” sera fait pour vous par le navigateur et ne sera déclenché uniquement lorsque vous cliquerez bien sur le cercle et pas à côté dans le reste de la surface de dessin SVG. Nous verrons que le fonctionnement de <canvas> est bien différent de ce côté.

Il y a un truc chouette avec SVG, on peut le coupler avec CSS. Observons par exemple le code source suivant (que vous pouvez tester ici : Exemple 4 SVG) :

 <!DOCTYPE html >
<html xmlns="https://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=9"/>
        <style type="text/css" media="screen">
            rect.greenrect {fill:green;}
            rect:hover {fill:yellow;} 
        </style>
    </head>
    <body>
        <svg height="75px" width="75px">    
            <!--No fill (defaults the color to #000000)-->
            <rect id="myRect1" height="100%" width="100%" >
        </svg>
        <svg height="75px" width="75px">    
            <!--using the class="greenrect"-->
            <rect id="myRect2" height="100%" width="100%" class="greenrect"/>
        </svg>
        <svg height="75px" width="75px">
            <!--using the style="fill:pink"-->    
            <rect id="myRect3" height="100%" width="100%" style="fill:pink"/> 
        </svg>
        <svg height="75px" width="75px">
            <!--using the attribute fill="red"-->    
            <rect id="myRect4" height="100%" width="100%" fill="red"/>
        </svg>
    </body>
</html>

On dessine 4 rectangles les uns derrière les autres. Le 1er est noir car nous ne précisons par la couleur donc il prend celle par défaut. Le 2ème est régit par la classe “greenrect” qui va, via CSS, le peindre en vert. Le 3ème est rose à travers le style in-line qui lui est fixé et enfin le dernier est simplement rouge. On observe alors une autre chose intéressante dans le style définit en haut de page. L’utilisation de la pseudo classe “hover” nous permet de dynamiquement changer la couleur en jaune au survol de la souris sur un des rectangles (sauf le 3ème qui écrase la règle avec son style in-line).

Si votre navigateur le supporte, vous pouvez jouer avec directement an sein de cet article :

Avouez que c’est sympa non? Sourire J’ai creusé un peu plus loin. Je me suis dit qu’il serait alors sympa de combiner les nouveaux jouets arrivant avec CSS3 avec SVG. Cela est possible uniquement avec l’élément racine de votre description SVG (le tag <svg> donc) et pas au niveau des sous-éléments. J’y reviendrais plus tard dans le 4ème billet dans les expérimentations que j’ai faites avec les différents navigateurs.

Pour finir, rien ne vous empêche bien évidemment de mélanger les 2 comme le montre cet exemple-ci : Exemple 5 SVG.

HTML5Graphics_004_SVGInteractivity

Niveau de support dans Internet Explorer

On retrouve la spécification SVG aujourd’hui sous 2 formes : la version complète SVG 1.1 2nd Edition aujourd’hui en “Recommendation” depuis le 16 aout 2011 implémentée par IE9+ et les autres navigateurs modernes et la version SVG Tiny 1.2 logiquement prévue pour les périphériques mobiles et elle en version “Recommendation” également depuis le 22 décembre 2008.

Dans IE, voici les éléments que nous avons choisis de supporter dans SVG 1.1 2nd Edition :

MVA - Graphismes HTML5 grâce à SVG^J Canvas 2D^J WebGL

Vous aurez davantage de détails sur notre support de SVG dans IE9 dans le Guide du développeur Internet Explorer 9.

Vous noterez l’absence du support des “Declarative Animation” (basée sur les animations SMIL) et des fonts SVG. Mozilla de son côté avec Firefox va également en partie dans notre sens sur les fonts SVG : Mythbusting: Why Firefox 4 won’t score 100 on Acid3...and why that’s a good thing

IE9+ comme Firefox ont en effet fait le choix d’implémenter le format WOFF pour les fonts plutôt que de partir sur les fonts SVG qui, de l’aveu même du working group SVG, dispose d’un avenir incertain. WOFF semble donc un choix plus judicieux et pragmatique plutôt que de partir sur un support disparate des fonts SVG juste pour augmenter artificiellement le score au test ACID3.

Par ailleurs, pour ma part, je considère que les animations SVG rentre en conflit avec les possibilités offertes par CSS3 Animations (supportées par tous les navigateurs actuels). Il existe en effet souvent des collisions dans les possibilités offertes par chacun des modèles (HTML5, CSS3 ou SVG). D’après ce que j’en sais, les groupes de travail au W3C n’ont pas encore tranchés sur l’intérêt des animations SVG vs les animations CSS3 qui pourraient être appliquées aux éléments SVG. Un groupe de travail s’est formé pour rassembler tout cela autour de la spécification Web Animations: https://dev.w3.org/fxtf/web-animations/ (mais surtout pour résoudre des problèmes bien plus complexes).

Les bases de Canvas

Les scènes statiques

Il faut voir <canvas> comme une image PNG dynamique. Vous disposez d’une surface de dessin (une bitmap) dans laquelle vous allez dessiner à l’aide de primitive accessibles via JavaScript. Ces primitives sont grosso-modo les mêmes qu’avec SVG : rectangles, lignes, remplissage de formes, courbes de Bézier, etc.

Par contre, la balise canvas est radicalement différente de SVG sur plusieurs points. Tout d’abord, c’est un mode dit “Fire and Forget”. Il n’y a donc pas de maintien en mémoire de ce que vous avez déjà dessiné. C’est à vous de savoir ce que vous avez dessiné et où. Ainsi, au niveau du DOM, canvas est vu comme une boite noire et peut donc poser potentiellement des problèmes d’accessibilité. J’en ai déjà parlé ici: Techdays 2011 - slides et ressources de la session Accessibilité d’HTML5 et Silverlight . Par ailleurs, contrairement à SVG, vous pouvez manipuler chacun des pixels de votre zone de dessin si vous le souhaitez. Si  vous êtes développeur Silverlight, on pourrait ainsi un peu comparer cet élément HTML5 à l’objet WriteableBitmap quelque part. Mais heureusement l’élément canvas est bien plus riche que cela.

Reprenons un exemple simple pour commencer. On va dessiner la même chose qu’avec le 1er exemple SVG vu plus haut. Regardez le code de cette page :

 <!DOCTYPE html >
<html xmlns="https://www.w3.org/1999/xhtml">
    <head>
        <style type="text/css" media="screen">
        </style>
        <script type="text/javascript">
            function init() {
                var canvas = document.getElementById("monCanvas");
                var ctx = canvas.getContext("2d");
                ctx.fillStyle = "rgb(0,0,255)";
                ctx.fillRect(10, 10, 100, 100);
            }
        </script>
    </head>
    <body onload="init();">
      <canvas id="monCanvas" width="100px" height="100px" />
    </body>
</html>

Vous pouvez le tester ici : Exemple 1 Canvas

L’élément canvas est déclaré avec une taille de 100 pixels de large par 100 pixels de haut. C’est tout ce qu’il faut faire du côté du markup HTML. Tout le reste se fait ensuite par JavaScript.

En JavaScript, on exécute la function init() au chargement du document. Cette fonction s’occupe alors de récupérer mon élément canvas à partir de son ID puis ensuite récupère le contexte 2D (la surface de dessin). Une fois que vous avez votre contexte en main, vous pouvez appeler dessus les primitives du canvas.

Si votre navigateur supporte cet élément, vous devriez avoir un magnifique carré bleu de dessiné juste en-dessous de ce texte :

Ajout de l’interactivité

L’interactivité avec les éléments dessinés au sein du Canvas va se faire uniquement via JavaScript. Vous n’avez en effet pas d’évènements qui seront propagés par le navigateur vu que le DOM n’est pas rempli par les éléments du Canvas. Pour bien comprendre la différence avec SVG, nous allons reprendre le même exemple que l’exemple 3 SVG (avec un cercle jaune) mais en canvas. Vous pouvez le tester sur cette page Exemple 2 Canvas. Cela correspond à ce code :

 <!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript">
        // This function is called on page load.
        function drawOnCanvas() {
            // Get the canvas element.
            var canvas = document.getElementById("uIElement");

            // Make sure you got it.
            if (canvas.getContext)
            // If you have it, create a canvas user inteface element.
            {
                // Specify 2d canvas type.
                var ctx = canvas.getContext("2d");
                // Draw gold UI element.
                // Start the path.
                ctx.beginPath();
                // Define the fill color in RGB for gold.
                ctx.fillStyle = "rgb(255 ,215 ,0)";
                // Draw the circle using an arc.
                ctx.arc(100, 100, 50, 0, 2 * Math.PI, true);
                // Fill the circle.
                ctx.fill();
            }
        }

        // This function is called when you click the canvas.
        function clickOnUI() {
            alert("You clicked the canvas UI element.");
        }
    </script>
  </head>
  
  <body onload="drawOnCanvas()">
    <h1>Canvas User Interface</h1>
    <!-- Create the Canvas element. -->
    <canvas id="uIElement" width="200" height="200" onclick="clickOnUI()" />
    <p>Click on the gold circular user interface element.</p>
  </body>
</html>

Si votre navigateur le supporte, vous devriez voir juste en-dessous le même cercle jaune que précédemment :

Vous remarquerez ainsi que l’évènement de click est levé indifféremment du fait que vous ayez cliqué sur le cercle jaune ou pas. En effet, c’est logique, nous nous sommes abonnés à l’évènement onclick() de l’élément canvas lui-même. Il sera donc levé quelque soit l’endroit sur lequel nous cliquerons. Si l’on souhaitait obtenir le même comportement qu’avec l’exemple SVG où l’évènement était uniquement levé sur le click sur le cercle, il faudrait procéder autrement. Il faudrait tout d’abord repérer où se trouvait le curseur de la souris au moment où l’utilisateur a cliqué pour obtenir les coordonnées X et Y. Une fois les coordonnées récupérées, il faudrait ensuite vérifier ce que nous avions dessiné à cet endroit précis et si ce pixel particulier faisait alors parti du cercle jaune (il suffit de faire un peu de math Clignement d'œil). C’est donc à vous de vous occuper de tout cela et je pense que vous devriez maintenant mieux comprendre la différence entre le mode “retained mode” de SVG et le mode “fire & forget” de Canvas.

CanvasPad : une application pour apprendre Canvas en tout simplicité

Si vous souhaitez vous familiariser avec les bases de canvas, je ne serais que trop vous recommander d’aller jouer avec notre application CanvasPad présente sur notre site IE Test Drive ici : Canvas Pad.

Il y a un petit éditeur de script intégré avec des exemples simples vous permettant de faire vos premières armes dessus.

HTML5Graphics_006_CanvasPad

On y retrouve les formes de bases, la possibilité de gérer des ombres portées, d’afficher du texte, des images et des vidéos au sein même du canvas, des transformations et des animations.

Revue du même jeu écrit en Canvas et SVG

Il m’a paru intéressant pour finir cette introduction aux bases de ces 2 jeux d’APIs de comparer exactement le même jeu écrit à base de Canvas puis à base de SVG. Le principe est simple : on a une barre (un paddle) en bas, une petite balle rouge qui rebondit sur les murs et l’on doit simplement éviter que la petite balle rouge touche le sol. Voilà les règles extrêmement complexes du jeu. Sourire

Visuellement, les 2 jeux sont identiques :

HTML5Graphics_007_GamePad

Vous pouvez tester la version canvas ici : Canvas Racquetball et la version SVG ici : SVG Racketball. Ils sont issus de ces 2 tutoriaux MSDN : Programmation de jeux simples avec Canvas ou SVG

Je vous invite à lire ce tutorial qui explique bien les différences entre les 2 modes sur ce jeu particulier. Nous verrons dans le prochain article une tentative de positionnement de chacun des 2 modes par scénario d’utilisation. 

A bientôt pour la suite !

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

Follow the author @davrous