Tutoriel CSS Houdini

Bonjour mes amis!

Qu'est-ce qu'un Houdini?


Houdini ( Houdini ) - une collection d'API de navigateur qui améliorent considérablement le processus de développement Web, y compris le développement de normes CSS. Les développeurs pourront étendre CSS à l'aide de JavaScript, influencer le rendu CSS et indiquer au navigateur comment les styles doivent être appliqués. Cela fournira une augmentation significative des performances et de la stabilité par rapport à l'utilisation de polyphiles.

Houdini se compose de deux groupes d'API: les API de haut niveau et les API de bas niveau.

Des API de haut niveau sont associées au processus de rendu (style - mise en page - dessin - composition). Ce groupe comprend:

  • API Paint - vous permet d'étendre CSS à l'étape (c'est-à-dire l'étape de rendu) du rendu des éléments visuels (couleur, arrière-plan, bordures, etc.).
  • API de mise en page - vous permet d'étendre CSS à l'étape de déterminer la taille, la position et l'alignement des éléments.
  • API d'animation - «point d'extension» dans l'étape d'affichage et d'animation des éléments.

Les API de bas niveau sont le fondement des API de niveau supérieur et comprennent:

  • API de modèle d'objet typé
  • API Propriétés et valeurs personnalisées
  • API de mesure des polices
  • Worklets

Futur CSS


Houdini, contrairement au CSS classique, permet aux développeurs d'étendre le CSS de manière plus naturelle. Cela signifie-t-il que les spécifications CSS cesseront d'évoluer et que de nouvelles normes seront adoptées? Pas du tout. L'objectif de Houdini est d'aider à développer de nouvelles fonctionnalités CSS par la création de prototypes fonctionnels qui peuvent facilement être standardisés.

De plus, les développeurs peuvent facilement partager des worklets CSS ouverts sans se soucier de la compatibilité.

API TOM (Typed Object Model)


Avant Houdini, la seule façon d'interagir avec JS et CSS était de convertir CSS en chaîne et de la modifier. L'analyse et la redéfinition manuelles des styles peuvent être complexes et sujettes aux erreurs en raison de la nécessité d'une double conversion du type de valeur (par exemple, d'un nombre en une chaîne et vice versa). Vous devez également spécifier manuellement les unités de mesure pour la nouvelle valeur.

selectedElement.style.fontSize = newFontSize + 'px' // newFontSize = 20
console.log(selectedElement.style.fontSize) // 20px

TOM donne aux propriétés CSS une signification plus sémantique en les représentant comme des objets JS typés. Cela améliore considérablement les performances, la stabilité et facilite la prise en charge du code. Les valeurs CSS sont représentées par l'interface CSSUnitValue, qui se compose de la valeur et de la propriété «unité de mesure».

{
  value: 20,
  unit: 'px'
}

Cette interface peut être utilisée avec les nouvelles fonctionnalités suivantes:

  • computedStyleMap (): pour analyser les styles calculés (non intégrés). Cette méthode est appelée avant d'analyser ou d'utiliser d'autres méthodes.
  • attributeStyleMap: pour analyser et modifier les styles en ligne. Il s'agit d'une propriété d'élément.

//       ( )
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//   
selectedElement.attributeStyleMap.set('font-size', CSS.em(2))
selectedElement. attributeStyleMap.set('color', 'blue')

//    
selectedElement.computedStyleMap().get('font-size') // { value: 20, unit: 'px' }

//    
selectedElement.attributeStyleMap.get('font-size') // { value: 2, unit: 'em' }

Notez comment les types CSS sont utilisés lors de l'attribution d'une nouvelle valeur. L'utilisation de cette syntaxe évite les problèmes associés aux types et le code résultant devient plus sécurisé.

L'API à l'étude inclut non seulement get et set, mais également d'autres méthodes, par exemple:

  • clear: supprime tous les styles en ligne
  • delete: supprime une propriété CSS spécifique et sa valeur
  • a: retourne vrai / faux selon la disponibilité de la propriété spécifiée
  • ajouter: ajoute une valeur supplémentaire à une propriété qui prend en charge plusieurs valeurs

Détection


let selectedElement = document.getElementById('example')

if(selectedElement.attributeStyleMap){
  // ...
}

if(selectedElement.computedStyleMap){
  // ...
}

Statut de spécification


Ébauche de travail : publiée pour discussion avec la communauté.

Soutien


ChromeBordOpéraFirefoxSafari
Supporté parSupporté parSupporté parNon supportéSupport partiel

API Propriétés et valeurs personnalisées


Cette API permet aux développeurs d'étendre les variables CSS en définissant le type, la valeur initiale et l'héritage. Pour définir une propriété personnalisée, il est nécessaire de l'enregistrer à l'aide de la méthode registerProperty. Cette méthode détermine comment les navigateurs doivent appliquer la propriété et gérer les erreurs.

CSS.registerProperty({
  name: '--colorPrimary',
  syntax: '<color>',
  inherits: false,
  initialValue: 'blue',
})

Un objet avec les propriétés suivantes est passé à cette méthode comme argument:

  • nom: nom de la propriété personnalisée
  • syntaxe: instruction d'analyse. Les valeurs prédéfinies sont: <couleur>, <entier>, <numéro>, <longueur>, <pourcentage>, etc.
  • initialValue: valeur par défaut (avant la substitution, ainsi que lorsque des erreurs se produisent)

Dans l'exemple ci-dessus, une propriété personnalisée de type <couleur> a été définie. Cette propriété sera utilisée pour déterminer le gradient. CSS standard ne prend pas en charge les transitions de dégradé. Remarquez comment la propriété personnalisée sera utilisée pour définir la transition.

.gradient-box {
  background: linear-gradient(45deg, rgba(255, 255, 255, 1) 0% var(--colorPrimary) 60%);
  transition: --colorPrimary 0.5s ease;
  ...
}

.gradient-box:hover {
  --colorPrimary: red;
  ...
}

Le navigateur ne sait pas comment faire la transition pour le dégradé, mais il sait comment le faire pour la couleur. C'est pourquoi nous avons défini le type de propriété comme <couleur>. Dans un navigateur qui prend en charge Houdini, un changement de dégradé se produit lorsque vous survolez. La position du dégradé, mesurée en pourcentage, peut également être modifiée à l'aide de la propriété personnalisée CSS (enregistrée sous <percentage>).

À l'avenir, il sera probablement possible d'enregistrer une propriété personnalisée directement dans CSS.

@property --colorPrimary {
  syntax: '<color>';
  inherits: false;
  initial-value: blue;
}

Exemple


Cet exemple simple montre comment modifier la couleur et les points de contrôle d'un dégradé à l'aide de propriétés CSS personnalisées. Un exemple de code peut être trouvé ici .



Détection


if(CSS.registeredProperty) {
  // ...
}

Statut de spécification


Ébauche de travail : publiée pour discussion avec la communauté.

Soutien


ChromeBordOpéraFirefoxSafari
Supporté parSupporté parSupporté parNon supportéNon supporté

API de mesure des polices


Cette API est à un stade précoce de développement, de sorte que la spécification pourrait changer considérablement à l'avenir. Il fournit actuellement des méthodes pour mesurer la taille des éléments de texte affichés à l'écran, permettant aux développeurs d'influencer le rendu des caractères. Les fonctionnalités CSS existantes ne vous permettent pas de travailler avec ces valeurs ou de rendre ce travail très difficile. Un exemple d'utilisation de cette API est la troncature dynamique de plusieurs lignes de texte.

Statut de spécification


Collection d'idées : projet non publié.

Les navigateurs ne sont pas pris en charge.

Vorkleta


Avant de passer aux API suivantes, vous devez comprendre le concept de worklets. Les vorklets sont des scripts qui s'exécutent pendant le rendu et sont indépendants du code JS sous-jacent. Ils étendent les capacités du moteur de rendu, sont conçus pour une exécution parallèle (2 instances ou plus), ne bloquent pas le thread principal, ont un accès limité à la portée globale et sont appelés par le moteur si nécessaire. Les vorklets ne peuvent être exécutés que via HTTPS (en production) ou via localhost (à des fins de développement et de test).

Houdini inclut les worklets suivants qui étendent le moteur de rendu du navigateur:

  • Paint Worklet - Paint API
  • Animation Worklet - Animation API
  • Layout Worklet - Layout API

API Paint


L'API Paint permet aux développeurs d'utiliser les fonctions JS pour peindre l'arrière-plan, les bordures ou le contenu d'un élément à l'aide du contexte de rendu 2D, qui est un sous-ensemble de l'API HTML5 Canvas. L'API Paint utilise le Paint Worklet pour dessiner une image qui dépend des modifications apportées au CSS (telles que les modifications apportées aux variables CSS). Ceux qui connaissent l'API Canvas se sentiront comme chez eux avec l'API Paint.

La création d'un worklet de peinture comprend plusieurs étapes:

  1. Écrivez et enregistrez un worklet à l'aide de la fonction registerPaint
  2. Appelez un worklet en HTML ou JS à l'aide de CSS.paintWorklet.addModule
  3. Utilisez la méthode paint () en CSS avec le nom du vorklet et les arguments passés

Examinons la fonction registerPaint, qui est utilisée pour enregistrer et déterminer la fonctionnalité du Paint Worklet.

registerPaint('paintWorkletExample', class {
  static get inputProperties() { return ['--myVariable']; }
  static get inputArguments() { return ['<color>']; }
  static get contextOptions() { return {alpha: true} }

  paint(ctx, size, properties, args) {
    // ...
  }
})

La fonction registerPaint se compose des parties suivantes:

  • inputProperties: tableau de propriétés CSS personnalisées observées par le worklet. Ce tableau représente les dépendances du worklet.
  • inputArguments: un tableau d'arguments qui peut être passé d'une fonction à un CSS externe
  • contextOptions: transparence des couleurs. Si faux, toutes les couleurs seront complètement opaques
  • paint: la fonction principale prenant les arguments suivants:

    • ctx: un contexte de dessin 2D presque identique au contexte de canevas 2D de l'API Canvas
    • taille: un objet avec la largeur et la hauteur de l'élément. Les valeurs dépendent du processus de rendu de la mise en page. La taille de la toile est identique à la taille réelle de l'article
    • propriétés: variables contenues dans inputProperties
    • args: un tableau d'arguments transmis à la fonction paint

Après avoir enregistré le worklet, il doit être appelé en HTML, indiquant le chemin d'accès au fichier.

CSS.paintWorklet.addModule('path/to/worklet/file.js')

Les vorklets peuvent être ajoutés à partir de n'importe quelle source externe (par exemple, à partir de CDN), ce qui les rend modulaires et réutilisables.

CSS.paintWorklet.addModule('https://url/to/worklet/file.js')

Après avoir appelé le worklet, il peut être utilisé en CSS à l'aide de la fonction «paint». Cette fonction, en tant que premier paramètre, prend le nom enregistré du vorklet et tous les arguments spécifiés dans inputArguments. À partir de ce moment, le navigateur sait quand appeler le worklet et quelles actions de l'utilisateur entraînent la modification de certaines valeurs des propriétés personnalisées CSS.

.example-element {
  // paintWorkletExample -  
  // blue - ,  
  background: paint(paintWorkletExample, blue);
}

Exemple


L'exemple suivant illustre l'utilisation de l'API Paint, ainsi que la modularité et la réutilisation des worklets. Il utilise le worklet d'entraînement du référentiel Google Chrome Labs . Voir l'exemple de code ici .



Détection


if(‘paintWorklet’ in CSS){
  // …
}

@supports(background: paint(paintWorkletExample)){
  // …
}

Statut de spécification


Recommandation : Un projet de travail stable, prêt à l'emploi.

Soutien


ChromeBordOpéraFirefoxSafari
Supporté parSupporté parSupporté parNon supportéNon supporté

API d'animation


Cette API étend les animations Web à travers le traitement de divers événements (défilement, survol, clic, etc.) et améliore les performances en lançant des animations dans son propre flux via le worklet d'animation.

Comme tout autre worklet, une animation d'entraînement doit d'abord être enregistrée.

registerAnimation(‘animationWorkletExample’, class {
  constructor(options){
    // …
  }
  animate(currentTime, effect){
    // …
  }
})

Cette classe comprend deux fonctions:

  • constructeur: appelé lorsqu'une nouvelle instance est créée. Utilisé pour la configuration générale.
  • animer: la fonction principale contenant la logique d'animation. Les paramètres suivants sont acceptés:

    • currentTime: horodatages sur une chronologie spécifique
    • effet: un tableau d'effets utilisé dans l'animation

Après l'enregistrement, le worklet est inclus dans le fichier JS principal, une animation (élément, images, paramètres) est ajoutée et attachée à la chronologie. Le concept des marques de chronologie et les bases de l'animation Web sont dans la section suivante.

//   
await CSS.animationWorklet.addModule(‘path/to/worklet/file.js’)

//    
const elementExample = document.getElementById(‘element-example’)

//  
const effectExample = new KeyframeEffect(
  elementExample, //  
  [ // … ], //  
  { // … } //  - , ,    ..
)

//        
new WorkletAnimation(
  ‘animationWorkletExample’ //  
  effectExample, //  
  document.timeline, //  
  {},  //   
).play()

Horodatages


Les animations Web sont basées sur des horodatages - des jalons pour les effets sur la chronologie de l'animation. Par exemple, nous analysons une animation linéaire répétitive, qui se compose de trois images (début, milieu, fin), démarre 1 seconde après le chargement complet de la page (délai) et dure 4 secondes.

L'horodatage des effets ressemblera à ceci (pour une animation de 4 secondes et sans délai):

HorodatagesCadre d'animation
0 msLa première image - le début de l'animation
2000msLa deuxième image - le milieu de l'animation
4000msDernière image - fin de l'animation ou réinitialisation à la première image

effect.localTime avec une valeur de 3000ms (avec un délai de 1000ms) lie l'animation à l'image moyenne sur la timeline (délai de 1000ms + image moyenne de 2000ms). Le même effet sera obtenu lors du réglage de 7000 ms et 11 000 ms, car l'animation se répète tous les 4000 ms.

animate(currentTime, effect){
  effect.localTime = 3000 // 1000ms  + 2000ms  
}

Avec une valeur constante d'effet.localTime, l'animation sera verrouillée sur une image spécifique. Par conséquent, la valeur de effect.localTime doit changer. Cette valeur doit être une fonction liée à currentTime ou à une autre variable.

Voici à quoi ressemble le code d'animation linéaire:

animate(currentTime, effect){
  effect.localTime = currentTime // y = x  
}

Chronologie (document.timeline)HorodatageCadre
startTime + 0ms (temps écoulé)startTime + 0msLa première
startTime + 1000ms (temps écoulé)startTime + 1000ms (retard) + 0msLa première
startTime + 3000ms (temps écoulé)startTime + 1000ms (retard) + 2000msMilieu
startTime + 5000ms (temps écoulé)startTime + 1000ms (retard) + 4000msDerniers seront les premiers
startTime + 7000ms (temps écoulé)startTime + 1000ms (retard) + 6000msMilieu
startTime + 9000ms (temps écoulé)startTime + 1000ms (retard) + 8000msDerniers seront les premiers

Les horodatages ne sont pas limités à 1: 1. L'API d'animation permet aux développeurs de manipuler les marques via la fonction d'animation, en utilisant les fonctions JS standard pour créer des effets complexes. Les animations peuvent également varier à chaque itération (avec des animations répétables).

L'animation peut être liée non seulement au chargement du document, mais également aux actions de l'utilisateur. Une action de l'utilisateur telle que le défilement d'une page peut être utilisée dans l'animation via un objet ScrollTimeline. Par exemple, une animation peut commencer lorsque vous faites défiler 200 pixels et se terminer lorsque vous faites défiler 800 pixels.

const scrollTimelineExample = new ScrollTimeline({
  scrollSource: scrollElement, // ,     
  orientation: ‘vertical’, //  
  startScrollOffset: ‘200px’, //  
  endScrollOffset: ‘800px’, //  
  timeRange: 1200, //  
  fill: ‘forwards’ //  
})

L'animation s'adapte automatiquement à la vitesse de défilement, tout en restant fluide et réactive. Étant donné que l'animation s'exécute dans son propre flux et se connecte au moteur de rendu du navigateur, elle démarre en douceur et n'affecte pas les performances.

Exemple


L'exemple suivant illustre une animation non linéaire. Il utilise la fonction gaussienne avec la même rotation de temps en arrière et en arrière. Voir l'exemple de code ici .



Détection


if(CSS.animationWorklet){
  // …
}

Statut de spécification


Premier projet de travail public : prêt pour une discussion avec la communauté, sujet à changement à l'avenir.

Soutien


ChromeBordOpéraFirefoxSafari
Support partielSupport partielSupport partielNon supportéNon supporté

API de mise en page


L'API de mise en page permet aux développeurs d'étendre le processus de rendu de mise en page en définissant de nouveaux modules à utiliser dans la propriété CSS «display». Cette API présente de nouveaux concepts, est très complexe et offre un grand nombre de paramètres pour développer des algorithmes personnalisés pour travailler avec la mise en page.

Tout d'abord, un worklet doit être enregistré.

registerLayout(‘exampleLayout’, class{
  static get inputProperties() { return [‘--example-variable’] }

  static get childrenInputProperties() { return [‘--exampleChildVariable’] }

  static get layoutOptions(){
    return {
      childDisplay: ‘normal’,
      sizing: ‘block-like’
    }
  }

  intrinsicSizes(children, edges, styleMap){
    // …
  }

  layout(children, edges, constraints, styleMap, breakToken){
    // …
  }
})

L'enregistrement d'un worklet comprend les méthodes suivantes:

  • inputProperties: un tableau de propriétés CSS personnalisées qui sont surveillées par le worklet et qui appartiennent au parent, l'élément qui a provoqué le rendu de la présentation. Ce tableau représente les dépendances de disposition
  • childrenInputProperties: un tableau de propriétés CSS personnalisées qui sont surveillées par le widget et qui appartiennent au descendant
  • layoutOptions: définit les propriétés de disposition suivantes:

    • childDisplay: Les valeurs prédéfinies sont block et normal. Définit la façon dont l'élément est affiché (bloc ou ligne)
    • dimensionnement: les valeurs prédéfinies sont de type bloc et manuelles. Détermine la nécessité d'un calcul préliminaire de la taille des éléments (si non spécifié)
  • intrinsicSizes: définit la façon dont le conteneur ou son contenu sont affichés dans le contexte de mise en page:

    • enfants: enfant de l'élément qui a rendu la mise en page
    • bord: bord du conteneur
    • styleMap: modèle d'objet de style conteneur typé
  • mise en page: la fonction principale pour travailler avec la mise en page:

    • enfants: élément enfant
    • arêtes: bordures
    • contraintes: contraintes imposées par la disposition parent
    • styleMap: modèle d'objet de style conteneur typé
    • breakToken: point d'arrêt pour la pagination ou le fractionnement de la mise en page d'impression

Ensuite, le worklet est ajouté au fichier HTML ou JS.

CSS.layoutWorklet.addModule(‘path/to/worklet/file.js’)

Nous faisons un lien vers le worklet dans le fichier avec des styles.

.example-element {
  display: layout(exampleLayout)
}

Fonctionnement de l'API de mise en page avec la mise en page


Dans l'exemple précédent, nous avons défini exampleLayout.

Un élément .example est appelé une disposition parent, y compris les retraits, les bordures et les curseurs de défilement. Une disposition parent se compose d'éléments enfants appelés dispositions actuelles. Les présentations actuelles sont des éléments cibles dont les présentations sont «personnalisées» à l'aide de l'API de présentation. Par exemple, lorsque vous utilisez «display: flex», les descendants de l'élément sont réorganisés selon la disposition flexible. Ceci est similaire au fonctionnement de l'API de mise en page.

Chaque disposition actuelle se compose de dispositions enfant contenant des algorithmes pour rendre la disposition de l'élément enfant - LayoutChild (y compris les pseudo-classes :: avant et :: après). LayoutChild - un conteneur généré par des outils CSS contenant des données sur les styles (sans données sur la mise en page). Les éléments LayoutChild sont automatiquement créés par le navigateur au stade de l'application des styles. Une disposition enfant peut créer un fragment contenant des instructions de rendu de la disposition.

Exemple


Cet exemple utilise également le référentiel Google Chrome Labs , mais le texte est remplacé par des images. Voir l'exemple de code ici .



Détection


if(CSS.layoutWorklet){
  // …
}

Statut de spécification


Premier projet de travail public : prêt pour une discussion avec la communauté, sujet à changement à l'avenir.

Soutien


ChromeBordOpéraFirefoxSafari
Support partielSupport partielSupport partielNon supportéNon supporté

Houdini et l'amélioration progressive


Malgré le fait que Houdini ne dispose pas actuellement d'un support de navigateur optimal, il peut être utilisé pour une amélioration progressive. Si vous n'êtes pas familier avec le concept d'amélioration progressive, je vous conseille de jeter un œil à cet article . Lors de l'utilisation de Houdini, les éléments suivants doivent être pris en compte:

Identifiez toujours le support pour éviter les erreurs.


Chaque API et Worklet Houdini a un moyen simple de vérifier l'accessibilité. Cela évite les problèmes d'utilisation de Houdini dans les navigateurs qui ne prennent pas encore en charge cette technologie.

Utilisez Houdini pour améliorer l'affichage et la visualisation.


Les utilisateurs utilisant des navigateurs qui ne prennent pas en charge Houdini doivent avoir accès au contenu et aux fonctionnalités de base du site. L'expérience utilisateur et l'affichage du contenu ne doivent pas dépendre de Houdini.

Utiliser le CSS standard comme alternative


Par exemple, les propriétés CSS personnalisées peuvent être utilisées comme alternative à l'API des propriétés et valeurs personnalisées.

Concentrez-vous sur le développement d'applications productives et fiables, en utilisant Houdini à des fins décoratives uniquement comme une amélioration progressive.

Conclusion


Houdini permet aux développeurs d'utiliser du code JS pour travailler avec des styles dans le processus de rendu, augmentant ainsi les performances et la stabilité des applications. La possibilité d'incorporer du code dans le processus de rendu vous permet de créer des polyphiles CSS faciles à partager avec d'autres, à appliquer et éventuellement à inclure dans la spécification. De plus, Houdini permet aux développeurs et aux concepteurs d'être moins dépendants des restrictions CSS lorsqu'ils travaillent avec des styles, des mises en page et des animations.

Houdini peut être utilisé maintenant, mais uniquement comme une amélioration progressive. Cela permettra aux navigateurs qui ne prennent pas en charge Houdini d'afficher des pages sans erreur, offrant une expérience utilisateur optimale.

Je ne peux pas attendre que la communauté des développeurs puisse profiter pleinement des capacités de Houdini. Voici quelques exemples:

Exigences CSS Houdini
Une introduction interactive aux exemples CSS Houdini Houdini
de Google Chrome Labs

Références


W3C Brouillons de la spécification Houdini
Houdini (Chrome Dev Summit 2018)
Worklet d'animation - Développeurs Google

Merci de votre attention.

All Articles