Nouvelles possibilités dans Chrome 65
L'API CSS Paint (également appelée "CSS Custom Paint" ou "Worklet de peinture de Houdini") est activée par défaut à partir de Chrome 65. De quoi s'agit-il ? À quoi sert-il ? Et comment ça marche ? Eh bien, lisez la suite :
L'API CSS Paint vous permet de générer une image de manière programmatique chaque fois qu'une propriété CSS attend une image. Les propriétés telles que background-image
ou border-image
sont généralement utilisées avec url()
pour charger un fichier image ou avec des fonctions intégrées CSS telles que linear-gradient()
. Au lieu de les utiliser, vous pouvez désormais utiliser paint(myPainter)
pour référencer un worklet de peinture.
Écrire un worklet de peinture
Pour définir un worklet de peinture appelé myPainter
, nous devons charger un fichier de worklet de peinture CSS à l'aide de CSS.paintWorklet.addModule('my-paint-worklet.js')
. Dans ce fichier, nous pouvons utiliser la fonction registerPaint
pour enregistrer une classe de worklet de peinture:
class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter);
Dans le rappel paint()
, nous pouvons utiliser ctx
de la même manière que nous le ferions avec un CanvasRenderingContext2D
tel que nous le connaissons de <canvas>
. Si vous savez dessiner dans un <canvas>
, vous pouvez dessiner dans un worklet de peinture. geometry
indique la largeur et la hauteur du canevas à notre disposition. properties
Je vous expliquerai plus tard dans cet article.
Pour commencer, écrivons un worklet de peinture en damier et utilisons-le comme image de fond d'un <textarea>
. (J'utilise un textarea, car il est redimensionnable par défaut.):
<!-- index.html -->
<!doctype html>
<style>
textarea {
background-image: paint(checkerboard);
}
</style>
<textarea></textarea>
<script>
CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
paint(ctx, geom, properties) {
// Use `ctx` as if it was a normal canvas
const colors = ['red', 'green', 'blue'];
const size = 32;
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
const color = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.fillStyle = color;
ctx.rect(x * size, y * size, size, size);
ctx.fill();
}
}
}
}
// Register our class under a specific name
registerPaint('checkerboard', CheckerboardPainter);
Si vous avez déjà utilisé <canvas>
, ce code devrait vous sembler familier. Regardez la démonstration en direct ici.
La différence avec l'utilisation d'une image de fond commune est que le motif sera redessiné à la demande, chaque fois que l'utilisateur redimensionnera le champ de texte. Cela signifie que l'image de fond est toujours exactement aussi grande qu'elle doit l'être, y compris pour compenser les écrans haute densité.
C'est plutôt cool, mais c'est aussi assez statique. Voulez-vous écrire un nouveau worklet chaque fois que vous souhaitez le même motif, mais avec des carrés de taille différente ? La réponse est non.
Paramétrer votre worklet
Heureusement, le worklet de peinture peut accéder à d'autres propriétés CSS, c'est là que le paramètre supplémentaire properties
entre en jeu. En attribuant à la classe un attribut inputProperties
statique, vous pouvez vous abonner aux modifications apportées à n'importe quelle propriété CSS, y compris les propriétés personnalisées. Les valeurs vous seront fournies via le paramètre properties
.
<!-- index.html -->
<!doctype html>
<style>
textarea {
/* The paint worklet subscribes to changes of these custom properties. */
--checkerboard-spacing: 10;
--checkerboard-size: 32;
background-image: paint(checkerboard);
}
</style>
<textarea></textarea>
<script>
CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
// inputProperties returns a list of CSS properties that this paint function gets access to
static get inputProperties() { return ['--checkerboard-spacing', '--checkerboard-size']; }
paint(ctx, geom, properties) {
// Paint worklet uses CSS Typed OM to model the input values.
// As of now, they are mostly wrappers around strings,
// but will be augmented to hold more accessible data over time.
const size = parseInt(properties.get('--checkerboard-size').toString());
const spacing = parseInt(properties.get('--checkerboard-spacing').toString());
const colors = ['red', 'green', 'blue'];
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
ctx.fillStyle = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.rect(x*(size + spacing), y*(size + spacing), size, size);
ctx.fill();
}
}
}
}
registerPaint('checkerboard', CheckerboardPainter);
Nous pouvons maintenant utiliser le même code pour tous les types de damiers. Mais mieux encore, nous pouvons maintenant accéder à DevTools et ajuster les valeurs jusqu'à ce que nous trouvions le look approprié.
Navigateurs non compatibles avec le worklet de peinture
Au moment de la rédaction de cet article, seul Chrome a implémenté le worklet de peinture. Bien que des signaux positifs soient émis par tous les autres fournisseurs de navigateurs, les progrès sont peu nombreux. Pour vous tenir informé, consultez régulièrement Est-ce que Houdini est prêt ? En attendant, veillez à utiliser l'amélioration progressive pour que votre code continue de s'exécuter même si le travaillet de peinture n'est pas compatible. Pour vous assurer que tout fonctionne comme prévu, vous devez ajuster votre code à deux endroits: le CSS et le JS.
Pour détecter la prise en charge du worklet de peinture en JS, vous pouvez vérifier l'objet CSS
:
js
if ('paintWorklet' in CSS) {
CSS.paintWorklet.addModule('mystuff.js');
}
Pour le côté CSS, vous avez deux options. Vous pouvez utiliser @supports
:
@supports (background: paint(id)) {
/* ... */
}
Une astuce plus compacte consiste à utiliser le fait que le CSS invalide et ignore ensuite l'ensemble d'une déclaration de propriété s'il contient une fonction inconnue. Si vous spécifiez une propriété deux fois (d'abord sans le worklet de peinture, puis avec le worklet de peinture), vous obtenez une amélioration progressive:
textarea {
background-image: linear-gradient(0, red, blue);
background-image: paint(myGradient, red, blue);
}
Dans les navigateurs compatibles avec le worklet de peinture, la deuxième déclaration de background-image
écrase la première. Dans les navigateurs sans prise en charge des worklets de peinture, la deuxième déclaration n'est pas valide et sera supprimée, laissant la première déclaration en vigueur.
Polyfill de peinture CSS
Pour de nombreuses utilisations, vous pouvez également utiliser le polyfill CSS Paint, qui ajoute la compatibilité avec CSS Custom Paint et Paint Worklets aux navigateurs modernes.
Cas d'utilisation
Il existe de nombreux cas d'utilisation des worklets de peinture, certains plus évidents que d'autres. L'une des plus évidentes consiste à utiliser un worklet de peinture pour réduire la taille de votre DOM. Souvent, des éléments sont ajoutés uniquement pour créer des embellissements à l'aide de CSS. Par exemple, dans Material Design Lite, le bouton avec l'effet de l'ondulation contient deux éléments <span>
supplémentaires pour implémenter l'ondulation elle-même. Si vous avez beaucoup de boutons, cela peut représenter un grand nombre d'éléments DOM et entraîner une dégradation des performances sur mobile. Si vous implémentez l'effet d'ondulation à l'aide d'un worklet de peinture, vous n'obtenez aucun élément supplémentaire et un seul worklet de peinture.
De plus, vous disposez d'un élément beaucoup plus facile à personnaliser et à paramétrer.
Un autre avantage de l'utilisation d'un worklet de peinture est que, dans la plupart des cas, une solution utilisant un worklet de peinture est petite en termes d'octets. Bien entendu, il y a un compromis: votre code de peinture s'exécute chaque fois que la taille du canevas ou l'un des paramètres change. Par conséquent, si votre code est complexe et prend du temps, il peut entraîner des à-coups. Chrome travaille à déplacer les worklets de peinture du thread principal afin que même les worklets de peinture de longue durée n'affectent pas la réactivité du thread principal.
Pour moi, la perspective la plus excitante est que le worklet de peinture permet un polyfillage efficace des fonctionnalités CSS qu'un navigateur ne possède pas encore. Par exemple, vous pouvez utiliser un polyfill pour les dégradés coniques jusqu'à ce qu'ils soient disponibles en mode natif dans Chrome. Autre exemple: lors d'une réunion CSS, il a été décidé que vous pouvez désormais utiliser plusieurs couleurs de bordure. Pendant cette réunion, mon collègue Ian Kilpatrick a écrit un polyfill pour ce nouveau comportement CSS à l'aide d'un worklet de peinture.
Penser "out of the box"
La plupart des utilisateurs commencent à penser aux images de fond et aux images de bordure lorsqu'ils découvrent le worklet de peinture. Un cas d'utilisation moins intuitif du worklet de peinture est mask-image
pour donner aux éléments DOM des formes arbitraires. Par exemple, un diamant:
mask-image
utilise une image de la taille de l'élément. Dans les zones où l'image du masque est transparente, l'élément est transparent. Zones où l'image du masque est opaque, élément opaque.
Disponible dans Chrome
Le worklet Paint est disponible dans Chrome Canary depuis un certain temps. Avec Chrome 65, il est activé par défaut. N'hésitez pas à tester les nouvelles possibilités offertes par le nouveau module de peinture et à nous montrer ce que vous avez créé. Pour plus d'inspiration, consultez la collection de Vincent De Oliveira.