Découvrez la Paint API de CSS Houdini : Libérez votre Créativité Graphique
Lors de la conception d’interfaces web, il est courant de vouloir ajouter des arrière-plans intéressants, dynamiques, et visuellement captivants. Par exemple, nous pourrions vouloir ajouter un arrière-plan rayé à une boîte sur notre page.
Traditionnellement, nous pouvons utiliser un dégradé CSS ou une image d’arrière-plan pour cela. Ces solutions fonctionnent, mais elles présentent des limitations, en particulier lorsqu’on souhaite plus de flexibilité ou de dynamisme.
Cet article fait partie d’une série d’explorations plus approfondies sur les capacités de CSS Houdini, et fait suite à notre introduction générale sur CSS Houdini : Ouvrir la Boîte Noire du Navigateur. Ici, nous allons explorer spécifiquement la Paint API et découvrir comment elle peut nous aider à créer des arrière-plans personnalisés et dynamiques.
Voyons d’abord comment faire avec une approche classique, puis nous découvrirons comment CSS Houdini nous permet de créer quelque chose de bien plus puissant.
Solution Classique : Utilisation des Dégradés CSS
Commençons par une solution classique, en utilisant des dégradés CSS :
.striped-box {
width: 300px;
height: 200px;
background: repeating-linear-gradient(
45deg,
#ff5733,
#ff5733 10px,
#ffffff 10px,
#ffffff 20px
);
border: 1px solid #333;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
color: #333;
font-size: 1.2rem;
}
Dans cet exemple, nous créons des rayures diagonales à l’aide de repeating-linear-gradient
. Cela fonctionne bien si l’on veut créer des motifs statiques.
Cependant, si nous souhaitons changer l’apparence des rayures de manière dynamique, par exemple la largeur ou la couleur, cela devient rapidement complexe. Nous devrions manipuler des règles CSS complexes ou écrire du JavaScript pour modifier directement les styles, ce qui alourdit le code.
C’est là que Houdini entre en scène.
Solution Moderne avec Houdini : Paint API
Avec la Paint API, nous pouvons peindre directement dans le navigateur en utilisant JavaScript, tout en intégrant ce rendu dans notre CSS. Cette approche donne une grande flexibilité, et permet d’introduire des rendus dynamiques et personnalisés, sans dépendre d’images d’arrière-plan statiques. Voici comment cela fonctionne :
Fichier CSS : Initialisation du Worklet : styles.css
Le CSS est utilisé pour définir les propriétés de notre boîte rayée et appliquer l’arrière-plan via paint(striped-background)
, qui fait référence à un worklet de peinture :
.striped-box {
width: 800px;
height: 400px;
background: paint(striped-background);
--stripe-color: #ff5733;
--stripe-width: 15;
border: 1px solid #333;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
color: #333;
font-size: 1.2rem;
}
background: paint(striped-background);
: Utilise un worklet nomméstriped-background
pour dessiner l’arrière-plan.--stripe-color
et--stripe-width
: Ce sont des propriétés CSS personnalisées. Elles définissent respectivement la couleur et la largeur des rayures, que nous pourrons modifier dynamiquement.
Fichier JavaScript Worklet : striped-background.js
Le fichier striped-background.js
est un script de Paint Worklet qui va gérer le dessin de l’arrière-plan. Voici le code :
// Enregistrement d'un worklet de peinture avec Houdini
registerPaint('striped-background', class {
// Spécifier les propriétés CSS qui peuvent être utilisées par le worklet
static get inputProperties() {
return ['--stripe-color', '--stripe-width'];
}
// Méthode paint qui est appelée pour dessiner sur le canvas de l'élément
paint(ctx, size, properties) {
// Récupérer la couleur des rayures depuis la propriété CSS, avec une valeur par défaut
const stripeColor = properties.get('--stripe-color').toString() || 'black';
// Récupérer la largeur des rayures depuis la propriété CSS, avec une valeur par défaut
const stripeWidth = parseInt(properties.get('--stripe-width')) || 10;
// Définir la couleur de remplissage pour les rayures
ctx.fillStyle = stripeColor;
// Dessiner des rectangles pour créer les rayures en boucle
for (let i = 0; i < size.width; i += stripeWidth * 2) {
ctx.fillRect(i, 0, stripeWidth, size.height);
}
}
});
Explications détaillées :
registerPaint('striped-background', class { ... })
: Enregistre un nouveau Paint Worklet sous le nomstriped-background
. Ce nom est ensuite utilisé dans notre CSS pour appliquer le rendu personnalisé.inputProperties()
: Liste les propriétés CSS que le worklet va utiliser. Ici,--stripe-color
et--stripe-width
sont passées comme paramètres, permettant au worklet d’accéder aux valeurs définies dans le CSS.paint(ctx, size, properties)
:ctx
: Contexte de dessin, similaire à celui d’un<canvas>
. Utilisé pour dessiner les rayures.size
: Dimensions de la zone à peindre.properties
: Liste des propriétés CSS accessibles depuisinputProperties()
.
Amélioration : Ajout de l’Interactivité avec JavaScript : script.js
Pour montrer la valeur ajoutée de Houdini, nous avons ajouté de l’interactivité. Nous allons permettre à l’utilisateur de modifier dynamiquement les rayures en fonction du mouvement de la souris sur la boîte :
- En montant la souris : La couleur devient plus froide (bleutée).
- En descendant la souris : La couleur devient plus chaude (rouge).
- En allant à droite : La largeur des rayures augmente.
- En allant à gauche : La largeur des rayures diminue.
Voici le code JavaScript permettant d’ajouter cette interactivité :
document.addEventListener('DOMContentLoaded', () => {
const stripedBox = document.querySelector('.striped-box');
// Ajouter un écouteur d'événement sur le mouvement de la souris
stripedBox.addEventListener('mousemove', (event) => {
const { offsetX, offsetY, target } = event;
// Calculer la largeur des rayures en fonction de la position X de la souris
const stripeWidth = Math.min(60, Math.max(1, Math.floor((offsetX / target.clientWidth) * 60)));
// Calculer la couleur des rayures en fonction de la position Y de la souris
const maxHeight = target.clientHeight;
const percentageHeight = offsetY / maxHeight;
let stripeColor;
if (percentageHeight < 0.5) {
// Couleurs froides (bleues) plus pastel
const blueIntensity = Math.floor(200 - percentageHeight * 2 * 100);
stripeColor = `rgba(0, 0, ${blueIntensity}, 0.35)`;
} else {
// Couleurs chaudes (rouges) plus pastel
const redIntensity = Math.floor((percentageHeight - 0.5) * 2 * 100 + 155);
stripeColor = `rgba(${redIntensity}, 100, 100, 0.35)`;
}
// Appliquer les nouvelles valeurs aux propriétés CSS via element.style.setProperty
stripedBox.style.setProperty('--stripe-width', `${stripeWidth}px`);
stripedBox.style.setProperty('--stripe-color', stripeColor);
});
});
Explications :
mousemove
: Lorsque l’utilisateur déplace la souris sur la boîte, les valeurs de--stripe-width
et--stripe-color
sont recalculées et mises à jour.- Calcul des couleurs pastels : Pour obtenir des couleurs plus douces, nous utilisons un mélange de bleu ou de rouge avec une composante blanche, en utilisant des valeurs
rgba
avec une faible opacité pour adoucir l’intensité des couleurs. - Application des propriétés CSS :
element.style.setProperty()
est utilisé pour modifier les valeurs CSS personnalisées directement, ce qui provoque un redessin par le Paint Worklet.
Pourquoi Séparer les Fichiers JavaScript ?
La séparation entre striped-background.js
(le Worklet de peinture) et script.js
(le script d’interaction utilisateur) est cruciale en raison de leurs rôles différents et des contraintes techniques liées à la Paint API.
Le Paint Worklet est exécuté dans un environnement de type « Web Worker », isolé du contexte principal de la page. Cela signifie qu’il ne peut pas interagir directement avec le DOM ou accéder à des objets globaux comme document
ou window
. Le Paint Worklet se charge uniquement de dessiner l’arrière-plan en fonction des propriétés CSS fournies.
En revanche, le script principal script.js
s’exécute dans le contexte global de la page et peut interagir avec le DOM. C’est ce script qui se charge de réagir aux événements de l’utilisateur, comme le mouvement de la souris, et de modifier les propriétés CSS. Ces modifications sont ensuite relayées au Paint Worklet, qui les utilise pour redessiner l’arrière-plan.
Cette séparation garantit que chaque partie du code reste dans son contexte approprié, permettant des performances optimales et une architecture claire entre la logique d’interaction et la logique de peinture.
HTML : Mise en Place de l’Élément : index.html
Voici le code HTML qui inclut notre boîte rayée ainsi que les scripts nécessaires :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Houdini - Paint API Example</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="striped-box">
Voici une boîte avec un arrière-plan peint grâce à CSS Houdini.
</div>
<script>
// Charger le Paint Worklet pour la Paint API
if (CSS && CSS.paintWorklet) {
CSS.paintWorklet.addModule('striped-background.js');
}
</script>
<script src="script.js"></script> <!-- Script principal pour l'interaction utilisateur -->
</body>
</html>
Note : Le script pour charger le worklet striped-background.js
est essentiel pour que le navigateur sache comment peindre l’arrière-plan.
Conclusion : La Magie de la Paint API
En utilisant CSS Houdini, nous avons pu créer un arrière-plan entièrement dynamique et interactif, directement dessiné par le navigateur, sans nécessiter d’images d’arrière-plan ou de manipulations complexes du DOM.
- Flexibilité : Les propriétés CSS sont modifiables dynamiquement, permettant des effets visuels réactifs sans surcharger le DOM.
- Performances : En déléguant la peinture directement au moteur CSS via un Paint Worklet, nous permettons au navigateur d’optimiser le rendu, offrant une fluidité accrue, surtout lors de modifications fréquentes comme les déplacements de souris.
- Design Simplifié : L’usage des propriétés CSS personnalisées permet une meilleure lisibilité du code et une séparation claire entre le style, l’interaction, et la logique de peinture.
Avec Houdini, le CSS devient plus puissant et plus dynamique, ce qui nous ouvre des perspectives infinies en matière de créativité dans la conception d’interfaces web.
Vous pouvez explorer cet exemple sur https://samples.puce-et-media.com/css-houdini/paint/