Canvas pour SVG: surface de dessin programmée

Premier pas: Réaliser par programme un paysage à partir de composants SVG pris dans une bibliothèque.

1) Créer une bibliothèque de modèles

Prenons d'abord le cas où la bibliothèque se trouve dans la page. Elle se réaliser avec une balise svg contenant plusieurs figures, dans notre démonstration, un rectangle et un cercle.

<svg id="repository">
<rect id="rect" width="200" height="100" style="fill:rgb(100,150,200);" />
<circle id="circle" cx="300" cy="80" r="60" style="fill:rgb(0,160,0);";/>
</svg>
Bibliothèque de formes

Il serait aussi possible de créer des images de façon dynamique. Le code ci-dessous par exemple crée un cercle que l'on remplit avec la couleur rouge.

var cir = document.createElementNS("http://www.w3.org/2000/svg", "circle");
cir.setAttribute("cx", 40);
cir.setAttribute("cy", 60);
cir.setAttribute("r", 35);
cir.setAttribute("fill", "red");

Une autre solution est d'utiliser des images stockées dans des fichiers SVG, et qui auront été générées par un logiciel comme Inkscape ou SVG-Edit.

On peut intégrer une image SVG de plusieurs façons. Par exemple avec l'attribut xlink de image:

<svg width="96" height="96">
<image xlink:href="code/boat.svg" src="code/boat.png" width="100" height="100"/>
</svg>

L'avantage de cette méthode qui intègre dans une balise svg une balise image, est que l'on peut afficher une image bitmap de remplacement si le navigateur est trop ancien pour supporter SVG. On verra comment utiliser cette balise dans l'article SVG: Voiture sur une route et programmation d'évènement. Une seconde méthode consiste à utiliser la balise object.

<object width="100" height="100" data="code/plane.svg"></object>
<object width="100" height="100" data="code/boat.svg"></object>
<object width="100" height="100" data="code/building.svg"></object>
Bibliothèque de fichiers

Cela convient parfaitement pour charger des images SVG dans la page, et les afficher. On peut encore recourir à une troisième méthode, les iframes:

<div style="display:none">
<iframe id="iplane" width="100" height="100" src="code/plane.svg"></iframe>
<iframe id="iboat" width="100" height="100" src="code/boat.svg"></iframe>
<iframe id="ibuilding" width="100" height="100" src="code/building.svg"></iframe> </div>

Dans la balise object, l'image est redimensionnée, ce qui n'est pas le case pour l'iframe. On place les iframes dans un calque qui n'est pas affiché car on ne veut qu'utiliser leur contenu et non pas l'afficher, aussi ce n'est pas important ici.

Utiliser le contenu des iframes ou object est très facile comme on va le voir.

2) Réaliser une composition

On créer une surface comme on le fait pour Canvas, mais le conteneur est ici une balise svg:

<svg class="surface" id="surface"></svg>

Les composants sont insérés avec la méthode appendChild du DOM:

var surface = document.getElementById("surface");

var rect = document.getElementById("rect");
var clonerect = rect.cloneNode();
clonerect.setAttribute("x", 100);
clonerect.setAttribute("y", 200);
surface.appendChild(clonerect);
var circle = document.getElementById("circle");
var clonecir = circle.cloneNode();
surface.appendChild(clonecir);

On crée un clone des deux figures pour éviter qu'elle ne disparaissent dans la galerie ci-dessus, car appendChild autrement déplacerait la balise dans le DOM de la galerie vers la balise svg.

On ajoute aussi la figure créée dynamiquement:

surface.appendChild(cir);

Cela donne la composition ci-dessous:

Nous allons maintenant créer une composition à partir d'objet stockés dans des fichiers svg. Une nouvelle surface est créée dont l'ID est "city":

<svg class="surface" id="city"></svg>

La fonction getSVG est définie pour extraire le contenu du fichier une fois chargé dans l'iframe.

function getSVG(iframeID, objID)
{
var ifr = document.getElementById(iframeID);
var graphics = ifr.contentWindow || ifr.contentDocument;
return graphics.document.getElementById(objID);
}

Les arguments sont l'ID de l'iframe dans laquelle on charge le fichier, et l'ID du contenu dans le fichier svg. Le fichier contient une balise <g> avec une ID pour identifier le contenu a insérer dans notre surface, par exemple:

<g id="boat"> ... </g>

Il est important d'afficher le contenu svg après chargement complet de la page, aussi on recourt à la propriété onload de window.

Avant d'inclure les composants, on leur donne une couleur et position dans la surface. Puisque la balise <g> n'a pas d'attributs x et y, on emploie à la place la propriété translate.

function displaySVG() 
{
var city = document.getElementById("city");
var building = getSVG("ibuilding", "building");
building.setAttribute("transform", "translate(360,126)");
building.setAttribute("fill", "#ccc");
city.appendChild(building);
var boat = getSVG("iboat", "boat");
boat.setAttribute("fill", "#090");
boat.setAttribute("transform", "translate(10,247)");
city.appendChild(boat);
var plane = getSVG("iplane", "plane");
plane.setAttribute("fill", "blue");
plane.setAttribute("width", "120");
plane.setAttribute("height", "50");
plane.setAttribute("transform", "translate(60,30)");
city.appendChild(plane);
}
window.onload=displaySVG;

Voici le tableau final:

On peut aller plus loin, ajouter des animations, permettre à l'utilisateur d'interagir avec le tableau, c'est l'avantage du format svg... ce sera l'objet d'articles ultérieurs... La démonstration d'alunissage utilise les principes expliqués dans cette page.