« Tutoriel SVG dynamique avec DOM » : différence entre les versions

De EduTech Wiki
Aller à la navigation Aller à la recherche
m (Page créée avec « {{stub}} Category: SVG »)
 
m (Remplacement de texte — « <pageby nominor="false" comments="false"/> » par « <!-- <pageby nominor="false" comments="false"/> --> »)
 
(23 versions intermédiaires par 8 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
{{stub}}
{{Ebauche}}
 
<!-- <pageby nominor="false" comments="false"/> -->
 
'''Avertissement''': Il faudrait complètement revoir ce texte. Il a été écrit il y a longtemps quand les navigateurs implémentaient mal SVG - [[Utilisateur:Daniel K. Schneider|Daniel K. Schneider]] 26 mars 2011 à 15:55 (CET)
 
Consultez plutôt:
* http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html (enfin même ce document semi-officiel n'est pas à jour pour HTML5)
 
== Introduction ==
 
Créer des interactions et animations avec le DOM est plus difficile que [[Tutoriel SVG dynamique avec SMIL|SVG dynamique avec SMIL]], mais on peut faire ce qu’on veut selon le principe que tous les attributs du DOM sont animables.
 
Certains types d’interactions peuvent se programmer sans savoir grand chose de la programmation DOM (c’est pareil pour le traitement de formulaires HTML avec Javascript ...)
 
Etat de la technologie début 2011. SVG stand-alone ou SVG dans XHTML:
* marche avec Firefox, Opéra, Chrome, Safari (?)
* marchera avec IE9 Béta (?)
 
Pour HTML 5, consultez [http://caniuse.com/svg-html5 Inline SVG in HTML5].
* Chrome 10 ou 11 semblent le mieux marcher
 
Voici le principe:
 
* Dans l’élément qui doit déclencher un script on place un '''attribut d’événement''', par exemple ''onclick'' (Voir [[tutoriel SVG dynamique avec SMIL]])
* Cet attribut doit définir un fonction avec un argument
** le nom d’une fonction ECMAScript, par ex: ''circle_click'' et qui sera appelée
** un argument avec lequel sera transmis un ''objet événement'', par ex. ''evt''
 
Un objet événement est dynamiquement crée par le client. Il contient des informations variées, par exemple où l’événement a été déclenché etc.
 
<source lang="javascript">
  function circle_click(evt) {
    var circle = evt.target;
    var currentRadius = circle.getAttribute("r");
  }
</source>
 
<source lang="xml">
<circle onclick="circle_click(evt)" cx="300" cy="225" r="100" fill="red"/>
</source>
 
'''Exemple : Simple switch avec DOM'''
 
* http://www.yoyodesign.org/doc/w3c/svg1/animate.html'' (original, home de la traduction française de la spécification SVG 1.0)
* http://tecfa.unige.ch/guides/svg/ex/svg-w3c/script01.svg
* http://tecfa.unige.ch/guides/svg/ex/svg-w3c/
 
<source lang="xml">
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
          "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="6cm" height="5cm" viewBox="0 0 600 500"
    xmlns="http://www.w3.org/2000/svg">
  <desc>Exemple script01 - invoque une fonction ECMAScript
        a partir d'un evenement onclick </desc>
 
  <!-- Fonction ECMAScript qui change le rayon a chaque clic -->
 
  <script type="text/ecmascript"> <![CDATA[
    function circle_click(evt) {
      var circle = evt.target;
      var currentRadius = circle.getAttribute("r");
      if (currentRadius == 100)
        circle.setAttribute("r", currentRadius*2);
      else
        circle.setAttribute("r", currentRadius*0.5);
    }
  ]]> </script>
 
  <!-- Contour en bleu de l'aire du dessin -->
  <rect x="1" y="1" width="598" height="498" fill="none" stroke="blue"/>
 
  <!-- S'execute a chaque clic -->
  <circle onclick="circle_click(evt)" cx="300" cy="225" r="100"
          fill="red"/>
 
  <text x="300" y="480"
        font-family="Verdana" font-size="32" text-anchor="middle">
    Cliquer sur le cercle change sa taille
  </text>
</svg>
</source>
 
Explications:
 
* L’exemple implémente un switch (assimilable à un interrupteur de lumière)
* On ajoute à la définition cercle un event-handler. Autrement dit: en cliquant sur le cercle (événement = "onclick"), la fonction circle_click ("event handler") va être activée avec l'argument "evt"
 
<circle onclick="circle_click(evt)
        cx="300" cy="225" r="100" fill="red"/>
 
* La fonction ''circle_click'' qu’on a défini va recevoir comme argument un objet événement ( le paramètre '' evt'' )
* L’objet evt sait quel objet a été touché par l’événement. Autrement dit, ''evt.target'' contient l’objet DOM du cercle et on l’assigne à la variable ''circle'' .
 
<source lang="javascript">
function circle_click(evt) {
    var circle = evt.target;
    .....
  }
</source>
 
* Ensuite on demande au cercle son attribut "r" (qui définit le radius)
 
<source lang="javascript">
var currentRadius = circle.''getAttribute("r");
</source>
 
Le code suivant substitue la valeur du "r":
* si le radius == 100, alors on va l’agrandir
* sinon, on va le diminuer
 
<source lang="javascript">
if (currentRadius == 100)
    circle.setAttribute("r", currentRadius*2);
else circle.setAttribute("r", currentRadius*0.5);
</source>
 
facile, non ? (ce genre de choses se fait aussi en X3D, en HTML et en VRML)
 
== Introduction au scripting avec DOM ==
 
=== Placement et initialisation du script ===
 
Le script doit se trouver dans une section CDATA du code SVG, comme en XHTML. La raison est simple: pour que le XML soit bien formée, on peut ne peut pas y inclure des éléments utilisant une autre syntaxe.
 
<source lang="javascript">
<script type="text/ecmascript">
  <![CDATA[
  function circle_click(evt) {
    .....    }
  ]]>
</script>
</source>
 
Un script travaille souvent avec des variables globales et il faut faire de sorte à ce que l’initialisation soit déjà faite lorsque l’utilisateur interagit avec la scène.
 
Voici une façon de faire:
 
<source lang="xml">
<svg
  <script type="text/ecmascript"> <![CDATA[
  // variables globales qu'on utilise dans toutes les fonctions
  var svgdoc, eye_right, ...... ;
 
    function init (big_bang) {
    svgdoc =  big_bang.target.ownerDocument;  // instance of the document
    .....  }
<svg onload="init(evt)">
    <desc> Ici on insère la véritable scène SVG </desc>
  </svg>
</svg>
</source>
 
L’élément svg imbriqué déclenche le script init() lorsqu’il est chargé
 
Une autre façon est la suivante:
 
<source lang="xml">
<svg width="4cm" height="2cm" viewBox="0 0 400 200"
    xmlns="http://www.w3.org/2000/svg"
    onload="DebuterAnimation(evt)" >
 
  <script type="text/ecmascript"><![CDATA[
    function DebuterAnimation(evt) {
      racine = evt.target.ownerDocument;
    }
    function XXX {
    }
  </script>
 
<rect .... />
</svg>
</source>
 
=== Le déboggage ===
 
Si vous le souhaitez vous pouvez utiliser des outils comme [http://edutechwiki.unige.ch/fr/FireBug#Extension_FireBug_Pour_FireFox [FireBug]]  pour débugger votre script. Sinon quelques autres solutions sont présentées ci-dessous.
 
Une solution est de semer partout dans votre code des "alert" qui produisent des fenêtres pop-up lorsque la ligne est exécutée.
 
Au lieu de les copier/coller tout le temps on propose la solution suivante:
* Une variable globale debug qu’on peut mettre à 0, 1 ou 2 selon vos besoin.
* Si par exemple un problème est résolu, on met le "debug level" à 2. Si debug==1 on ne voit plus l’alert.
 
<source lang="javascript">
// set to 0 in production, 1 for things to debug, 2 for things fixed
var debug = 1;
</source>
 
Voici une du code pour produire les "alert"
<source lang="javascript">
if (debug==2) alert ("The document did load nicely, global variables are defined");
// affiche un string plus une représentation d’un objet
if (debug==1) alert ("button =" + currentButton);
</source>
 
... sans ce genre de stratégie vous allez souffrir, car il est facile de mal interpréter les spécifications du DOM ....
 
=== Classes et méthodes SVG DOM et XML DOM 2 ===
 
Une liste complète des classes et méthodes SVG DOM serait monstrueuse, il existe une vingtaine de "chapitres" avec nombreuses classes et méthodes.
 
Tout SVG est répliqué dans DOM (donc y compris animations et interactivité)
 
A titre d’exemple, il y a 13 classes DOM spécifiques à la structure du document SVG: SVGDocument, SVGSVGElement, SVGGElement, SVGDefsElement, SVGDescElement, SVGTitleElement, SVGSymbolElement, SVGUseElement, ....
 
Sinon, SVG implémente DOM2 et nous allons surtout utiliser cela par la suite
 
==== L’interface SVGDocument ====
 
Un objet de type '''SVGDocument''' va exister quand l'élément racine de la hiérarchie du document XML est un élément 'svg' (cela correspond à HTMLDocument)
On peut utiliser cet interface pour chercher des éléments par leur id par exemple.
 
<source lang="javascript">
svgdoc = big_bang.target.ownerDocument; // instance of the document
eye_right = svgdoc.getElementById ("eyeRightId"); // getElementById()
</source>
 
Attention: Si votre SVG est imbriqué dans un autre document XML (XHTML par exemple) cet objet n’existera pas !
 
====L'interface SVGSVGElement====
 
* SVGSVGElement est l'interface pour l'élément 'svg'. Cette interface contient des méthodes utilitaires variées diverses couramment employées, telles que des opérations matricielles et la capacité de contrôler le moment du ré-affichage sur les appareils de rendu visuel....
 
==== Eléments utiles du XML DOM2 ====
 
=====L'interface Document=====
 
L'interface Document représente le document XML entier. Donc soit le document SVG standalone ou encore le document XML ou HTML dans lequel SVG est imbriqué
 
=====L'interface Node=====
 
L'interface Node est le type de données principal pour tous les modèles DOM. Il représente un seul nœud dans l'arbre du document
* Le plupart des classes implémentent la plupart des méthodes (cela veut dire que pour manipuler un élément SVG, vous pouvez utiliser des méthodes "Node").
* Ainsi, les attributs '' nodeName'' , '' nodeValue'' et '' attributes'' forment un mécanisme pour obtenir une information sur un nœud, sans devoir faire appel à des interfaces dérivées spécifiques. (voir exemple suivant)
* Sinon, il existe plein de méthodes pour connaître et manipuler les enfants.
=====L'interface Element=====
 
* L'interface Element représente un élément dans un document XML. Les éléments peuvent avoir des attributs associés.
 
===== getAttribute ("string")=====
 
Ramène une valeur d'attribut par son nom
 
element = ... // objet qui représente un élément
element.''getAttribute("r")''
 
===== setAttribute ("name", "value")=====
 
Ajoute un nouvel attribut. Si un attribut avec ce nom est déjà présent dans l'élément, sa valeur est changée pour celle du paramètre value. Exemples d'application :
element.setAttribute ("fill", "yellow");
element.setAttribute ("font-weight","bold");
element.setAttribute ("stroke-width", "25.5px");
 
=====getElementById ("id")=====
 
Retourne l’élément dont l'ID est donné par l'attribut elementId. Si un tel élément n'existe pas, cela retourne null.
 
Il s'agit ici d'une méthode très importante pour réaliser facilement des animations, car elle vous permet d’identifier facilement n’importe quel élément enfant qui a un identificateur (attribut "id").
eye_right = svgdoc.getElementById("eyeRightId");
 
======L’interface Event======
 
permet de fournir des informations sur un événement, par ex. l’attribut target
 
function infos(evt) {
var element = '' evt.target'' ; .... }
<circle '' onclick="infos(evt)"'' .... />
 
====Exemple DOM alert====
 
Montre quelques méthodes DOM simples
* http://tecfa.unige.ch/guides/svg/ex/svg-dom/dom-alert.svg'' (animation)
* http://tecfa.unige.ch/guides/svg/ex/svg-dom/
 
<source lang="xml">
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
          "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="7cm" height="6cm" viewBox="0 0 600 500"
    xmlns="http://www.w3.org/2000/svg">
  <desc>Exemple qui affiche des alert suite à un evenement onclick
  </desc>
 
  <script type="text/ecmascript"> <![CDATA[
    function infos(evt) {
      var element = evt.target;
      el_name = element.nodeName;
      el_attributes = element.attributes;
      circle_size = element.getAttribute("r")
      display_text = "Element name = " + el_name;
      display_text += " \nAttributes = " + el_attributes;
      display_text += " \nCircle size = " + circle_size;
      alert(display_text);
      element.setAttribute("fill", "yellow");
    }
  ]]> </script>
 
  <!-- S'execute a chaque clic -->
  <circle onclick="infos(evt)" cx="300" cy="225" r="100" fill="red"/>
 
  <text x="300" y="480"
        font-family="Verdana" font-size="32" text-anchor="middle">
    Cliquer sur le cercle et lire le popup ...
  </text>
</svg>
</source>
=== Gestionnaire d'événements SVG ===
 
Voici quelques-uns des plus importants gestionnaires d'événements SVG: (Pour une liste complète, voir: [http://www.w3.org/TR/SVG11/interact.html#SVGEvents W3C-SPEC])
 
* '''onmouseover''': exécuté lorsque le curseur se déplace au-dessus d'un objet
* '''onmouseout''':  exécuté lorsque le curseur quitte un objet
* '''onmousemove''': exécuté lorsque le curseur se déplace sur un objet
* '''onclick''': exécuté quand l'utilisateur clique sur un objet
* '''onload''': déclenché lorsqu'un fichier SVG a été chargé
* '''onzoom''': déclenché après que l'utilisateur change le niveau du zoom, soit par une interaction directe de l'utilisateur ou de tout changement à la propriété currentScale disponible sur l'interface SVGSVGElement.
* '''onscroll''': déclenché lorsque l'utilisateur fait défiler un svg graphiques
* '''onresize''': déclenché lorsqu'un document a été redimensionnée
 
=== Exemple DOM simple===
 
* http://tecfa.unige.ch/guides/svg/ex/svg-dom/omme-dom1.svg'' (animation)
* http://tecfa.unige.ch/guides/svg/ex/svg-dom/omme.svg'' (dessin de départ)
* http://tecfa.unige.ch/guides/svg/ex/svg-dom/''
 
<source lang="xml">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
          "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created with Inkscape (http://www.inkscape.org/)
Daniel Schneider @ tecfa.unige.ch (DKS)
March 2005
Modified some Inkscape output: missing doctype, opacity
Added script and events
-->
<svg
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink">
  height="600.00000" width="800.00000"
  y="0.00000000" x="0.00000000"
  version="1.0">
 
  <title>Simple Interactivity with Dom</title>
  <desc>This is a simple SVG-DOM demo. Clicking on buttons will change the face, i.e. modifiy XML/SMIL attributes. The drawing was made (quickly) with Inkscape. I did not try to change CSS styles, so I made a few manual changes to the original static SVG which is in file omme.svg</desc>
  <defs>
  <script type="text/ecmascript">
 
  <![CDATA[
 
  // variables globales qu'on utilise dans les 2 fonctions
  var svgdoc, eye_right, eye_left, mouth_angry, mouth_happy, mouth_sad ;
 
  // set to 0 in production, 1 for things to debug, 2 for things fixed
  // var debug = 2;
  var debug = 1;
 
  function init (big_bang) {
    svgdoc =    big_bang.target.ownerDocument;          // instance of the document
    eye_right = svgdoc.getElementById("eyeRightId");    // getElementById()
    eye_left =  svgdoc.getElementById("eyeLeftId"); 
    if (debug==2) alert("eyeLeftId = " + eye_left);
    mouth_angry = svgdoc.getElementById("angry"); 
    mouth_happy = svgdoc.getElementById("path1363"); 
    mouth_sad  = svgdoc.getElementById("path2168"); 
    if (debug==2) alert ("The document did load nicely, global variables are defined");
  }
 
    function face_change(eye_color, emotion) {
    eye_left.setAttribute("fill", eye_color);
    eye_right.setAttribute("fill", eye_color);
    if (debug==2) alert ("mouth emotion =" + emotion);
    switch (emotion) {
    case mouth_angry:
mouth_angry.setAttribute ("visibility","visible");
mouth_happy.setAttribute ("visibility","hidden");
mouth_sad.setAttribute ("visibility","hidden");
break;
    case mouth_happy:
mouth_angry.setAttribute ("visibility","hidden");
mouth_happy.setAttribute ("visibility","visible");
mouth_sad.setAttribute ("visibility","hidden");
break;
    case mouth_sad:
mouth_angry.setAttribute ("visibility","hidden");
mouth_happy.setAttribute ("visibility","hidden");
mouth_sad.setAttribute ("visibility","visible");
break;
    }
    }
 
    function button_click(evt) {
      var button = evt.target;
      var currentButton = button.getAttribute("id");
      if (debug==2) alert ("button =" + currentButton);
      if (debug==2) alert ("eye_left,style = " + eye_left.getAttribute('style') + " fill = " + eye_left.getAttribute('fill') );
      if (currentButton == "blue_button")  {
        face_change ("blue", mouth_happy );
        }
      else if (currentButton == "rect1333") {
        face_change ("red", mouth_angry);
        }
      else {
        face_change ("black", mouth_sad);
        }
    }
 
  ]]> </script></defs>
 
 
  <desc>This inner svg is some hack: onload will not work with the top-level svg. We do need init to work in order to get at the document root (this is not like normal XML it seems).</desc>
 
  <svg onload="init(evt)">
 
    <desc>Below is the definition of the head, eyes and mouth </desc>
 
  <rect
    id="rect1303"
    style="fill:#edca5e;fill-opacity:1.0000000;fill-rule:nonzero;stroke:#000000;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
    y="314.00000"
    x="177.37018"
    height="188.00000"
    width="122.00000" />
 
  <path
    id="path1301"
    style="fill:#edca5e;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    transform="translate(-284.0000,-122.0000)"
    d="M 676.00000,319.00000 C 676.00000,400.18586 606.60414,466.00000 521.00000,466.00000 C 435.39586,466.00000 366.00000,400.18586 366.00000,319.00000 C 366.00000,237.81414 435.39586,172.00000 521.00000,172.00000 C 606.60414,172.00000 676.00000,237.81414 676.00000,319.00000 L 676.00000,319.00000 z " />
 
  <desc>Below we have the 2 eyes. Both are deep green for starters.</desc>
 
  <path
    id="eyeLeftId"
    fill = "#00ff00"
    style="fill-opacity:1;stroke:#000000;stroke-width:2.0000000;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    transform="translate(-284.0000,-122.0000)"
    d="M 492.00000,311.00000 C 492.00000,330.32997 475.43454,346.00000 455.00000,346.00000 C 434.56546,346.00000 418.00000,330.32997 418.00000,311.00000 C 418.00000,291.67003 434.56546,276.00000 455.00000,276.00000 C 475.43454,276.00000 492.00000,291.67003 492.00000,311.00000 L 492.00000,311.00000 z " />
 
  <path
    id="eyeRightId"
    fill = "#00ff00"
    style="fill-opacity:0.75000000;stroke:#000000;stroke-width:2.0000000;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    transform="translate(-153.0000,-125.0000)"
    d="M 492.00000,311.00000 C 492.00000,330.32997 475.43454,346.00000 455.00000,346.00000 C 434.56546,346.00000 418.00000,330.32997 418.00000,311.00000 C 418.00000,291.67003 434.56546,276.00000 455.00000,276.00000 C 475.43454,276.00000 492.00000,291.67003 492.00000,311.00000 L 492.00000,311.00000 z " />
 
    <desc>Below we have 3 mouthes</desc>
 
  <path
    id="path1363"
    visibility="visibile"
    style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:14.173228;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    d="M 154.14365,255.24862 C 154.14365,255.24862 169.06078,300.00000 240.33150,298.34255 C 311.60221,296.68509 326.51934,246.96133 326.51934,246.96133" />
  <path
    id="path2168"
    visibility="hidden"
    style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:14.173228;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    d="M 150.82873,294.22136 C 150.82873,294.22136 165.74586,249.46998 237.01658,251.12743 C 308.28729,252.78489 323.20442,302.50865 323.20442,302.50865" />
  <path
    id="angry"
    visibility="hidden"
    style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:14.173228;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    d="M 139.22652,280.11050 L 339.77901,278.45304" />
 
    <desc>Below are the 3 buttons to the right </desc>
 
  <rect
    onclick="button_click(evt)"
    id="rect1333"
    style="fill:#ff0000;fill-opacity:1.0000000;stroke-width:14.173228;stroke-miterlimit:4.0000000"
    y="197.23756"
    x="474.03317"
    height="79.558014"
    width="77.900551" />
  <rect
    onclick="button_click(evt)"
    id="rect1335"
    style="stroke-width:14.173228;stroke-miterlimit:4.0000000"
    y="416.02209"
    x="474.03317"
    height="79.558014"
    width="77.900551" />
  <rect
    onclick="button_click(evt)"
    id="blue_button"
    style="fill:#0000ff;fill-opacity:1.0000000;stroke-width:14.173228;stroke-miterlimit:4.0000000"
    y="306.62982"
    x="474.03317"
    height="79.558014"
    width="77.900551" />
</svg>
</svg>
</source>
 
'''Note sur la fabrication du dessin''':
 
Le dessin a été fait avec le logiciel InkScape (qui est un très bon logiciel gratuit qui produit du SVG statique), mais il ne fait pas (encore) du dynamique
 
Il fallait faire 2-3 retouches:
* remplacer des éléments CSS par des attributs XML (je ne sais pas comment accéder facilement à un élément CSS dans une longe chaîne via le DOM)
* changer quelques valeurs (opacité, couleurs)
* corriger qq. bugs mineurs
* changer qq. id pour des raisons pédagogiques
* Il faut identifier les id de certains éléments
* Trop souvent ce type de logiciel produit des path (au lieu d’une cercle par ex.), donc on ne s’y retrouve pas dans le code.
* Toutefois, on cliquant sur l’objet dans l’éditeur on peut le voir dans l’arbre DOM dans l’éditeur XML.<br /> Il suffit de noter l’id sur une feuille de papier ou encore de le changer en une valeur intelligible.
 
Cet exemple:
* implémente des boutons (à droite) qui changent l’émotion du visage.
* les yeux changent de couleur (on aurait pu faire cela sans DOM)
* différentes "bouches" sont cachées/montrées et pour cela il faut un script à mon avis.
 
''' Voici quelques éléments du dessin SVG''':
 
* Voici l’oeil gauche produit par Inkscape, vert au départ.
 
<path
    id="eyeLeftId" fill = "#00ff00"
    style="fill-opacity:1;stroke:#000000;stroke-width:2.0000000;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    transform="translate(-284.0000,-122.0000)"
    d="M 492.00000,311.00000 C 492.00000,330.32997 475.43454,346.00000 455.00000,346.00000 C 434.56546,346.00000 418.00000,330.32997 418.00000,311.00000 C 418.00000,291.67003 434.56546,276.00000 455.00000,276.00000 C 475.43454,276.00000 492.00000,291.67003 492.00000,311.00000 L 492.00000,311.00000 z " />
 
* Voici la bouche "angry", une simple ligne (cachée au départ)
 
<path
  id="angry" visibility="hidden"
  style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:14.173228;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
  d="M 139.22652,280.11050 L 339.77901,278.45304" />
 
* Voici le bouton (rouge) qui déclenche le visage "angry"
 
<rect
    onclick="button_click(evt)"
    id="rect1333"
    style="fill:#ff0000;fill-opacity:1;stroke-width:14.173228"
    y="197.23756"
    x="474.03317"
    height="79.558014"
    width="77.900551" />
 
'''Les event handlers'''
 
Lorsque l’utilisateur clique sur un des rectangles à droite, un script se déclenche
  ''onclick="button_click(evt)"''
 
''' Les variables globales du script '''
 
  // variables globales qu'on utilise dans les 2 fonctions
  var svgdoc, eye_right, eye_left, mouth_angry, mouth_happy, mouth_sad ;
 
La fonction d’initialisation sert à trouver les objets qu’on veut animer:
 
function init (big_bang) {
    svgdoc =    big_bang.target.ownerDocument;          // instance of the document
    eye_right = svgdoc.getElementById("eyeRightId");    // getElementById()
    eye_left =  svgdoc.getElementById("eyeLeftId");
    mouth_angry = svgdoc.getElementById("angry");
    mouth_happy = svgdoc.getElementById("path1363");
    mouth_sad  = svgdoc.getElementById("path2168");
    }
 
La fonction button_click qui gère les clics de l’utilisateur sur les rectangles:
 
function button_click(evt) {
  var button = evt.target;
  var currentButton = button.getAttribute("id");
    if (currentButton == "blue_button")  {
      face_change ("blue", mouth_happy );
      }
  else if (currentButton == "rect1333") {
      face_change ("red", mouth_angry);
      }
  else {
      face_change ("black", mouth_sad);
      }  }
 
La fonction button_click reçoit un objet informatique (le paramètre "evt") qui contient toutes les informations sur le click (et surtout l’objet sur lequel on a cliqué) et qu’on va sauver dans une variable "button" ici.
 
var button = ''evt.target;''
 
la propriété "target" de l’objet "evt" référence l’objet sur lequel on a cliqué
 
var currentButton = button.getAttribute("id");
 
Ensuite on identifie le nom du bouton sur lequel on a cliqué (on lui demande son "id"). En fonction de l’id du bouton (donc 1 des 3 rectangles) on appelle une fonction face_change qui va transformer le visage.
 
face_change ("black", mouth_sad);
 
Le premier argument donné est une couleur de cheveux (blue, red ou black).
Le 2ème est une variable associée à l’objet qu’il faut utiliser pour dessiner la bouche
 
La fonction face_change:
 
<source lang="javascript">
function face_change(eye_color, emotion) {
  eye_left.setAttribute("fill", eye_color);
  eye_right.setAttribute("fill", eye_color);
  if (debug==2) alert ("mouth emotion =" + emotion);
  switch (emotion) {
    case mouth_angry:
    mouth_angry.setAttribute ("visibility","visible");
    mouth_happy.setAttribute ("visibility","hidden");
    mouth_sad.setAttribute ("visibility","hidden");
    break;
    case mouth_happy:
    mouth_angry.setAttribute ("visibility","hidden");
    mouth_happy.setAttribute ("visibility","visible");
    mouth_sad.setAttribute ("visibility","hidden");
    break;
    case mouth_sad:
    mouth_angry.setAttribute ("visibility","hidden");
    mouth_happy.setAttribute ("visibility","hidden");
    mouth_sad.setAttribute ("visibility","visible");
    break;
    }
</source>
 
L’instruction switch nous permet en fonction de l’objet "emotion/bouche" transmise de rendre cet objet visible et les autres invisibles.
 
Ceci dit, au lieu de rendre visible/invisible un objet on aurait aussi pu l’enlever/ajouter de la scène. Avec une balise "<g>" on peut regrouper des enfants (dessin de la "bouche"). Ensuite, avec le DOM, on peut en ajouter ou enlever
 
== Animation avec le DOM ==
 
Exemple 5-1: Exemple dom01 de la spécification française
 
* http://www.yoyodesign.org/doc/w3c/svg1/animate.html (home de la traduction française de la spécification SVG 1.0)
* http://tecfa.unige.ch/guides/svg/ex/svg-w3c/dom01.svg
* http://tecfa.unige.ch/guides/svg/ex/svg-w3c/
 
A notre avis, il faut surtout utiliser cette fonctionnalité (au lieu des balises animation de SVG):
* lorsque certaines types d’animation et d’interpolation n’existent pas, par exemple un rectangle qui se promène sur l’écran au hasard (on ne connaît pas son chemin)
* lorsqu’on utilise un navigateur de type Firefox qui n’implémente pas encore les animations.
 
==Bibliothèques JavaScript==
 
Une autre possibilité est de créer, animer et/ou manipuler les éléments SVG à l'aide d'une bibliothèque [[JavaScript]]. Ces bibliothèques mettent à disposition une interface de programmation (API) qui permet d'interagir avec les éléments SVG. Voici deux exemples de bibliothèques :
 
* [http://raphaeljs.com/ Raphaël] : bibliothèque qui est compatible avec les navigateurs plus anciens
* [http://snapsvg.io/ Snap SVG] : bibliothèque plus moderne, mais compatible avec des versions plus récentes des navigateurs


[[Category: SVG]]
[[Category: SVG]]

Dernière version du 22 août 2016 à 18:49

Cet article est une ébauche à compléter. Une ébauche est une entrée ayant un contenu (très) maigre et qui a donc besoin d'un auteur.


Avertissement: Il faudrait complètement revoir ce texte. Il a été écrit il y a longtemps quand les navigateurs implémentaient mal SVG - Daniel K. Schneider 26 mars 2011 à 15:55 (CET)

Consultez plutôt:

Introduction

Créer des interactions et animations avec le DOM est plus difficile que SVG dynamique avec SMIL, mais on peut faire ce qu’on veut selon le principe que tous les attributs du DOM sont animables.

Certains types d’interactions peuvent se programmer sans savoir grand chose de la programmation DOM (c’est pareil pour le traitement de formulaires HTML avec Javascript ...)

Etat de la technologie début 2011. SVG stand-alone ou SVG dans XHTML:

  • marche avec Firefox, Opéra, Chrome, Safari (?)
  • marchera avec IE9 Béta (?)

Pour HTML 5, consultez Inline SVG in HTML5.

  • Chrome 10 ou 11 semblent le mieux marcher

Voici le principe:

  • Dans l’élément qui doit déclencher un script on place un attribut d’événement, par exemple onclick (Voir tutoriel SVG dynamique avec SMIL)
  • Cet attribut doit définir un fonction avec un argument
    • le nom d’une fonction ECMAScript, par ex: circle_click et qui sera appelée
    • un argument avec lequel sera transmis un objet événement, par ex. evt

Un objet événement est dynamiquement crée par le client. Il contient des informations variées, par exemple où l’événement a été déclenché etc.

  function circle_click(evt) {
    var circle = evt.target;
    var currentRadius = circle.getAttribute("r");
  }
<circle onclick="circle_click(evt)" cx="300" cy="225" r="100" fill="red"/>

Exemple : Simple switch avec DOM

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" 
          "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="6cm" height="5cm" viewBox="0 0 600 500"
     xmlns="http://www.w3.org/2000/svg">
  <desc>Exemple script01 - invoque une fonction ECMAScript 
        a partir d'un evenement onclick </desc>

  <!-- Fonction ECMAScript qui change le rayon a chaque clic -->

  <script type="text/ecmascript"> <![CDATA[
    function circle_click(evt) {
      var circle = evt.target;
      var currentRadius = circle.getAttribute("r");
      if (currentRadius == 100)
        circle.setAttribute("r", currentRadius*2);
      else
        circle.setAttribute("r", currentRadius*0.5);
    }
  ]]> </script>

  <!-- Contour en bleu de l'aire du dessin -->
  <rect x="1" y="1" width="598" height="498" fill="none" stroke="blue"/>

  <!-- S'execute a chaque clic -->
  <circle onclick="circle_click(evt)" cx="300" cy="225" r="100"
          fill="red"/>

  <text x="300" y="480" 
        font-family="Verdana" font-size="32" text-anchor="middle">
    Cliquer sur le cercle change sa taille
  </text>
</svg>

Explications:

  • L’exemple implémente un switch (assimilable à un interrupteur de lumière)
  • On ajoute à la définition cercle un event-handler. Autrement dit: en cliquant sur le cercle (événement = "onclick"), la fonction circle_click ("event handler") va être activée avec l'argument "evt"
<circle onclick="circle_click(evt)
        cx="300" cy="225" r="100" fill="red"/>
  • La fonction circle_click qu’on a défini va recevoir comme argument un objet événement ( le paramètre evt )
  • L’objet evt sait quel objet a été touché par l’événement. Autrement dit, evt.target contient l’objet DOM du cercle et on l’assigne à la variable circle .
 function circle_click(evt) {
    var circle = evt.target;
    .....
  }
  • Ensuite on demande au cercle son attribut "r" (qui définit le radius)
 var currentRadius = circle.''getAttribute("r");

Le code suivant substitue la valeur du "r":

  • si le radius == 100, alors on va l’agrandir
  • sinon, on va le diminuer
if (currentRadius == 100)
    circle.setAttribute("r", currentRadius*2);
else circle.setAttribute("r", currentRadius*0.5);

facile, non ? (ce genre de choses se fait aussi en X3D, en HTML et en VRML)

Introduction au scripting avec DOM

Placement et initialisation du script

Le script doit se trouver dans une section CDATA du code SVG, comme en XHTML. La raison est simple: pour que le XML soit bien formée, on peut ne peut pas y inclure des éléments utilisant une autre syntaxe.

<script type="text/ecmascript"> 
  <![CDATA[
  function circle_click(evt) {
    .....    }
  ]]> 
</script>

Un script travaille souvent avec des variables globales et il faut faire de sorte à ce que l’initialisation soit déjà faite lorsque l’utilisateur interagit avec la scène.

Voici une façon de faire:

<svg 
  <script type="text/ecmascript"> <![CDATA[
  // variables globales qu'on utilise dans toutes les fonctions
  var svgdoc, eye_right, ...... ;

    function init (big_bang) {
    svgdoc =  big_bang.target.ownerDocument;  // instance of the document
     .....  }
<svg onload="init(evt)">
    <desc> Ici on insère la véritable scène SVG </desc>
   </svg>
</svg>

L’élément svg imbriqué déclenche le script init() lorsqu’il est chargé

Une autre façon est la suivante:

<svg width="4cm" height="2cm" viewBox="0 0 400 200"
     xmlns="http://www.w3.org/2000/svg"
     onload="DebuterAnimation(evt)" >

  <script type="text/ecmascript"><![CDATA[
    function DebuterAnimation(evt) {
      racine = evt.target.ownerDocument;
    }
    function XXX {
    }
  </script>

 <rect .... />
</svg>

Le déboggage

Si vous le souhaitez vous pouvez utiliser des outils comme [FireBug] pour débugger votre script. Sinon quelques autres solutions sont présentées ci-dessous.

Une solution est de semer partout dans votre code des "alert" qui produisent des fenêtres pop-up lorsque la ligne est exécutée.

Au lieu de les copier/coller tout le temps on propose la solution suivante:

  • Une variable globale debug qu’on peut mettre à 0, 1 ou 2 selon vos besoin.
  • Si par exemple un problème est résolu, on met le "debug level" à 2. Si debug==1 on ne voit plus l’alert.
// set to 0 in production, 1 for things to debug, 2 for things fixed
var debug = 1;

Voici une du code pour produire les "alert"

 if (debug==2) alert ("The document did load nicely, global variables are defined");
 // affiche un string plus une représentation d’un objet
 if (debug==1) alert ("button =" + currentButton);

... sans ce genre de stratégie vous allez souffrir, car il est facile de mal interpréter les spécifications du DOM ....

Classes et méthodes SVG DOM et XML DOM 2

Une liste complète des classes et méthodes SVG DOM serait monstrueuse, il existe une vingtaine de "chapitres" avec nombreuses classes et méthodes.

Tout SVG est répliqué dans DOM (donc y compris animations et interactivité)

A titre d’exemple, il y a 13 classes DOM spécifiques à la structure du document SVG: SVGDocument, SVGSVGElement, SVGGElement, SVGDefsElement, SVGDescElement, SVGTitleElement, SVGSymbolElement, SVGUseElement, ....

Sinon, SVG implémente DOM2 et nous allons surtout utiliser cela par la suite

L’interface SVGDocument

Un objet de type SVGDocument va exister quand l'élément racine de la hiérarchie du document XML est un élément 'svg' (cela correspond à HTMLDocument) On peut utiliser cet interface pour chercher des éléments par leur id par exemple.

 svgdoc = big_bang.target.ownerDocument; // instance of the document
 eye_right = svgdoc.getElementById ("eyeRightId"); // getElementById()

Attention: Si votre SVG est imbriqué dans un autre document XML (XHTML par exemple) cet objet n’existera pas !

L'interface SVGSVGElement

  • SVGSVGElement est l'interface pour l'élément 'svg'. Cette interface contient des méthodes utilitaires variées diverses couramment employées, telles que des opérations matricielles et la capacité de contrôler le moment du ré-affichage sur les appareils de rendu visuel....

Eléments utiles du XML DOM2

L'interface Document

L'interface Document représente le document XML entier. Donc soit le document SVG standalone ou encore le document XML ou HTML dans lequel SVG est imbriqué

L'interface Node

L'interface Node est le type de données principal pour tous les modèles DOM. Il représente un seul nœud dans l'arbre du document

  • Le plupart des classes implémentent la plupart des méthodes (cela veut dire que pour manipuler un élément SVG, vous pouvez utiliser des méthodes "Node").
  • Ainsi, les attributs nodeName , nodeValue et attributes forment un mécanisme pour obtenir une information sur un nœud, sans devoir faire appel à des interfaces dérivées spécifiques. (voir exemple suivant)
  • Sinon, il existe plein de méthodes pour connaître et manipuler les enfants.
L'interface Element
  • L'interface Element représente un élément dans un document XML. Les éléments peuvent avoir des attributs associés.
getAttribute ("string")

Ramène une valeur d'attribut par son nom

element = ... // objet qui représente un élément
element.getAttribute("r")
setAttribute ("name", "value")

Ajoute un nouvel attribut. Si un attribut avec ce nom est déjà présent dans l'élément, sa valeur est changée pour celle du paramètre value. Exemples d'application :

element.setAttribute ("fill", "yellow");
element.setAttribute ("font-weight","bold");
element.setAttribute ("stroke-width", "25.5px");
getElementById ("id")

Retourne l’élément dont l'ID est donné par l'attribut elementId. Si un tel élément n'existe pas, cela retourne null.

Il s'agit ici d'une méthode très importante pour réaliser facilement des animations, car elle vous permet d’identifier facilement n’importe quel élément enfant qui a un identificateur (attribut "id").

eye_right = svgdoc.getElementById("eyeRightId");
L’interface Event

permet de fournir des informations sur un événement, par ex. l’attribut target

function infos(evt) {
var element =  evt.target ; .... }

<circle onclick="infos(evt)" .... />

Exemple DOM alert

Montre quelques méthodes DOM simples

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" 
          "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="7cm" height="6cm" viewBox="0 0 600 500"
     xmlns="http://www.w3.org/2000/svg">
  <desc>Exemple qui affiche des alert suite à un evenement onclick
  </desc>

  <script type="text/ecmascript"> <![CDATA[
    function infos(evt) {
      var element = evt.target;
      el_name = element.nodeName;
      el_attributes = element.attributes;
      circle_size = element.getAttribute("r")
      display_text = "Element name = " + el_name;
      display_text += " \nAttributes = " + el_attributes;
      display_text += " \nCircle size = " + circle_size;
      alert(display_text);
      element.setAttribute("fill", "yellow");
    }
  ]]> </script>

  <!-- S'execute a chaque clic -->
  <circle onclick="infos(evt)" cx="300" cy="225" r="100" fill="red"/>

  <text x="300" y="480" 
        font-family="Verdana" font-size="32" text-anchor="middle">
    Cliquer sur le cercle et lire le popup ...
  </text>
</svg>

Gestionnaire d'événements SVG

Voici quelques-uns des plus importants gestionnaires d'événements SVG: (Pour une liste complète, voir: W3C-SPEC)

  • onmouseover: exécuté lorsque le curseur se déplace au-dessus d'un objet
  • onmouseout: exécuté lorsque le curseur quitte un objet
  • onmousemove: exécuté lorsque le curseur se déplace sur un objet
  • onclick: exécuté quand l'utilisateur clique sur un objet
  • onload: déclenché lorsqu'un fichier SVG a été chargé
  • onzoom: déclenché après que l'utilisateur change le niveau du zoom, soit par une interaction directe de l'utilisateur ou de tout changement à la propriété currentScale disponible sur l'interface SVGSVGElement.
  • onscroll: déclenché lorsque l'utilisateur fait défiler un svg graphiques
  • onresize: déclenché lorsqu'un document a été redimensionnée

Exemple DOM simple

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" 
          "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!-- Created with Inkscape (http://www.inkscape.org/)
 Daniel Schneider @ tecfa.unige.ch (DKS)
 March 2005
 Modified some Inkscape output: missing doctype, opacity
 Added script and events
 -->
<svg
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink">
  height="600.00000" width="800.00000"
  y="0.00000000" x="0.00000000"
  version="1.0">

  <title>Simple Interactivity with Dom</title>
  <desc>This is a simple SVG-DOM demo. Clicking on buttons will change the face, i.e. modifiy XML/SMIL attributes. The drawing was made (quickly) with Inkscape. I did not try to change CSS styles, so I made a few manual changes to the original static SVG which is in file omme.svg</desc>
  <defs>
   <script type="text/ecmascript">

   <![CDATA[

  // variables globales qu'on utilise dans les 2 fonctions
  var svgdoc, eye_right, eye_left, mouth_angry, mouth_happy, mouth_sad ;

  // set to 0 in production, 1 for things to debug, 2 for things fixed
  // var debug = 2;
  var debug = 1;

  function init (big_bang) {
    svgdoc =    big_bang.target.ownerDocument;          // instance of the document
    eye_right = svgdoc.getElementById("eyeRightId");    // getElementById()
    eye_left =  svgdoc.getElementById("eyeLeftId");   
    if (debug==2) alert("eyeLeftId = " + eye_left);
    mouth_angry = svgdoc.getElementById("angry");   
    mouth_happy = svgdoc.getElementById("path1363");   
    mouth_sad   = svgdoc.getElementById("path2168");   
    if (debug==2) alert ("The document did load nicely, global variables are defined");
   }

    function face_change(eye_color, emotion) {
     eye_left.setAttribute("fill", eye_color);
     eye_right.setAttribute("fill", eye_color);
     if (debug==2) alert ("mouth emotion =" + emotion);
     switch (emotion) {
     case mouth_angry:
	 mouth_angry.setAttribute ("visibility","visible");
	 mouth_happy.setAttribute ("visibility","hidden");
	 mouth_sad.setAttribute ("visibility","hidden");
	 break;
     case mouth_happy:
	 mouth_angry.setAttribute ("visibility","hidden");
	 mouth_happy.setAttribute ("visibility","visible");
	 mouth_sad.setAttribute ("visibility","hidden");
	 break;
     case mouth_sad:
	 mouth_angry.setAttribute ("visibility","hidden");
	 mouth_happy.setAttribute ("visibility","hidden");
	 mouth_sad.setAttribute ("visibility","visible");
	 break;
     }
    }

    function button_click(evt) {
      var button = evt.target;
      var currentButton = button.getAttribute("id");
      if (debug==2) alert ("button =" + currentButton);
      if (debug==2) alert ("eye_left,style = " + eye_left.getAttribute('style') + " fill = " + eye_left.getAttribute('fill') );
      if (currentButton == "blue_button")  {
        face_change ("blue", mouth_happy );
        }
      else if (currentButton == "rect1333") {
        face_change ("red", mouth_angry);
        }
      else {
        face_change ("black", mouth_sad);
        }
    }

  ]]> </script></defs>
  

  <desc>This inner svg is some hack: onload will not work with the top-level svg. We do need init to work in order to get at the document root (this is not like normal XML it seems).</desc>

  <svg onload="init(evt)">

    <desc>Below is the definition of the head, eyes and mouth </desc>

  <rect
    id="rect1303"
    style="fill:#edca5e;fill-opacity:1.0000000;fill-rule:nonzero;stroke:#000000;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
    y="314.00000"
    x="177.37018"
    height="188.00000"
    width="122.00000" />

  <path
    id="path1301"
    style="fill:#edca5e;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    transform="translate(-284.0000,-122.0000)"
    d="M 676.00000,319.00000 C 676.00000,400.18586 606.60414,466.00000 521.00000,466.00000 C 435.39586,466.00000 366.00000,400.18586 366.00000,319.00000 C 366.00000,237.81414 435.39586,172.00000 521.00000,172.00000 C 606.60414,172.00000 676.00000,237.81414 676.00000,319.00000 L 676.00000,319.00000 z " />

  <desc>Below we have the 2 eyes. Both are deep green for starters.</desc>

  <path
    id="eyeLeftId"
    fill = "#00ff00"
    style="fill-opacity:1;stroke:#000000;stroke-width:2.0000000;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    transform="translate(-284.0000,-122.0000)"
    d="M 492.00000,311.00000 C 492.00000,330.32997 475.43454,346.00000 455.00000,346.00000 C 434.56546,346.00000 418.00000,330.32997 418.00000,311.00000 C 418.00000,291.67003 434.56546,276.00000 455.00000,276.00000 C 475.43454,276.00000 492.00000,291.67003 492.00000,311.00000 L 492.00000,311.00000 z " />

  <path
    id="eyeRightId"
    fill = "#00ff00"
    style="fill-opacity:0.75000000;stroke:#000000;stroke-width:2.0000000;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    transform="translate(-153.0000,-125.0000)"
    d="M 492.00000,311.00000 C 492.00000,330.32997 475.43454,346.00000 455.00000,346.00000 C 434.56546,346.00000 418.00000,330.32997 418.00000,311.00000 C 418.00000,291.67003 434.56546,276.00000 455.00000,276.00000 C 475.43454,276.00000 492.00000,291.67003 492.00000,311.00000 L 492.00000,311.00000 z " />

    <desc>Below we have 3 mouthes</desc>

  <path
    id="path1363"
    visibility="visibile"
    style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:14.173228;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    d="M 154.14365,255.24862 C 154.14365,255.24862 169.06078,300.00000 240.33150,298.34255 C 311.60221,296.68509 326.51934,246.96133 326.51934,246.96133" />
  <path
    id="path2168"
    visibility="hidden"
    style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:14.173228;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    d="M 150.82873,294.22136 C 150.82873,294.22136 165.74586,249.46998 237.01658,251.12743 C 308.28729,252.78489 323.20442,302.50865 323.20442,302.50865" />
  <path
    id="angry"
    visibility="hidden"
    style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:14.173228;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    d="M 139.22652,280.11050 L 339.77901,278.45304" />

    <desc>Below are the 3 buttons to the right </desc>

  <rect
    onclick="button_click(evt)"
    id="rect1333"
    style="fill:#ff0000;fill-opacity:1.0000000;stroke-width:14.173228;stroke-miterlimit:4.0000000"
    y="197.23756"
    x="474.03317"
    height="79.558014"
    width="77.900551" />
  <rect
    onclick="button_click(evt)"
    id="rect1335"
    style="stroke-width:14.173228;stroke-miterlimit:4.0000000"
    y="416.02209"
    x="474.03317"
    height="79.558014"
    width="77.900551" />
  <rect
    onclick="button_click(evt)"
    id="blue_button"
    style="fill:#0000ff;fill-opacity:1.0000000;stroke-width:14.173228;stroke-miterlimit:4.0000000"
    y="306.62982"
    x="474.03317"
    height="79.558014"
    width="77.900551" />
</svg>
</svg>

Note sur la fabrication du dessin:

Le dessin a été fait avec le logiciel InkScape (qui est un très bon logiciel gratuit qui produit du SVG statique), mais il ne fait pas (encore) du dynamique

Il fallait faire 2-3 retouches:

  • remplacer des éléments CSS par des attributs XML (je ne sais pas comment accéder facilement à un élément CSS dans une longe chaîne via le DOM)
  • changer quelques valeurs (opacité, couleurs)
  • corriger qq. bugs mineurs
  • changer qq. id pour des raisons pédagogiques
  • Il faut identifier les id de certains éléments
  • Trop souvent ce type de logiciel produit des path (au lieu d’une cercle par ex.), donc on ne s’y retrouve pas dans le code.
  • Toutefois, on cliquant sur l’objet dans l’éditeur on peut le voir dans l’arbre DOM dans l’éditeur XML.
    Il suffit de noter l’id sur une feuille de papier ou encore de le changer en une valeur intelligible.

Cet exemple:

  • implémente des boutons (à droite) qui changent l’émotion du visage.
  • les yeux changent de couleur (on aurait pu faire cela sans DOM)
  • différentes "bouches" sont cachées/montrées et pour cela il faut un script à mon avis.

Voici quelques éléments du dessin SVG:

  • Voici l’oeil gauche produit par Inkscape, vert au départ.
<path
    id="eyeLeftId" fill = "#00ff00"
    style="fill-opacity:1;stroke:#000000;stroke-width:2.0000000;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
    transform="translate(-284.0000,-122.0000)"
    d="M 492.00000,311.00000 C 492.00000,330.32997 475.43454,346.00000 455.00000,346.00000 C 434.56546,346.00000 418.00000,330.32997 418.00000,311.00000 C 418.00000,291.67003 434.56546,276.00000 455.00000,276.00000 C 475.43454,276.00000 492.00000,291.67003 492.00000,311.00000 L 492.00000,311.00000 z " />
  • Voici la bouche "angry", une simple ligne (cachée au départ)
<path
 id="angry" visibility="hidden"
 style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-width:14.173228;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000"
 d="M 139.22652,280.11050 L 339.77901,278.45304" />
  • Voici le bouton (rouge) qui déclenche le visage "angry"
<rect
    onclick="button_click(evt)"
    id="rect1333"
    style="fill:#ff0000;fill-opacity:1;stroke-width:14.173228"
    y="197.23756"
    x="474.03317"
    height="79.558014"
    width="77.900551" />

Les event handlers

Lorsque l’utilisateur clique sur un des rectangles à droite, un script se déclenche

  onclick="button_click(evt)"

Les variables globales du script

 // variables globales qu'on utilise dans les 2 fonctions
  var svgdoc, eye_right, eye_left, mouth_angry, mouth_happy, mouth_sad ;

La fonction d’initialisation sert à trouver les objets qu’on veut animer:

function init (big_bang) {
    svgdoc =    big_bang.target.ownerDocument;          // instance of the document
    eye_right = svgdoc.getElementById("eyeRightId");    // getElementById()
    eye_left =  svgdoc.getElementById("eyeLeftId");
    mouth_angry = svgdoc.getElementById("angry");
    mouth_happy = svgdoc.getElementById("path1363");
    mouth_sad   = svgdoc.getElementById("path2168");
    }

La fonction button_click qui gère les clics de l’utilisateur sur les rectangles:

function button_click(evt) {
  var button = evt.target;
  var currentButton = button.getAttribute("id");
   if (currentButton == "blue_button")  {
      face_change ("blue", mouth_happy );
      }
  else if (currentButton == "rect1333") {
     face_change ("red", mouth_angry);
     }
  else {
     face_change ("black", mouth_sad);
     }   }

La fonction button_click reçoit un objet informatique (le paramètre "evt") qui contient toutes les informations sur le click (et surtout l’objet sur lequel on a cliqué) et qu’on va sauver dans une variable "button" ici.

var button = evt.target;

la propriété "target" de l’objet "evt" référence l’objet sur lequel on a cliqué

var currentButton = button.getAttribute("id");

Ensuite on identifie le nom du bouton sur lequel on a cliqué (on lui demande son "id"). En fonction de l’id du bouton (donc 1 des 3 rectangles) on appelle une fonction face_change qui va transformer le visage.

face_change ("black", mouth_sad);

Le premier argument donné est une couleur de cheveux (blue, red ou black). Le 2ème est une variable associée à l’objet qu’il faut utiliser pour dessiner la bouche

La fonction face_change:

function face_change(eye_color, emotion) {
  eye_left.setAttribute("fill", eye_color);
  eye_right.setAttribute("fill", eye_color);
  if (debug==2) alert ("mouth emotion =" + emotion);
  switch (emotion) {
    case mouth_angry:
     mouth_angry.setAttribute ("visibility","visible");
	    mouth_happy.setAttribute ("visibility","hidden");
	    mouth_sad.setAttribute ("visibility","hidden");
	    break;
    case mouth_happy:
	     mouth_angry.setAttribute ("visibility","hidden");
	     mouth_happy.setAttribute ("visibility","visible");
	     mouth_sad.setAttribute ("visibility","hidden");
	     break;
    case mouth_sad:
	     mouth_angry.setAttribute ("visibility","hidden");
	     mouth_happy.setAttribute ("visibility","hidden");
	     mouth_sad.setAttribute ("visibility","visible");
	     break;
     }

L’instruction switch nous permet en fonction de l’objet "emotion/bouche" transmise de rendre cet objet visible et les autres invisibles.

Ceci dit, au lieu de rendre visible/invisible un objet on aurait aussi pu l’enlever/ajouter de la scène. Avec une balise "<g>" on peut regrouper des enfants (dessin de la "bouche"). Ensuite, avec le DOM, on peut en ajouter ou enlever

Animation avec le DOM

Exemple 5-1: Exemple dom01 de la spécification française

A notre avis, il faut surtout utiliser cette fonctionnalité (au lieu des balises animation de SVG):

  • lorsque certaines types d’animation et d’interpolation n’existent pas, par exemple un rectangle qui se promène sur l’écran au hasard (on ne connaît pas son chemin)
  • lorsqu’on utilise un navigateur de type Firefox qui n’implémente pas encore les animations.

Bibliothèques JavaScript

Une autre possibilité est de créer, animer et/ou manipuler les éléments SVG à l'aide d'une bibliothèque JavaScript. Ces bibliothèques mettent à disposition une interface de programmation (API) qui permet d'interagir avec les éléments SVG. Voici deux exemples de bibliothèques :

  • Raphaël : bibliothèque qui est compatible avec les navigateurs plus anciens
  • Snap SVG : bibliothèque plus moderne, mais compatible avec des versions plus récentes des navigateurs