Houdini – Demystifying CSS

Avez-vous déjà pensé à la quantité de travail que le CSS effectue ? Vous modifiez un seul attribut et soudainement, l'ensemble de votre site Web apparaît dans une mise en page différente. C'est un peu magique. Jusqu'à présent, la communauté des développeurs Web n'a pu qu'observer et observer cette magie. Et si nous voulions trouver notre propre magie ? Et si nous voulions devenir les magiciens ?

Entrez Houdini !

Le groupe de travail Houdini est composé d'ingénieurs de Mozilla, Apple, Opera, Microsoft, HP, Intel et Google qui travaillent ensemble pour présenter certaines parties du moteur CSS aux développeurs Web. Le groupe de travail travaille sur une collection de brouillons dans le but de les faire accepter par le W3C et devenir de véritables normes Web. Ils se sont fixé quelques objectifs de haut niveau et les ont transformés en brouillons de spécifications qui ont donné naissance à un ensemble de brouillons de spécifications de niveau inférieur.

La collection de ces brouillons correspond généralement à ce que l'on appelle généralement "Houdini". Au moment de la rédaction de ce document, la liste des brouillons est incomplète, et certains d'entre eux ne sont que de simples espaces réservés.

Spécifications

Worklets (spec)

Les Worklets en eux-mêmes ne sont pas vraiment utiles. Il s'agit d'un concept introduit pour rendre possibles de nombreuses ébauches ultérieures. Si vous pensiez à Web Workers lorsque vous lisez "worklet", vous avez raison. Ils présentent de nombreux chevauchements conceptuels. Alors pourquoi une nouvelle solution alors que nous avons déjà des nœuds de calcul ?

L'objectif d'Houdini est de proposer de nouvelles API pour permettre aux développeurs Web de connecter leur propre code au moteur CSS et aux systèmes environnants. Il n'est probablement pas irréaliste de supposer que certains de ces fragments de code devront être exécutés chaque. frame. Certains d'entre eux doivent par définition. Mention de la spécification Web Worker:

Cela signifie que les nœuds de calcul Web ne sont pas viables pour les choses que Houdini prévoit de faire. C'est pourquoi les Worklets ont été inventés. Les Worklets utilisent les classes ES2015 pour définir un ensemble de méthodes, dont les signatures sont prédéfinies par leur type. Ils sont légers et de courte durée.

API CSS Paint (spec)

L'API Paint est activée par défaut dans Chrome 65. Lisez l'introduction détaillée.

Worklet de compositeur

L'API décrite ici est obsolète. Le Worklet de compositeur a été repensé et est désormais proposé sous le nom de "Worklet d'animation". Apprenez-en plus sur l'itération actuelle de l'API.

Même si la spécification du Worklet du compositeur a été déplacée vers WICG et fera l'objet d'itérations, c'est celle qui m'intéresse le plus. Certaines opérations sont externalisées sur la carte graphique de votre ordinateur par le moteur CSS, bien que cela dépende à la fois de votre carte graphique et de votre appareil en général.

Un navigateur utilise généralement l'arborescence DOM et, en fonction de critères spécifiques, décide de donner à certaines branches et sous-arborescences leur propre couche. Ces sous-arbres se peignent dessus (peut-être qu'ils utiliseront un atelier de peinture à l'avenir). Pour finir, tous ces calques individuels, maintenant peints, sont empilés et positionnés les uns sur les autres, conformément aux z-indices, aux transformations 3D et autres, afin d'obtenir l'image finale visible à l'écran. Ce processus, appelé composition, est exécuté par le compositeur.

L'avantage du processus de composition est qu'il n'est pas nécessaire de repeindre tous les éléments lorsque la page défile un peu. À la place, vous pouvez réutiliser les calques du cadre précédent et simplement réexécuter le compositeur avec la position de défilement mise à jour. Cela accélère les choses. Cela nous aide à atteindre 60 ips.

Worklet de compositeur.

Comme son nom l'indique, le compositeur vous permet de vous rattacher au compositeur et d'influencer la façon dont le calque d'un élément, qui a déjà été peint, est positionné et superposé aux autres calques.

Pour être un peu plus précis, vous pouvez indiquer au navigateur que vous souhaitez vous impliquer dans le processus de composition pour un certain nœud DOM et demander l'accès à certains attributs tels que la position de défilement, transform ou opacity. Cela force cet élément sur sa propre couche et sur chaque image, votre code est appelé. Vous pouvez déplacer votre couche en manipulant les couches transformées et modifier ses attributs (comme opacity), ce qui vous permet d'effectuer des tâches sophistiquées à 60 FPS.

Voici une implémentation complète du défilement parallaxe à l'aide du workflow du compositeur.

// main.js
window.compositorWorklet.import('worklet.js')
    .then(function() {
    var animator = new CompositorAnimator('parallax');
    animator.postMessage([
        new CompositorProxy($('.scroller'), ['scrollTop']),
        new CompositorProxy($('.parallax'), ['transform']),
    ]);
    });

// worklet.js
registerCompositorAnimator('parallax', class {
    tick(timestamp) {
    var t = self.parallax.transform;
    t.m42 = -0.1 * self.scroller.scrollTop;
    self.parallax.transform = t;
    }

    onmessage(e) {
    self.scroller = e.data[0];
    self.parallax = e.data[1];
    };
});

Robert Flack a écrit un polyfill pour le Worklet du compositeur afin que vous puissiez l'essayer, évidemment avec un impact beaucoup plus élevé sur les performances.

Worklet de mise en page (spec)

La première ébauche de spécifications réelles a été proposée. L'implémentation peut prendre un certain temps.

Là encore, la spécification correspondante est pratiquement vide, mais le concept est intéressant: écrivez votre propre mise en page. Le Worklet de mise en page est censé vous permettre d'utiliser display: layout('myLayout') et d'exécuter votre code JavaScript pour organiser les enfants d'un nœud dans la zone du nœud.

Bien entendu, l'exécution d'une implémentation JavaScript complète de la mise en page flex-box du CSS est plus lente que l'exécution d'une implémentation native équivalente, mais il est facile d'imaginer un scénario dans lequel le moindre virage peut générer un gain de performances. Imaginez un site Web ne contenant rien d'autre que des tuiles, comme Windows 10 ou une mise en page de type maçonnerie. Les positionnements absolus et fixes ne sont pas utilisés, de même que z-index. Les éléments ne se chevauchent pas et ne présentent jamais de bordures ou de dépassements. Le fait de pouvoir ignorer toutes ces vérifications lors de la remise en page peut générer un gain de performances.

registerLayout('random-layout', class {
    static get inputProperties() {
        return [];
    }
    static get childrenInputProperties() {
        return [];
    }
    layout(children, constraintSpace, styleMap) {
        const width = constraintSpace.width;
        const height = constraintSpace.height;
        for (let child of children) {
            const x = Math.random()*width;
            const y = Math.random()*height;
            const constraintSubSpace = new ConstraintSpace();
            constraintSubSpace.width = width-x;
            constraintSubSpace.height = height-y;
            const childFragment = child.doLayout(constraintSubSpace);
            childFragment.x = x;
            childFragment.y = y;
        }

        return {
            minContent: 0,
            maxContent: 0,
            width: width,
            height: height,
            fragments: [],
            unPositionedChildren: [],
            breakToken: null
        };
    }
});

Code CSSOM saisi (spec)

Le code CSSOM typé (CSS Object Model ou Cascading Style Sheets Object Model) permet de résoudre un problème que nous avons probablement rencontré tous et que nous venons d'apprendre à résoudre. Prenons l'exemple d'une ligne de code JavaScript:

    $('#someDiv').style.height = getRandomInt() + 'px';

Nous effectuons des opérations mathématiques, en convertissant un nombre en chaîne afin d'ajouter une unité, simplement pour que le navigateur analyse cette chaîne et la reconvertisse en nombre pour le moteur CSS. Ce comportement est encore plus affaibli lorsque vous manipulez des transformations avec JavaScript. Ce n'est plus le cas ! Le CSS va bientôt commencer à saisir du texte.

Ce brouillon est l'un des plus matures, et un polyfill est déjà en cours de traitement. (Clause de non-responsabilité: l'utilisation du polyfill ajoute évidemment encore plus de frais de calcul. L'objectif est de montrer à quel point l'API est pratique.)

Au lieu de chaînes, vous travaillerez sur le StylePropertyMap d'un élément, où chaque attribut CSS possède sa propre clé et le type de valeur correspondant. Les attributs tels que width ont LengthValue comme type de valeur. Un LengthValue est un dictionnaire de toutes les unités CSS telles que em, rem, px, percent, etc. Définir height: calc(5px + 5%) génère une LengthValue{px: 5, percent: 5}. Certaines propriétés telles que box-sizing n'acceptent que certains mots clés et possèdent donc un type de valeur KeywordValue. La validité de ces attributs peut ensuite être vérifiée au moment de l'exécution.

<div style="width: 200px;" id="div1"></div>
<div style="width: 300px;" id="div2"></div>
<div id="div3"></div>
<div style="margin-left: calc(5em + 50%);" id="div4"></div>
var w1 = $('#div1').styleMap.get('width');
var w2 = $('#div2').styleMap.get('width');
$('#div3').styleMap.set('background-size',
    [new SimpleLength(200, 'px'), w1.add(w2)])
$('#div4')).styleMap.get('margin-left')
    // => {em: 5, percent: 50}

Propriétés et valeurs

(spec)

Connaissez-vous les propriétés personnalisées CSS (ou leur alias non officiel "Variables CSS") ? C'est eux, mais avec des types ! Jusqu'à présent, les variables ne pouvaient contenir que des valeurs de chaîne et utilisaient une approche de recherche et de remplacement simple. Ce brouillon vous permet non seulement de spécifier un type pour vos variables, mais aussi de définir une valeur par défaut et d'influencer le comportement d'héritage à l'aide d'une API JavaScript. Techniquement, cela permettrait également aux propriétés personnalisées de s'animer avec des animations et des transitions CSS standards, ce qui est également pris en compte.

["--scale-x", "--scale-y"].forEach(function(name) {
document.registerProperty({
    name: name,
    syntax: "<number>",
    inherits: false,
    initialValue: "1"
    });
});

Métriques de police

Les métriques de police correspondent exactement à ce qu'elles semblent être. Qu'est-ce que le cadre de délimitation (ou les cadres de délimitation) lorsque j'affiche la chaîne X avec une police Y de taille Z ? Que se passe-t-il si j'utilise des annotations Ruby ? Cette idée a été très demandée, et Houdini devrait enfin réaliser ses vœux.

Et ce n'est pas tout !

La liste des brouillons de Houdini contient encore plus de spécifications, mais l'avenir est plutôt incertain et ce ne sont pas beaucoup plus que des espaces réservés aux idées. Les exemples incluent les comportements de dépassement personnalisés, l'API d'extension de syntaxe CSS, l'extension du comportement de défilement natif et des éléments similaires ambitieux, qui permettent tous des éléments qui n'étaient pas possibles sur la plate-forme Web auparavant.

Démonstrations

J'ai mis le code de la démo en Open Source (démonstration en direct avec polyfill).