C'est vous, Canvas2D

Aaron Krajeski
Aaron Krajeski

Dans un monde de nuanceurs, de maillages et de filtres, Canvas2D ne vous donnera peut-être pas envie. Mais ça devrait être le cas ! 30 à 40% des pages Web contiennent un élément <canvas> et 98% de toutes les toiles utilisent un contexte de rendu Canvas2D. Canvas2D est disponible dans les voitures, sur les réfrigérateurs et dans l'espace (vraiment).

Certes, l'API est un peu en retard par rapport au temps consacré au dessin 2D de pointe. Heureusement, nous avons travaillé d'arrache-pied pour implémenter de nouvelles fonctionnalités dans Canvas2D afin de rattraper le retard CSS, de simplifier l'ergonomie et d'améliorer les performances.

1re partie: S'informer sur les CSS

Certaines commandes de dessin du CSS sont très manquantes dans Canvas2D. Avec la nouvelle API, nous avons ajouté quelques-unes des fonctionnalités les plus demandées:

Rectangle arrondi

Rectangles arrondis: à la base d'Internet, de l'informatique, de la civilisation

Plus sérieusement, les rectangles arrondis sont extrêmement utiles: boutons, bulles de chat, vignettes, bulles de texte, etc. Il a toujours été possible de créer un rectangle arrondi dans Canvas2D, mais cela a été un peu désordonné:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'magenta';

const top = 10;
const left = 10;
const width = 200;
const height = 100;
const radius = 20;

ctx.beginPath();
ctx.moveTo(left + radius, top);
ctx.lineTo(left + width - radius, top);
ctx.arcTo(left + width, top, left + width, top + radius, radius);
ctx.lineTo(left + width, top + height - radius);
ctx.arcTo(left + width, top + height, left + width - radius, top + height, radius);
ctx.lineTo(left + radius, top + height);
ctx.arcTo(left, top + height, left, top + height - radius, radius);
ctx.lineTo(left, top + radius);
ctx.arcTo(left, top, left + radius, top, radius);
ctx.stroke();

Tout cela était nécessaire pour un rectangle arrondi simple et modeste:

Rectangle arrondi.

La nouvelle API comporte une méthode roundRect().

ctx.roundRect(upper, left, width, height, borderRadius);

Ce qui précède peut donc être entièrement remplacé par:

ctx.roundRect(10, 10, 200, 100, 20);

La méthode ctx.roundRect() utilise également un tableau comportant jusqu'à quatre nombres pour l'argument borderRadius. Ces rayons contrôlent les quatre coins du rectangle arrondi de la même manière que pour les fichiers CSS. Exemple :

ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);

Regardez la démo pour faire un tour !

Dégradé conique

Vous avez vu les dégradés linéaires:

const gradient = ctx.createLinearGradient(0, 0, 200, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(0.5, 'magenta');
gradient.addColorStop(1, 'white');
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);

Dégradé linéaire.

Dégradé radial:

const radialGradient = ctx.createRadialGradient(150, 75, 10, 150, 75, 70);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(0.5, 'magenta');
radialGradient.addColorStop(1, 'lightblue');

ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);

Dégradé radial.

Mais que diriez-vous d'un joli dégradé conique ?

const grad = ctx.createConicGradient(0, 100, 100);

grad.addColorStop(0, 'red');
grad.addColorStop(0.25, 'orange');
grad.addColorStop(0.5, 'yellow');
grad.addColorStop(0.75, 'green');
grad.addColorStop(1, 'blue');

ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 200);

Dégradé conique.

Modificateurs de texte

Les fonctionnalités de rendu de texte de Canvas2D sont terriblement en retard. Chrome a ajouté plusieurs nouveaux attributs au rendu de texte Canvas2D:

Ces attributs correspondent tous à leurs équivalents CSS portant le même nom.

Partie 2: ajustements de l'ergonomie

Auparavant, certaines choses avec Canvas2D étaient possibles, mais leur implémentation était inutile. Voici quelques améliorations en matière de qualité de vie pour les développeurs JavaScript qui souhaitent utiliser Canvas2D:

Réinitialisation du contexte

Pour expliquer comment effacer un canevas, j'ai écrit une petite fonction loufoque permettant de dessiner un motif rétro:

draw90sPattern();

Motif rétro de triangles et de carrés.

Parfait. Maintenant que j'ai terminé avec ce motif, je veux vider la toile et dessiner autre chose. Attendez, comment vider à nouveau un canevas ? Tout juste ctx.clearRect(), bien sûr.

ctx.clearRect(0, 0, canvas.width, canvas.height);

Hein... Ça n'a pas marché. Tout juste Je dois d'abord réinitialiser la transformation:

ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
Une toile vierge.

Parfait ! Une belle toile vierge. Commençons maintenant à tracer une bonne ligne horizontale:

ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();

Une ligne horizontale et une ligne diagonale.

Grrrr! Mauvaise réponse. 💌 Qu'est-ce que cette ligne supplémentaire fait ici ? Et pourquoi est-il rose ? D’accord, vérifions simplement StackOverflow.

canvas.width = canvas.width;

Pourquoi est-ce si bête ? Pourquoi est-ce si difficile ?

Eh bien, ce n'est plus le cas. La nouvelle API offre une solution simple, élégante et novatrice:

ctx.reset();

Désolé, cela a pris si longtemps.

Filtres

Les filtres SVG sont un monde à part entière. Si vous débutez, je vous recommande vivement de lire l'article The Art Of SVG Filters And Why It Is Awesome (L'art des filtres SVG et pourquoi elles sont géniales), qui présentent leur incroyable potentiel.

Les filtres de style SVG sont déjà disponibles pour Canvas2D. Il vous suffit d'accepter de transmettre le filtre en tant qu'URL pointant vers un autre élément de filtre SVG sur la page:

<svg>
  <defs>
    <filter id="svgFilter">
      <feGaussianBlur in="SourceGraphic" stdDeviation="5" />
      <feConvolveMatrix kernelMatrix="-3 0 0 0 0.5 0 0 0 3" />
      <feColorMatrix type="hueRotate" values="90" />
    </filter>
  </defs>
</svg>
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 400;
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);

ctx.filter = "url('#svgFilter')";
draw90sPattern(ctx);

Ce qui perturbe le fonctionnement de notre modèle:

Motif rétro avec un effet flouté appliqué.

Mais que se passe-t-il si vous voulez effectuer la procédure ci-dessus tout en restant dans JavaScript et sans manipuler de chaînes ? Avec la nouvelle API, c'est tout à fait possible.

ctx.filter = new CanvasFilter([
  { filter: 'gaussianBlur', stdDeviation: 5 },
  {
    filter: 'convolveMatrix',
    kernelMatrix: [
      [-3, 0, 0],
      [0, 0.5, 0],
      [0, 0, 3],
    ],
  },
  { filter: 'colorMatrix', type: 'hueRotate', values: 90 },
]);

C'est aussi simple que gâteau ! Essayez-la et testez les paramètres présentés ici.

Partie 3: amélioration des performances

Avec la nouvelle API Canvas2D, nous voulions également améliorer les performances dans la mesure du possible. Nous avons ajouté quelques fonctionnalités pour permettre aux développeurs de contrôler plus précisément leurs sites Web et d'obtenir des fréquences de frames aussi nettes que possible:

Sera souvent lu

Utilisez getImageData() pour lire les données de pixels à partir d'un canevas. Cela peut être très lent. La nouvelle API vous permet de marquer explicitement un canevas pour la lecture (pour les effets génératifs, par exemple). Cela vous permet d'optimiser ce qui se trouve en arrière-plan et d'accélérer le canevas dans des cas d'utilisation plus variés. Cette fonctionnalité est disponible dans Firefox depuis un certain temps, et nous l'avons finalement intégrée aux spécifications de canevas.

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });

Perte de contexte

Ça fait plaisir aux tabourets ! Si un client manque de mémoire GPU ou qu'un autre sinistre tombe sur votre canevas, vous pouvez désormais recevoir un rappel et le redessiner si nécessaire:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);

Si vous souhaitez en savoir plus sur le contexte et la perte des canevas, le WhatWG propose une bonne explication sur son wiki.

Conclusion

Que vous débutiez avec Canvas2D, que vous l'utilisiez depuis des années ou que vous l'évitiez depuis des années, je suis là pour vous dire de donner un nouveau look à la toile. C'est la porte d'API suivante qui était là depuis le début.

Remerciements

Image principale de Sandie Clarke sur Unsplash.