Thomas Zilliox
Expert CSS Freelance à Lyon

Pixeliser une image avec canvas et JavaScript

Avec Zupple, team building et jeux de piste à Lyon, nous sommes partenaire du RAIDinLyon. Dans le cadre du prochain événement, on nous a demandé s’il était possible de pixeliser une image dynamiquement. C’est à dire avoir une image plus ou moins pixelisée en fonction des réponses des joueurs et joueuses. Et chez Zupple, on adore les défis !

Mon petit exercice du week-end a donc été de pixeliser une image avec JavaScript, et c'était plus facile que ce à quoi je m'attendais !

Voici une démonstration de ce que je voulais coder. Mon objectif était de pouvoir décider à la volée du niveau de pixelisation. Si vous déplacez le curseur qui se trouve sur l'image, vous pourrez donc rendre l'image de plus nette ou plus floue !

Stratégie

Pour réaliser une image pixelisée, la technique est de dessiner une image plus petite puis de l'étirer à sa taille originale.

Pour que ça fonctionne, il faut demander au canvas de ne pas lisser le rendu quand il étire l'image réduite. Cela nous permettra d'obtenir de grands pixels bien nets. L'option à définir pour décider si l'on veut ou non lisser le rendu est ctx.imageSmoothingEnabled.

// Demande au contexte de rendu du canvas de ne pas lisser les images
ctx.imageSmoothingEnabled = false;
Capture d'écran avec et sans l'option imageSmoothingEnabled des canvas activé
La différence entre une image étirée avec et sans `imageSmoothingEnabled` activé.

drawImage()

La méthode drawImage() permet de dessiner une image dans un contexte de rendu 2D d'un canvas. La plupart du temps on l'utilise avec seulement 3 paramètres : l'image et les coordonées où la dessiner. Pour réaliser notre image pixelisée, nous devrons utiliser des appels avec 5, puis 9 paramètres.

/** drawImage() avec 3 paramètres
 * - Prend l'image source
 * - Dessine la dans le canvas aux coordonnées destX / destY
 */
ctx.drawImage(image, destX, destY);

/** drawImage() avec 5 paramètres
 * - Prend l'image source
 * - Redimensionne la à la taille destL / destH
 * - Dessine la dans le canvas aux coordonnées destX / destY
 */
void ctx.drawImage(image, destX, destY, destL, destH);

/** drawImage() avec 9 paramètres
 * - Prend l'image source
 * - Garde une zone de taille sourceL / sourceH aux coordonnées sourceX / sourceY
 * - Redimensionne la à la taille destL / destH
 * - Dessine la dans le canvas aux coordonnées destX / destY
 */
void ctx.drawImage(
  image,
  sourceX,
  sourceY,
  sourceL,
  sourceH,
  destX,
  destY,
  destL,
  destH
);

Dessiner une image plus petite avec Canvas

Première étape, dessiner une image plus petite :

/**
 * Dessine une image plus petite
 *
 * @param {HTMLImageElement} img - image source
 * @param {number} ratio - le ratio de réduction de l'image
 * @returns {HTMLCanvasElement}
 */
function reduceImage(img, ratio) {
  // Initialisation d'un nouvean canvas
  const canvas = document.createElement("canvas");

  // On définit la taille intrinsèque du canvas avec des dimensions réduites
  canvas.width = img.naturalWidth / ratio;
  canvas.height = img.naturalHeight / ratio;

  // Récupération du contexte de rendu
  const ctx = canvas.getContext("2d");

  // On dessine l'image sur toute la taille du canvas
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

  return canvas;
}

Agrandir et pixeliser une image avec Canvas

Deuxième étape, on dessine une nouvelle image. Cette fois-ci aux dimensions initiales mais à partir de la version réduite :

/**
 * Pixelise une image dans un canvas
 *
 * @param {HTMLCanvasElement} canvas - canvas où l'on va dessiner l'image pixelisée
 * @param {HTMLImageElement} img - image source
 * @param {number} ratio - le ratio de réduction de l'image
 * @returns {void}
 */
function pixelateImage(canvas, img, ratio) {
  // On définit la taille intrinsèque du canvas avec les dimensions d'origine
  canvas.width = img.naturalWidth;
  canvas.height = img.naturalHeight;

  // Paramètrage du contexte de rendu pour ne pas lisser les pixels
  const ctx = canvas.getContext("2d");
  ctx.imageSmoothingEnabled = false;

  // On dessine une image plus petite
  const smallCanvas = reduceImage(img, ratio);

  // On étire l'image réduite sur toute la taille du canvas
  ctx.drawImage(
    smallCanvas,
    0,
    0,
    smallCanvas.width,
    smallCanvas.height,
    0,
    0,
    canvas.width,
    canvas.height
  );
}

En utilisant la fonction ci-dessus, vous pouvez recréer des images avec des effets pixelisés !

Amusez-vous bien, Thomas.

That's my face!

Thomas ZILLIOX

Je suis Thomas Zilliox, l'homme qui murmurait à l'oreille des chevrons, un développeur CSS freelance sur Lyon.

Je suis aussi le co-créateur de la société Zupple qui crée, organise, et anime des team building et escape games à Lyon.