Dans un monde de nuanceurs, de maillages et de filtres, Canvas2D ne vous enthousiasmera peut-être pas. Mais il devrait.
30 à 40% des pages Web contiennent un élément <canvas>
et 98% de tous les canevas utilisent un contexte de rendu Canvas2D. Il existe des Canvas2D dans les voitures, sur des réfrigérateurs et dans l'espace (vraiment).
Il est vrai que l'API est un peu dépassée en matière de dessin 2D de pointe. Heureusement, nous avons travaillé dur pour implémenter de nouvelles fonctionnalités dans Canvas2D afin de rattraper le CSS, d'optimiser l'ergonomie et d'améliorer les performances.
Partie 1: Rattraper son retard sur les CSS
Le CSS comporte quelques commandes de dessin qui manquent cruellement à Canvas2D. Avec la nouvelle API, nous avons ajouté quelques-unes des fonctionnalités les plus demandées:
Rectangle arrondi
Rectangles arrondis: pierre angulaire d'Internet, de l'informatique, voire de la civilisation.
Plus sérieusement, les rectangles arrondis sont extrêmement utiles: en tant que boutons, bulles de chat, miniatures, bulles de dialogue, etc. Il a toujours été possible de créer un rectangle arrondi dans Canvas2D, mais c'était 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 modeste et simple:
Avec la nouvelle API, il existe une méthode roundRect()
.
ctx.roundRect(upper, left, width, height, borderRadius);
Ainsi, le code ci-dessus peut être entièrement remplacé par:
ctx.roundRect(10, 10, 200, 100, 20);
La méthode ctx.roundRect()
accepte également un tableau pour l'argument borderRadius
, qui peut contenir jusqu'à quatre nombres. Ces rayons contrôlent les quatre coins du rectangle arrondi de la même manière que pour le CSS. Exemple :
ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);
Découvrez la démo pour vous amuser !
Dégradé conique
Vous avez déjà vu des 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é 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);
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);
Modificateurs de texte
Les fonctionnalités de rendu du texte de Canvas2D sont très en retard. Chrome a ajouté plusieurs nouveaux attributs au rendu de texte Canvas2D:
- ctx.letterSpacing
- ctx.wordSpacing
- ctx.fontVariant
- ctx.fontKerning
- ctx.fontStretch
- ctx.textDecoration
- ctx.textUnderlinePosition
- ctx.textRendering
Ces attributs correspondent tous à leurs équivalents CSS portant le même nom.
2e partie: amélioration de l'ergonomie
Auparavant, certaines choses étaient possibles avec Canvas2D, mais leur implémentation était inutilement compliquée. Voici quelques améliorations de la qualité de vie pour les développeurs JavaScript qui souhaitent utiliser Canvas2D:
Réinitialisation du contexte
Pour expliquer l'effacement d'un canevas, j'ai écrit une petite fonction pour dessiner un motif rétro:
draw90sPattern();
Parfait ! Maintenant que j'ai terminé ce motif, je souhaite effacer le canevas et dessiner autre chose.
Attends, comment vider à nouveau un canevas ? Oui ! ctx.clearRect()
, bien sûr.
ctx.clearRect(0, 0, canvas.width, canvas.height);
Hum… Cela n'a pas fonctionné. Oui ! Je dois d'abord réinitialiser la transformation:
ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
Parfait ! Une belle toile vierge. Commençons par dessiner une belle ligne horizontale:
ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();
Grrrr ! Mauvaise réponse. 😡 Qu'est-ce que cette ligne supplémentaire fait ici ? Pourquoi est-il rose ? OK, je vais juste vérifier sur StackOverflow.
canvas.width = canvas.width;
Pourquoi est-ce si ridicule ? Pourquoi est-ce si difficile ?
Ce n'est plus le cas. Avec la nouvelle API, nous avons une solution simple, élégante et révolutionnaire:
ctx.reset();
Je suis désolé que cela ait pris autant de temps.
Filtres
Les filtres SVG sont un monde à part. Si vous ne connaissez pas encore les filtres SVG, nous vous recommandons vivement de lire The Art Of SVG Filters And Why It Is Awesome, qui présente certains de leurs incroyables potentiels.
Les filtres de style SVG sont déjà disponibles pour Canvas2D. Vous devez simplement 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 considérablement notre modèle:
Mais que faire si vous souhaitez effectuer les opérations ci-dessus, mais en restant dans JavaScript et sans vous embêter avec des 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 },
]);
Rien de plus simple ! Essayez-le et jouez avec les paramètres dans la démonstration.
Partie 3: Améliorations des performances
Avec la nouvelle API Canvas2D, nous souhaitions é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 d'images aussi fluides que possible:
Lire fréquemment
Utilisez getImageData()
pour lire les données de pixel à 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 les éléments en arrière-plan et de préserver la rapidité du canevas pour un plus grand nombre de cas d'utilisation. Cette fonctionnalité est disponible dans Firefox depuis un certain temps, et nous l'intégrons enfin aux spécifications du canevas.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
Perte de contexte
Rendons les onglets tristes heureux ! Si un client manque de mémoire GPU ou qu'un autre désastre frappe votre canevas, vous pouvez désormais recevoir un rappel et redessiner si nécessaire:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);
Pour en savoir plus sur le contexte et la perte de 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 redonner un look à Canvas2D. Il s'agit de l'API voisine qui a toujours été là.