AXOPEN

JQuery – Réaliser facilement un zoom personnalisé

Pourquoi réinventer la roue ?

JQuery a rendu facile le recours au JavaScript dans les applications web. Un ensemble de pratiques courantes, auparavant assez techniques, sont désormais accessibles, même aux débutants : faire des requêtes Ajax, accéder à des éléments du DOM en utilisant des sélecteurs CSS, binder des actions sur des événements…

JQuery a aussi facilité le développement d’éléments plus complexes comme des champs d’autocomplétion, des fenêtres de dialogue ou des color pickers. Pour ces fonctionnalités plus complexes, il existe un grand nombre de plugins. Mais ces plugins sont souvent difficiles à intégrer et se veulent très génériques alors que l’on a souvent un besoin très précis (et que l’on souhaite limiter la quantité de JavaScript par souci évident de performance). Or certaines fonctionnalités pour lesquelles un grand nombre de plugins existent ne sont en fait pas très complexes à implémenter soi-même. Ici, nous allons voir comment on peut développer facilement et en peu de lignes un zoom.

Principe

Le but est de créer à partir d’une simple image dans le DOM une div qui présente une zone agrandie de cette image, de telle sorte que l’on puisse se déplacer sur l’image zoomée simplement en bougeant la souris.

De nombreuses présentations sont possibles : un zoom qui se déplace par dessus l’image, ou bien situé sur le côté, ou bien qui remplace l’image… un zoom carré, rond… Dans tous les cas le principe est le même, il sera donc facile de lui donner l’apparence que l’on veut et de le positionner où on veut.

Ici, nous allons réaliser un zoom rond qui suivra le curseur de la souris dès lors qu’elle sera au-dessus de l’image de départ. Les étapes sont donc :

  • initialiser le zoom : créer les structures dans le DOM qui permettront d’afficher le zoom
  • binder les événements : faire en sorte d’afficher le zoom lorsque la souris entre sur l’image, de le cacher quand elle en sort, et de le positionner quand elle bouge
  • positionner le zoom : afficher la bonne zone de l’image zoomée positionner la div du zoom par dessus l’image
  • mettre en place le style CSS qui permettra un rendu agréable

Version de JQuery

Les versions 2.x de JQuery sont plus légères mais présentent le fâcheux inconvénient de ne pas être compatible avec les versions d’Internet Explorer antérieures à 9. Or IE 8 représente encore 5% de part de marché en février 2014, pourcentage d’internautes dont nous ne voulons pas nous priver. Pensez bien en revanche lorsque vous téléchargez la dernière version 1.x sur le site de JQuery à prendre le format .min.js, qui est compressé.

Initialiser le zoom

Le but est d’obtenir à partir d’une image un agrandissement de cette image contenu dans une div. La « partie div » de la méthode ne pose pas de problème, la partie image un peu plus. Deux cas de figure se présentent :

  • vous avez la possibilité à partir de l’image source d’extrapoler une URL vers un agrandissement de cette image : c’est le meilleur cas de figure. Vous avez alors deux possibilités d’affichage :
    • vous affichez les deux images (l’original et l’agrandissement) en taille réelle : il faut alors veiller à ne pas avoir un rapport de taille trop important entre des deux images et stocker le ratio dans une variable ;
    • vous imposez une taille à votre agrandissement selon un ratio prédéfini judicieusement (2 par exemple…), en prenant le risque de réduire beaucoup ou agrandir excessivement votre image ;
  • vous ne disposez que de votre image source : il faut alors la dupliquer et appliquer à la copie une taille relative à celle de l’original.

Nous allons nous positionner dans ce dernier cas : il suffira de quelques modifications pour mettre en place une des deux autre possibilités. Imaginons que les images sur lesquelles on souhaite mettre en place notre zoom portent la classe CSS « zoomable ». On peut donc imaginer un code tel que :

$('img.zoomable').each(function(){
   var zoombox=$('').insertAfter($(this));      
   $(this).clone().attr('id', $(this).attr('id') + 'zoom').attr('width', $(this).attr('width')*2).appendTo(zoombox); 
});

Ici, on sélectionne toutes les images à zoomer, puis on boucle sur le résultat : pour chaque image trouvée, on crée une div qu’on met juste après l’image originale, et on copie l’image dans la div. On paramètre la largeur de la copie à deux fois celle de l’originale. De plus, la div et l’image copiée ont chacune un id dérivé de celui de l’image source, de façon à pouvoir y accéder à partir de l’image source.

Binder les événements

Maintenant que nous avons construit la structure HTML de notre zoom, il faut faire en sorte de l’afficher seulement lorsque la souris passe au-dessus, et de le positionner pour qu’il suive celle-ci. Les événements qu’il faut intercepter sont donc :

  • l’entrée de la souris sur l’image
  • le mouvement de la souris sur l’image
  • la sortie de la souris de l’image

Si l’on continue à partir du code précédent, on peut donc écrire le code suivant :

$('img.zoomable').each(function(){
   var zoombox=$('').insertAfter($(this));   
   $(this).clone().attr('id', $(this).attr('id') + 'zoom').attr('width', $(this).attr('width')*2).appendTo(zoombox); bindZoom($(this)); }); 
function bindZoom(img) { 
   img.bind('mouseenter mouseover', function(e){ 
      $('#' + img.attr('id') + 'zoombox').css('display','block'); 
      positionZoom(e,img); 
   }); 
   img.bind('mouseleave', function(e){ 
      $('#' + img.attr('id') + 'zoombox').css('display','none'); 
   }); 
   img.bind('mousemove', function(e){ 
      positionZoom(e,img); 
   }); 
}

Ici, pour chaque image zoomable, on crée le zoom comme vu précédemment, puis on appelle une fonction qui regroupe les bindings. Dans cette fonction, on retrouve les 3 événements annoncés :

  • mouseenter, mouseover : on affiche la « boîte » du zoom et on le positionne ;
  • mouseleave : on cache la « boîte » du zoom ;
  • mousemove : on repositionne le zoom.

Le positionnement du zoom fait toujours appel à une fonction positionZoom que nous allons voir à présent.

Positionner le zoom

Cette partie peut apparaître comme la plus compliquée, mais ne l’est pas tant que ça : il s’agit simplement de se déplacer dans un repère orthonormé en deux dimensions. Il faut donc calculer une position x et une position y. Deux options se présentent :

  • calculer les positions absolues par rapport à la page ;
  • calculer une position relative par rapport à l’image : il faut donc positionner l’image dans un conteneur (une div) par rapport auquel on puisse se positionner.

Nous allons retenir cette deuxième proposition. On peut donc obtenir le code suivant :

function positionZoom(e,img) {
   var offset=img.offset();
   var zoombox=$('#' + img.attr('id') + 'zoombox');
   zoombox.css('left', (e.pageX-offset.left+30)+'px');
   zoombox.css('top', (e.pageY-offset.top-75)+'px');
   var zoom=$('#' + img.attr('id') + 'zoom');
   zoom.css('left', ((e.pageX-offset.left-37.5)*-2)+'px');
   zoom.css('top', ((e.pageY-offset.top-37.5)*-2)+'px');
}

Ici, on récupère l’offset de l’image par rapport à la page. Puis on positionne la boîte en lui donnant un x qui l’offset de la souris par rapport à la page moins celui de l’image, ce qui nous donne l’offset de la souris par rapport à l’image. De plus, on décale la boîte de 30px vers la gauche pour la détacher du curseur de la souris, et de 75px vers le haut pour la centrer verticalement par rapport au curseur (on a une boîte de 150px de côté).

Pour positionner l’image zoomée, c’est un peu plus complexe. D’abord le mouvement est inversé : si je bouge ma souris vers la droite, alors je dois décaler mon image zoomée vers la gauche. Pourquoi ? Dans un cas, la zone ciblée bouge avec ma souris. Dans l’autre, la zone ciblée est fixe : c’est le centre de ma boîte. Donc pour que le centre de ma boîte coïncide avec un point plus à droite sur l’image, je dois déplacer mon image vers la gauche. Même raisonnement pour l’approche verticale.

Ensuite, il faut tenir compte du changement d’échelle : lorsque je me déplace de 100px sur mon image originale, je dois décaler mon zoom de 100 fois mon ratio, ici 100*2px. De plus, mon zoom ne fait pas la taille d’un px, et j’ai fait en sorte que le point ciblé par mon curseur corresponde au centre de mon zoom, et non pas au coin supérieur gauche. Je dois donc en tenir compte dans mon calcul d’offset.

Au final, on soustrait l’offset de l’image à celui de la page comme précédemment, et l’on soustrait encore la taille de la boîte divisée par le ratio. Ici j’ai une boîte de 150px et un ratio de 2 : je soustrais 37,5px. J’obtiens une position relative du curseur sur l’image originale. Pour avoir l’offset final sur le zoom, je multiplie par le ratio, et j’inverse le signe (ou je multiplie par -1) pour tenir compte du mouvement inversé que j’ai expliqué précédemment. Et le tour est joué !

Mise en forme CSS

Le style sur un composant de ce genre n’est pas uniquement décoratif. Il permet également le positionnement absolu ou relatif des éléments les uns par rapport aux autres. Ici, nous nous contenterons de ce qui est nécessaire à la réalisation du zoom et de la mise en forme de la boîte de zoom.

Nous allons considérer que nous travaillons sur le code HTML suivant :

Nous avons ici quelque chose de simpliste : une image dans une div. La div permettra de servir de repère pour le positionnement relatif du zoom. Je propose le code CSS suivant :

div#container {
   position: relative;
   width: 1000px;
   margin: auto;
}
img.zoomable{
   cursor: crosshair;
}
div[id$="zoombox"] {
   position: absolute;
   overflow: hidden;
   height: 150px;
   width: 150px;
   border: 2px solid #E5E5E5;
   background-color: #FFFFFF;
   box-shadow: 1px 1px 12px #999999;
   border-radius: 75px;
   -moz-border-radius: 75px;
   -webkit-border-radius: 75px;
}
img[id$="zoom"] {
   position: absolute;
}

Il y a quatre groupes de propriétés CSS :

  •  le premier concerne la div qui englobe l’image originale : l’élément à retenir est le positionnement relatif. C’est grâce à cela que le positionnement absolu de la boîte de zoom se fera relativement à cette div et non à la page entière ;
  • le deuxième est esthétique : il concerne l’apparence du pointeur au-dessus de l’image ;
  • le troisième concerne la boite : les éléments obligatoires sont le positionnement absolu, l’overflow, qui permet de masquer les parties de l’image zoomée qui sont en-dehors de la div, et la width et la height. Le reste permet de mettre en forme la boîte : ce style est un exemple, on peut le décliner à l’infini ;
  • le quatrième concerne l’image zoomée et son positionnement.

Il ne reste qu’à assembler tous ces éléments pour mettre en oeuvre notre zoom.

Exemple

Voici un petit code presque auto-suffisant pour afficher notre zoom. Il faut toutefois :

  • mettre une version de JQuery dans le même répertoire, et si ce n’est pas celle paramétrée dans le head modifier ce dernier ;
  • mettre une image dans le même répertoire, et mettre son nom dans la balise img. Pour un rendu optimal, utilisez une image de 1024px (d’où le width= »512″), ou bien modifiez la width de façon à ce qu’elle soit la moitié de la taille réelle.
  • mettez le code ci-dessous dans un fichier HTML et affichez-le.


   
      Zoom JQuery
      
      
      
   
   
      

Conclusion

La longueur de cet article peut faire penser que la tâche est plus ardue que le titre ne le laisse supposer, mais c’est en définitive effectivement assez simple.

Pour aller plus loin, il y a mille façons d’améliorer et/ou de personnaliser ce zoom :

  • afficher le zoom sur le côté
  • modifier sa forme / son apparence
  • utiliser réellement 2 images de définitions différentes
  • afficher le zoom à la place de l’image de départ