Une des fonctionnalités les plus amusantes de canvas est la possibilité d'utiliser des images. Celles-ci peuvent être utilisées pour de la composition dynamique de photos ou comme fond pour des graphiques, etc. Il s'agit également de la seule manière d'y ajouter du texte (la spécification ne prévoit aucune fonction pour dessiner du texte). Les images externes peuvent être utilisées dans tout format géré par Gecko (c'est-à-dire au format PNG, GIF ou JPEG). D'autres éléments canvas de la même page peuvent également être utilisés comme source.
Importation d'images
L'importation d'images est un processus en deux étapes :
- Premièrement, il est nécessaire de disposer d'une référence à un objet JavaScript Image ou un autre élément canevas comme source. Il n'est pas possible d'utiliser des images en en fournissant simplement l'URL/le chemin.
- Deuxièmement, l'image est dessinée sur le canevas à l'aide de la fonction
drawImage
.
Préoccupons-nous d'abord de la première étape. Quatre options sont disponibles :
Utilisation d'images présentes sur la même page
Il est possible d'accéder à toutes les images d'une page à l'aide soit de la collection document.images
, soit de la méthode document.getElementsByTagName
ou, si l'on connaît l'attribut ID de l'image, la méthode document.getElementById
.
Utilisation d'autres éléments canvas
Comme avec des images normales, on peut accéder à d'autres éléments canvas à l'aide de la méthode document.getElementsByTagName
ou la méthode document.getElementById
. Assurez-vous d'avoir dessiné quelque chose sur le canevas source avant de l'utiliser dans un autre élément canvas.
Une des utilisations les plus pratiques de cette fonctionnalité serait d'utiliser un second élément canvas en tant qu'aperçu de taille réduite d'un canevas de grande taille.
Création d'une image à partir de rien
Un autre option est de créer de nouveaux objets Image
dans le script même. Le principal désavantage à cette approche est que si l'on ne désire pas que le script soit interrompu au milieu de son exécution en attendant qu'une image soit chargée, il est nécessaire d'utiliser l'une ou l'autre forme de préchargement d'images.
En pratique, pour créer une nouvelle image, on procède ainsi :
var img = new Image(); // Crée un nouvel objet Image img.src = 'myImage.png'; // Définit le chemin vers sa source
Lorsque ce script est exécuté, l'image commence à être chargée. Si le chargement n'est pas terminé lorsqu'une instruction drawImage
est exécutée, le script s'arrête jusqu'à ce que l'image soit entièrement chargée. Si vous ne désirez pas que cela se produise, utilisez un gestionnaire d'évènement onload
:
var img = new Image(); // Crée un nouvel objet Image img.src = 'myImage.png'; // Définit le chemin vers sa source img.onload = function(){ // instructions appelant drawImage ici }
Si vous n'utilisez qu'une seule image externe, cela peut être une bonne approche, mais dès qu'il est nécessaire de contrôler le chargement de plus d'une image, il est préférable de recourir à quelque chose de plus astucieux. Les techniques de préchargement d'images sortent du cadre de ce tutoriel mais vous pouvez consulter JavaScript Image Preloader (en anglais) pour une solution complète.
Intégration d'une image via une URL data:
Une autre manière possible d'inclure des images est d'utiliser les URL de type data:. Celles-ci permettent de définir une image complète dans une chaîne de caractères encodée en Base64 directement dans votre code. Un avantage des URL data est que l'image résultante est disponible immédiatement sans appel supplémentaire au serveur. (Un autre avantage est qu'il est alors possible d'encapsuler dans un seul fichier tous vos CSS, JavaScript, HTML et images, ce qui les rend plus facilement portables vers d'autres emplacements.) Parmi les désavantages, le fait qu'une image chargée par cette méthode n'est pas mise en cache, et que pour les images plus grandes l'URL encodée peut devenir particulièrement longue :
var img_src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==';
drawImage
Une fois une référence à l'objet image source obtenue, on peut utiliser la méthode drawImage
pour l'afficher sur le canevas. Comme nous le verrons plus tard, la méthode drawImage
est surchargée et possède trois variantes différentes. Dans sa forme la plus basique, elle ressemble à ceci :
drawImage(image, x, y)
Où image
est une référence à notre objet image ou un autre canvas. x
et y
indiquent les coordonnées du canevas cible où l'image sera placée.
Premier exemple de drawImage
Dans l'exemple suivant, nous utiliserons une image externe comme fond pour un petit graphique linéaire. L'utilisation d'images de fond peut rendre vos scripts considérablement plus légers puisqu'il n'est alors pas nécessaire de dessiner des arrières-plans élaborés. Une seule image est utilisée ici, on utilise donc le gestionnaire d'évènement onload
de l'objet image pour lancer les instructions de dessin. La méthode drawImage
place l'image de fond aux coordonnées (0,0), soit le coin supérieur gauche du canevas.
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); img.src = 'backdrop.png'; img.onload = function(){ ctx.drawImage(img, 0, 0); ctx.beginPath(); ctx.moveTo(30, 96); ctx.lineTo(70, 66); ctx.lineTo(103, 76); ctx.lineTo(170, 15); ctx.stroke(); } }
Mise à l'échelle
La seconde variante de la méthode drawImage
ajoute deux paramètres supplémentaires et permet de placer des images redimensionnées sur le canevas.
drawImage(image, x, y, largeur, hauteur)
Où largeur
et hauteur
indiquent la taile de l'image sur le canevas cible.
Second exemple de drawImage
Dans cet exemple, nous utiliserons une image comme papier-peint en la répétant plusieurs fois sur le canevas. Cette opération est réalisée simplement en faisant une boucle plaçant plusieurs fois l'image redimensionnée à de différentes positions. Dans le code ci-dessous, la première boucle for
s'occupe des lignes alors que la seconde boucle sur les colonnes. L'image est redimensionnée à un tiers de sa taille originale, ce qui fait 50×38 pixels. Nous verrons comment cela aurait également pu être réalisé en créant un pattern personnalisé plus loin dans ce tutoriel.
Note : les images peuvent devenir floues lorsqu'elles sont agrandies ou granuleuses si elles sont réduites. Il ne vaut mieux pas redimensionner une image contenant du texte devant rester lisible.
function draw() { var ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); img.src = 'images/rhino.jpg'; img.onload = function(){ for (i = 0; i < 4; i++){ for (j = 0; j < 3; j++){ ctx.drawImage(img, j*50, i*38, 50, 38); } } } }
Découpage
La troisième et dernière variante de la méthode drawImage
possède huit nouveaux paramètres. On peut l'utiliser pour découper des parties d'une image source et les afficher sur le canevas.
drawImage(image, sx, sy, sLargeur, sHauteur, dx, dy, dLargeur, dHauteur)
Le premier paramètre image
, comme dans les autres variantes, est soit une référence à un objet image ou une référence à un autre élément canvas. Pour les huit autres paramètres, le plus facile est de regarder l'image à droite. Les quatre premiers définissent l'emplacement et la taille de la découpe sur l'image source, et les quatre derniers sa position et sa taille sur le canevas de destination.
Le découpage peut être un outil utile pour réaliser des compositions. Vous pouvez disposer tous les éléments dans un seul fichier image et utiliser cette méthode pour composer un dessin complet. Par exemple, si vous voulez réaliser un graphique, vous pouvez utiliser une image PNG contenant tout le texte nécessaire dans un seul fichier et, selon vos données, changer l'échelle de votre graphique sans trop de difficultés. Un autre avantage est qu'il n'est pas nécessaire de charger chaque image individuellement.
Troisième exemple de drawImage
Dans cet exemple, nous utiliserons le même rhinocéros que plus haut, mais sa tête sera coupée et composée dans un cadre. L'image du cadre fournit une ombre portée qui a été enregistrée dans une image PNG 24 bits. Comme les images PNG 24 bits comportent un canal alpha complet de 8 bits, contrairement aux images GIF et PNG 8 bits, elle peut être placée sur n'importe quel fond sans avoir à se préoccuper de la couleur de transition.
Pour le chargement des images, une approche différente de la précédente a été utilisée. Les images ont simplement été placées directement dans le document HTML et masquées à l'aide d'une règle CSS (display:none
). Un attribut id
a été assigné à chacune des images pour les rendre plus faciles à sélectionner. Le script en lui-même est très simple. Il dessine d'abord l'image découpée et redimensionnée sur le canevas (premier appel à drawImage
), et place ensuite le cadre par dessus (second appel à drawImage
).
function draw() { var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); // Dessine la découpe ctx.drawImage(document.getElementById('source'), 33, 71, 104, 124, 21, 20, 87, 104); // Dessine le cadre ctx.drawImage(document.getElementById('frame'), 0, 0); }
Exemple d'une galerie d'art
Dans le dernier exemple de ce chapitre, nous présenterons une petite galerie d'art. Cette galerie est constituée d'un tableau contenant plusieurs images. Lorsque la page est chargée, un élément canvas est inséré pour chaque image et un cadre est dessiné autour.
Dans notre cas, toutes les images ont une largeur et une hauteur fixe, ainsi que le cadre qui sera dessiné. Le script pourrait être amélioré afin d'utiliser la largeur et la hauteur de l'image pour que le cadre s'adapte parfaitement à ses dimensions.
Le code ci-dessous devrait s'expliquer de lui-même. On cycle parmi le tableau des images de la page et on ajoute de nouveaux éléments canvas. La seule chose notable est probablement, pour ceux qui ne sont pas familiers avec le DOM, l'utilisation de la méthode insertBefore. insertBefore
est une méthode du nœud parent (une cellule de tableau) de l'élément (l'image) avant lequel on désire insérer le nouveau nœud (l'élément canvas).
function draw() { // Cycle parmi toutes les images for (i = 0; i < document.images.length; i++) { // Ne pas ajouter de canevas pour l'image du cadre if (document.images[i].getAttribute('id') != 'frame') { // Création de l'élément canvas canvas = document.createElement('CANVAS'); canvas.setAttribute('width', 132); canvas.setAttribute('height', 150); // Insertion avant l'image document.images[i].parentNode.insertBefore(canvas, document.images[i]); ctx = canvas.getContext('2d'); // Dessin de l'image sur le canevas ctx.drawImage(document.images[i], 15, 20); // Ajout du cadre ctx.drawImage(document.getElementById('frame'), 0, 0); } } }