TwinXeon by Renaudet
/Accueil/Tous les articles
Création de composants graphiques en HTML 5
Ce petit tutoriel sans prétention montre comment créer un composant graphique en HTML 5. L'article est directement issu de mes premiers pas dans l'apprentissage des arcanes d'HTML 5 qui, je dois le dire, m'ont ouvert des perspectives très intéressantes.

Le but de ce tutoriel est de créer un composant Vu-mètre permettant un affichage dynamique et original d'une valeur numérique bornée :



Pré-requis de ce tutoriel : de bonnes connaissances du JavaScript, un peu de maths (trigonométrie) et XHTML

Les bases du graphisme en HTML 5 : les canvas

J'expliquais déjà dans cet article comment générer un graphique côté browser à partir d'un flux de données brutes. Nous allons reprendre cette démarche pas à pas, et tout d'abord, découvrons la balise <canvas> :

<canvas id="comp1" width="200" height="50">
   Votre navigateur ne supporte pas la balise &lt;canvas&gt;
</canvas>

Rien de bien compliqué. Cette balise déclare un canvas de 200 pixels de large sur 50 pixels de haut et lui donne l'identifiant comp1. Si le navigateur ne prend pas en charge la balise <canvas>, alors le texte Votre navigateur ne supporte pas la balise<canvas>  s'affiche à la place.

Jusque là, tout ce que nous avons fait, c'est réserver de la place pour notre graphique. Autrement dit, sans rien d'autre, notre balise canvas ne sert à rien. Si nous voulons afficher ne serait-ce qu'un rectangle blanc pour visualiser notre canvas, il faut dessiner ledit rectangle blanc :

<script type="text/javascript">
   function drawVumeter(canvas){
      if (canvas.getContext){
         var ctx = canvas.getContext('2d');
         width = canvas.width;
         height = canvas.height;
         
         ctx.fillStyle = "#ffffff";
         ctx.fillRect(0, 0, width, height);
      }
   }
</script>

Cette fonction javascript peut être appelée depuis un code tel que :

<script type="text/javascript">
   canvas = document.getElementById('comp1');
   drawVumeter(canvas);
</script>
Et voilà. Maintenant, un rectangle blanc s'affiche en lieu et place de notre canvas. Il sera surtout visible si le fond de page n'est pas blanc, bien sûr.

Insertion d'image dans le canvas

Nous pourrions commencer à dessiner notre Vu-mètre en utilisant les primitives graphiques du contexte 2D de notre canvas, mais ce serait un peu fastidieux. Je préfère utiliser une image de fond, ce qui fait gagner du temps et permet un look plus sympa à moindre frais.

Voici cette image de fond :



Avant de dessiner notre Vu-mètre, il faut charger l'image en mémoire, ce qui est très facile :

<script type="text/javascript">
   var vuMeterImg = new Image();
   vuMeterImg.src = '/img/vumeter.gif';
</script>

Ensuite, pour dessiner l'image dans notre canvas, je modifie le code de la fonction drawVumeter comme suit :

<script type="text/javascript">
   function drawVumeter(canvas){
      if (canvas.getContext){
         var ctx = canvas.getContext('2d');
         width = canvas.width;
         height = canvas.height;
         
         ctx.drawImage(vuMeterImg,0,0);
      }
   }
</script>

Mais si vous testez ce code, vous vous apercevrez que l'image ne s'affiche pas. En effet, l'image a toutes les chances de ne pas être encore chargée lorsque le code de la fonction drawVumeter est invoqué la première fois. Pour éviter ce décalage temporel, nous ne commencerons à dessiner notre canvas qu'une fois l'image complètement chargée :

<script type="text/javascript">
   vuMeterImg.onload = function(){
        vumetre = document.getElementById('vumetre');
        drawVumeter(vumetre);
    }
</script>
Et voilà. Nous avons réalisé jusque là l'équivalent d'une balise <IMG> à l'aide d'un canvas, à la différence près que le redimensionnement du canvas aura pour effet de tronquer l'image. C'est pourquoi il nous faudra initialiser notre canvas, pour cet exemple, à 383 x 282 pixels

Tracé de l'aiguille - la question des propriétés

Attaquons maintenant le dessin de l'aiguille de notre Vu-mètre. Il nous faut pour cela une propriété qui représente la grandeur physique mesurée par notre Vu-mètre. Ce peut être une tension, une température, une pression ou tout simplement une valeur numérique quelconque. Appelons-la valeur.

Nous pourrions déclarer une variable globale valeur et utiliser cette variable au sein de notre fonction drawVumeter() lors du dessin de l'aiguille. Mais que se passe-t-il si nous désirons utiliser deux composants Vu-mètre sur une même page ? Il y a bien sûr la solution qui consiste à utiliser une table de correspondance qui associe le canvas à cette variable, mais il y a en fait beaucoup plus simple : utiliser l'orientation Objet déclarative du langage JavaScript. Pour ajouter une propriété valeur à notre canvas, il suffit en effet de.. l'affecter :

<script type="text/javascript">
  vumeter = document.getElementById('comp1');
  vumeter.valeur = 0.0;
</script>
Et voilà. Il est désormais possible de faire varier cette propriété valeur attachée à cette instance spécifique de canvas, et d'y faire référence au sein de notre méthode drawVumeter().

Se pose maintenant la question de faire correspondre une valeur numérique avec une position de l'aiguille sur la cadran de notre Vu-mètre. Observons d'un peu plus près ce cadran :



Pour faire simple, nous prendrons la position extrême gauche comme origine (valeur 0) et la position extrême droite comme valeur maximum (100). Comme le cadran dessine un arc de cercle d'environ PI / 2, nous établirons cette correspondance.

La longueur de l'aiguille (depuis l'origine 192,224 jusqu'à l'arc de cercle noir) est de 148 pixels. Si A est l'angle en radian compté entre la ligne horizontale et l'aiguille, alors la position de l'extrémité de cette dernière est : 191 + (148 x cos(A)) , 224 - (148 x sin(A))

Reste à déterminer cet angle A en radian, étant donné la valeur numérique que l'on souhaite représenter, et sachant que la valeur 0 est assimilable à l'angle 3 x PI / 4. Une règle de trois nous donne (en numérique, pour ne pas avoir à calculer des fractions décimales à chaque fois) : A = 2.3562 - (0.015708) * valeur

Le code source complet :

function drawVumeter(vumeter){
   if (vumeter.getContext){
      var ctx = vumeter.getContext('2d');
      width = vumeter.width;
      height = vumeter.height;
        
      ctx.clearRect(0,0,width,height);
      ctx.drawImage(vuMeterImg,0,0);
    
      ctx.strokeStyle = "#000000";
    
      x = 0.015708*vumeter.valeur;
      cosA = Math.cos(2.3562-x);
      sinA = Math.sin(2.3562-x);  
      deltaX = 148 * cosA;
      deltaY = 148 * sinA;
            
      ctx.beginPath();
      ctx.moveTo(191,224);
      ctx.lineTo(191+deltaX,224-deltaY);
      ctx.stroke();
   }
}


Pour donner plus de réalisme à cette aiguille, on peut ajouter une ombre. Par contre, cette modification entraine l'ajout de quelques lignes de code supplémentaires à l'initialisation du contexte graphique. En effet, sans cela, l'image serait dessinée dans le canvas avec un ombrage en cas d'invocation ultérieure de notre méthode drawVumeter() :

function drawVumeter(vumeter){
   if (vumeter.getContext){
      var ctx = vumeter.getContext('2d');
      width = vumeter.width;
      height = vumeter.height;
    
      ctx.shadowOffsetX = 0;
      ctx.shadowOffsetY = 0;
      ctx.shadowBlur = 0;
 
      ctx.clearRect(0,0,width,height);
      ctx.drawImage(vuMeterImg,0,0);
    
      ctx.strokeStyle = "#000000";
      ctx.shadowOffsetX = -3;
      ctx.shadowOffsetY = 3;
      ctx.shadowBlur = 3;
      ctx.shadowColor = "rgba(0, 0, 0, 0.6)";
    
      x = 0.015708*vumeter.valeur;
      cosA = Math.cos(2.3562-x);
      sinA = Math.sin(2.3562-x);  
      deltaX = 148 * cosA;
      deltaY = 148 * sinA;
            
      ctx.beginPath();
      ctx.moveTo(191,224);
      ctx.lineTo(191+deltaX,224-deltaY);
      ctx.stroke();
   }
}
Voilà pour le côté graphique. Il faut maintenant ajouter un peu de logique pour faire de notre Vu-mètre un vrais composant.

Ajoutez votre commentaire :
  Votre pseudo :
  Votre adresse mail (obligatoire):
  Votre commentaire :
 
Site optimisé pour un affichage en 800x600 sous Firefox 8.x - ©Copyright 2011-2012 by Nicolas Renaudet