Premiers pas en JavaScript
39.1 Graphisme
cuy copyleft
  See You Why?  

 


Test relatif à la validation de formulaire (38.9) | | Librairies et autres ressources pour le graphisme (39.2)

Contenu

 

 

I. Graphisme et JavaScript

Les pages traitant du graphisme sont nombreuses sur le Web : malheureusement, elles n'ont souvent d'intérêt que si elles s'accompagnent de routines déjà écrites et souvent payantes. Nous essaierons ici de trouver les principales fonctions qui permettent les animations graphiques essentiellement sans l'aide de ces bibliothèques de fonctions, puis en en faisant usage.

Une des premières difficultés est que Internet Explorer joue toujours à part (il refuse d'adopter JavaScript et travaille avec son JScript) et que sans cesse, les instructions à donner dépendront du navigateur auquel ces instructions s'adressent. Nous n'irons peut-être pas jusqu'à rebaptiser IE de BB ou badbrowser comme le font certains, mais nous pensons que ces derniers n'ont pas fait un mauvais choix. Nous développerons les aspects suivants du graphisme :

  1. reconnaitre le browser (navigateur, babillard, etc.) utilisé par le visiteur de nos pages ;
  2. reconnaitre la position de la souris sur l'écran ;
  3. déplacer un dessin sur l'écran ;
  4. v ;

Avant de poursuivre, vérifions rapidement ce que nous répond l'appel des propriétés de l'objet navigator appelé sur votre ordinateur :

Ce qui a été affiché par la source suivante, une simple boucle du type 'for... in...' :

<script type="text/javascript">
for (propr in navigator) {
    document.write('<br /><b>navigator.' + propr + '</b> = ' + navigator[propr]);
}
</script>

Le lecteur attentif aura remarqué que la meilleure façon de tester l'OS et le navigateur n'est pas de faire appel aux propriétés de l'objet navigator. Il n'est en effet pas rare qu'un navigateur se cache derrière d'autres noms pour pouvoir exécuter des scripts qui n'ont pas été écrits pour eux. Chez moi, alors que j'utilise essentiellement Google Chrome, je vois aussi apparaitre des mots tels que Gecko, Mozilla, Safari, Netscape dans le descriptif des propriétés de mon navigator affiché ci-dessus... de quoi dérouter la recherche de mon navigateur réel.

La traditionnelle lecture de la propriété userAgent ne convient plus pour détecter quel navigateur ou browser le visiteur de votre page utilise.

 

A. quel navigateur ?

Il existe différents moyens de reconnaitre le navigateur (Internet Explorer, Google Chrome, Mozilla Firefox, Netscape, Opera, Safari ou autre) qu'utilise le visiteur de votre page.

Quel Micro§oft Internet Explorer ?

Nous vous proposons en premier lieu un procédé qui ne permet que de détecter IE et sa version,
d'après http://msdn.microsoft.com/en-us/library/cc817582.aspx.

function getInternetExplorerVersion() {
// Renvoie la version de Windows Internet Explorer ou la valeur -1
// (qui signifie que vous utilisez un autre navigateur).
   var rv = -1;                    // Renvoie la valeur si rien d'autre n'est valable
   if (navigator.appName == 'Microsoft Internet Explorer') {
      var ua = navigator.userAgent;
      var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
      if (re.exec(ua) != null) {
         rv = parseFloat( RegExp.$1 );
      }
   }
   return rv;
}

function checkIEVersion() {
   var msg = "Vous n'employez pas Windows Internet Explorer. Bravo !";
   var ver = getInternetExplorerVersion();
   if ( ver> -1 ) {
      if ( ver>= 8.0 ) {
         msg = "Vous utilisez une version récente 8.0 ou mieux d'Internet Explorer.";
      } else if ( ver == 7.0 ) {
         msg = "Vous utilisez Windows Internet Explorer 7.";
      } else if ( ver == 6.0 ) {
         msg = "Vous employez encore Windows Internet Explorer 6. Songez à mettre à jour";
      } else {
         msg = "Il faut mettre à jour votre vieille version d'Internet Explorer";
      }
   }
   alert( msg );
}

C'est la fonction checkIEVersion() qu'il faut appeler, cette dernière appelle getInternetExplorerVersion() pour retrouver la valeur de la variable ver. Chez vous, l'appel de cette méthode affichera :

 

Quel navigateur ? méthode longue

On pourrait aussi utiliser une fonction, parfois appelée classe, mais plus correctement 'objet' de type 'prototype' en JS, qui extrairait de toutes les propriétés du navigator, celles qui selon les cas seraient utiles. Cette fonction que nous avons appelée DetecteLeNavigateur est bien basée sur la construction des prototypes en JS: nous en donnerons plus de détails sous peu.
Pour ceux qui veulent comprendre avant notre explication, nous nous sommes inspirés de la lecture de https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Details_of_the_Object_Model, qui distingue la construction de type 'classe' en Java de celle de type 'prototype' en JavaScript.

Quant à l'utilisation de cet objet 'prototype' de JS, il faut :

      1. appeler la page d'extension .js qui contient la construction de cette classe ;
        <script type="text/javascript" src="scripts/detectenavigateur.js"></script>
      2. initialiser cette classe ;
        DetecteLeNavigateur.init();
      3. relever les informations utiles et les stocker dans des variables ;
        var nv_navig = DetecteLeNavigateur.browser;
        var nv_vers = DetecteLeNavigateur.version;
        var nv_os = DetecteLeNavigateur.OS;
      4. afficher ou utiliser les éléments relevés.
        Chez vous, on a écrit :
        <button onclick="alert(nv_navig+' * '+nv_vers+' * '+nv_os)">Lancer l'affichage</button>

Ce qui, sur l'ordi du visiteur de votre page donnera :

Quelle fonction marche sur cet ordi ?

Cependant, compte tenu que les navigateurs évoluent, adoptent parfois ce qu'ils refusaient dans une version précédente et vice-versa, c'est une des bonnes pratiques que de ne pas tester le navigateur, mais de préférer tester l'existence de la méthode... si la méthode n'offre pas assez de sécurité quant à son existence.

Ainsi, en écrivant  :
if (history.pushState) {
   // faire ce que l'on veut avec history.pushState
} else {
   // soit le dire autrement,
   // soit envoyer un petit message pour dire qu'on est désolé
}

on ne teste pas si on travaille avec certaines versions d'IE, certaines de FF ou sous Opera... on teste si le navigateur connait l'objet history et sa méthode pushState. Les programmeurs en JS n'emploient pas assez souvent cette option.

Quel navigateur par le test d'une fonction ? méthode courte

Nous l'avons déjà signalé, IE a des fonctions qui lui sont propres et que les autres navigateurs emploient via des autres fonctions quasi équivalents. document.all est une de ces fonctions de IE qui permettent de distinguer IE des autres qui généralement utilisent la fonction document.getElementById('un_id') et, comme d'une part, il n'est pas indispensable de donner un argument pour voir si une méthode fonctionne et que d'autre part IE a finalement, dans ses dernières versions, adopter le DOM (Document Object Model), on devra vérifier que les autres répondent au critère document.getElementById sans accepter le document.all. C'est pourquoi nous emploierons généralement le test suivant pour détecter si nous sommes face au navigateur de Micro§oft ou un autre :

var ie = document.all;
var gb = document.getElementById && !document.all;

Dans la suite de la programmation, si chaque navigateur a besoin de ses propres fonctions ou instructions, on utilisera le test :
if (gb) {
    var fobj= e.target;
} else {
    var fobj= event.srcElement;
}

ou, plus court encore, la condition ternaire :
var fobj = gb ? e.target : event.srcElement;

Quel est l'objet ou élément HTML a été ciblé par un évènement ? IE fait appel à sa propriété srcElement de l'objet event, alors que le DOM recommande la propriété target de l'objet passé en paramètre (e) à la fonction en cours... ce qui explique cette ou ces lignes de code...

De même, on lira :
var topelement = gb ? "HTML" : "BODY";
while (fobj.tagName != topelement && fobj.className != "glissemoi") {
    fobj = gb ? fobj.parentNode : fobj.parentElement;
}

car, en DOM, la propriété tagName renvoie le nom de l'élément courant, souvent spécifié par un id="monNom" dans une balise <span>, <form> ou autre ;
comme JS a une organisation hiérarchique des objets, la propriété parentNode renvoie en DOM et en JS l'objet parent, mais était inconnue de IE6, la propriété parentElement renvoie en JScript l'objet parent; ceci aurait pu être corrigé facilement par un nouveau prototype :
Element.prototype.parentElement = Element.prototype.parentNode;

HTML
HEAD
TITLE LINK SCRIPT
BODY id ="mon_body"
H1
id ="mon_h1"
UL id ="mon_ul"
LI id ="mon_li"
UL
LI LI
LI
DIV
id ="mon_div"
P id ="mon_p"
EM
INPUT
id ="mon_input"

La collection document.mon_body.children ne contient que 4 éléments :

Elle ne contient pas "mon_li".
Par contre document.mon_ul.parentNode désigne document.mon_body, son parent.
Dans IE le simple fait d'avoir un tag (ou balise) avec un id l'utilisation du nom de l'id comme variable référence implicitement le tag portant l'id ;

Pendant de nombreuses versions, la surface totale accessible et visible sur l'écran (donc l'objet le plus haut dans la hiérarchie) était l'objet body pour IE mais html pour les autres ;

 

B. reconnaitre la position de la souris sur l'écran

On ne fait pas de graphisme sans au moins pouvoir détecter la position du curseur (de la souris). JS a prévu plusieurs moyens pour repérer la position de la souris sur l'écran. Les deux propriétés que nous utiliserons sont clientX qui renvoie l'abscisse (distance du bord gauche) et clientY qui renvoie l'ordonnée (distance du bord supérieure) de la pointe de la souris. Rappelons d'abord qu'en HTML et en JS, l'origine des axes se trouve dans le coin supérieur gauche, ce point a comme abscisse 0 et comme ordonnée 0, ce qui se note (0,0).

Si vous longez le bord supérieur en allant vers la droite,
l'ordonnée de votre souris (sa distance p/r au bord supérieur) reste quasi nulle,
mais son abscisse (sa distance p/r au bord gauche) va en augmentant.
Horizontalement, l'abscisse maximale (bord droit) dépendra de la définition de votre écran (de 640 ou 800 sur des écrans avec une faible définition à près de 2000 sur des écrans plus précis).
Par contre,
si vous partez du coin supérieur gauche et longez le bord gauche en descendant,
l'abscisse (distance du bord gauche) reste quasi nulle,
mais son ordonnée (distance p/r au bord supérieur) augmente jusqu'à 480 ou 600 sur des écrans avec une faible définition à près de 1200 sur des écrans plus précis.
Et oui, on augmente en descendant alors que les matheux s'enfoncent dans les valeurs négatives en descendant.

Javascript est un langage événementiel. Les évènements relatifs à la souris seront détectés par :
- onMouseMove :
     lorsque l'utilisateur bouge la souris
     indépendamment du fait que la touche en soit appuyée ou non ;
- onMouseClick :
     lorsque l'utilisateur clique ;

Et, lorsqu'on se risquera à faire glisser un objet, il faudra aussi distinguer :
- onMouseDown :
     lorsque l'utilisateur maintient la touche de souris appuyée ;
- onMouseUp :
     lorsque l'utilisateur, après avoir appuyé la touche de souris, la relâche.

Ces évènements peuvent n'être détectés que sur certains objets. Ainsi, dans l'exemple ci-dessous, le onclick n'est applicable que si le curseur de la souris est sur la zone rouge, alors que le mouvement de la souris onMouseMove est détectable sur toute la page.

Exemple

<html>
<head>
   <title>Evenement position souris</title>
   <script type="text/javascript">
      function handleEvent(oEvt) {
         var oTextbox = document.getElementById("txta");
         oTextbox.value += "\n sur (" + oEvt.clientX + "," + oEvt.clientY + "), OK pour la cible";
      }
   </script>
</head>
<body>
   <h2>CUY position souris</h2>
   <script type="text/javascript">
      document.body.onmousemove = function (e) {
         souris = document.getElementById("souris");
         souris.innerHTML= "Coordonnées souris : <br>X= "+e.clientX+" ; Y= "+e.clientY+" .";
      }
   </script>
   <table align="center"><tr align="center">
      <td>
         <textarea id="txta" rows="23" cols="35"></textarea>
      </td>
      <td>
         Déplacez votre souris <br>
         et lisez la ligne surlignée en jaune.<br>
         Avec votre souris, <br>
         cliquez et double-cliquez<br>
         sur le carré rouge de 200px x 200px,<br>
         particulièrement les coins.<br>
         Essayez aussi en dehors du carré.
         <div style="width: 200px; height: 200px; background-color: red"
            onclick="handleEvent(event)" id="zone_rouge"></div>
         <div id="souris" style="background-color: yellow">Coordonnées souris :<br>&nbsp; </div>
      </td>
   </tr></table>
</body>
</html>

 

 

C. déplacer un dessin sur l'écran (drag and drop)

Si vous avez compris toutes les nuances des deux scripts qui relèvent la position de pointeur de la souris, nous n'avons plus grand chose à vous expliquer pour que vous puissiez déplacer un dessin ou une image, voire même un objet sur votre écran. Quelles sont les informations qui vous manquent ? Commençons par observer la source :

Exemple

<html>
<head>
   <title>
      CUY Javascript (Glisse et laisse tomber image)
   </title>
<style type="text/css">
   .glissemoi{position:relative;}
</style>

<script type="text/javascript">
   <!-- d'après http://www.lecodejava.com/javaadraganddrop.html -->

   var ie=document.all;
   var nn6=document.getElementById&&!document.all;
   var estenglisse=false;
   var x,y;
   var dobj;

   function bougesouris(e) {
      if (estenglisse) {
         dobj.style.left = nn6 ? tx + e.clientX - x : tx + event.clientX - x;
         dobj.style.top = nn6 ? ty + e.clientY - y : ty + event.clientY - y;
         return false;
      }
   }

   function choixsouris(e) {
      var fobj = nn6 ? e.target : event.srcElement;
      var topelement = nn6 ? "HTML" : "BODY";
      while (fobj.tagName != topelement && fobj.className != "glissemoi") {
         fobj = nn6 ? fobj.parentNode : fobj.parentElement;
      }
      if (fobj.className=="glissemoi") {
         estenglisse = true;
         dobj = fobj;
         tx = parseInt(dobj.style.left+0);
         ty = parseInt(dobj.style.top+0);
         x = nn6 ? e.clientX : event.clientX;
         y = nn6 ? e.clientY : event.clientY;
         document.onmousemove=bougesouris;
         return false;
      }
   }

   document.onmousedown=choixsouris;
   document.onmouseup=new Function("estenglisse=false");

</script>
</head>
<body>
   <p>Faire un drag-and-drop (glisser-déposer) de l'image</p>
   <div align="center">
      <img src="../images/logocuy1mini4.gif" width="60" height="40" class="glissemoi">
   </div>
</body>
</html>

      1. Que contient le <body> ?

        Uniquement l'image choisie que l'on voudra déplacer... et à déclarer dans un style "glissemoi" qui permettra de faire partie des objets "glissables" ;
        mais à placer dans une balise <div> sans devoir l'identifier.
         
      2. Que fait la propriété style.left (et style.top) appliquée à un objet ?
         
        La propriété style.left (en mode lecture) relève l'abscisse (distance p/r au bord gauche) de l'objet auquel elle s'applique ; cette méthode peut être utilisée en mode écriture et dans ce cas déplace l'objet jusqu'à ce qu'il ait l'abscisse souhaitée ;
        idem pour style.top qui relève ou impose l'ordonnée de cet objet.
         
        Malheureusement, ces propriétés renvoient une valeur suivie de son unité en pixels, comme, par exemple '60px'. Puisqu'il s'agit d'une chaine de caractères, rien n'empêche de remplacer 'px' par '' et le tour est joué... malheureusement, le '60' qui resterait serait toujours une chaine. Dans notre page jvs_504_emprunt_bancaire_4.html nous comparons 4 fonctions qui peuvent s'appliquer à une chaine pour en extraire une valeur numérique entière. La fonction parseInt(chaine) semble donner le résultat convoité.
         
      3. Pourquoi trouve-t-on les 3 détecteurs relatifs aux évènements de la souris ?

        Un 'glisser-jeter', en anglais 'drag-and-drop' se fait en 3 temps :
        - enfoncer le bouton de souris sur l'objet choisi : onMouseDown,
        - glisser sans relâcher le bouton de la souris : onMouseMove,
        - relâcher le bouton de souris quand l'objet est arrivé à destination : onMouseMove.
         
      4. À quoi sert cette variable "estenglisse" ?
         
        Il s'agit d'une variable logique. Elle détecte si l'objet choisi est en cours de glissage ou pas. En d'autres mots, elle est 'fausse' par défaut, devient 'vraie' dès que l'on enfonce le bouton de la souris et redevient fausse dès que l'on relève le bouton de souris.
         
      5. P

       

       

       

      D. Le "drag and drop" en HTML5

      Le HTML5 nous apporte bien des avantages par rapport à sa version précédente. C'est bien la procédure suivie lorsque vous «attrapez» un objet et le faites glisser vers un autre emplacement.

      Étape 1 : rendre l'objet déplaçable

      HTML5 permet de rendre tout objet 'glissable' ou 'draggable'. Il suffit pour cela de lui attribuer l'attribut "draggable="true". Le lecteur excusera mon vocabulaire concernant la dragabilité de mes expressions.

      Étape 2 : préciser dans 'ondragstart' quelle fonction sera appelée

      Ensuite, on précisera ce qui devrait se passer si l'élément est glissé. Ici, dans notre exemple, on précise dans notre argument ondragstart, à quelle fonction on fera appel, ici glisse avec comme attribut evenement que nous définirons plus loin en spécifiant quelle donnée doit être transportée.

       

      Il existe de nombreuses librairies extérieures qui peuvent vous aider au niveau du graphisme. Vous trouverez par exemple un autre "drag and drop", réalisé avec l'aide de la librairie RGraph ici. La séquence suivante vous renseignera sur quelques librairies utiles pour le graphisme ; les exemples sont donnés à titre informatif et n'ont pas la prétention d'être une liste exhaustive.

 

II. Librairies extérieures pour le graphisme JS

voir suite >>>

 

III. HTML5 et <canvas> pour le graphisme JS

voir suite >>>

 

IV. Propriétés et méthodes pour <canvas> et le graphisme JS

voir suite >>>

 

 

VIII. Exercices relatifs au graphisme

voir suite >>>

 

 

IX. Test relatif au graphisme

voir suite >>>

 

 


Test relatif à la validation de formulaire (38.9) | | Librairies et autres ressources pour le graphisme (39.2)