Houdini - CSS ontraadselen

Heb je ooit nagedacht over de hoeveelheid werk die CSS doet? Je wijzigt één attribuut en plotseling verschijnt je hele website in een andere lay-out. Het is een soort magie . Tot nu toe hebben wij – de gemeenschap van webontwikkelaars – alleen maar getuige kunnen zijn van de magie. Wat als we onze eigen magie willen bedenken? Wat als we de tovenaar willen worden ?

Kom Houdini binnen!

De Houdini-taskforce bestaat uit ingenieurs van Mozilla, Apple, Opera, Microsoft, HP, Intel en Google die samenwerken om bepaalde delen van de CSS-engine bloot te stellen aan webontwikkelaars. De taskforce werkt aan een verzameling concepten met als doel deze door het W3C geaccepteerd te krijgen om daadwerkelijke webstandaarden te worden. Ze stelden zichzelf een aantal doelstellingen op hoog niveau en zetten deze om in specificatieontwerpen, waaruit op hun beurt een reeks ondersteunende specificatieontwerpen op een lager niveau voortkwamen.

De verzameling van deze concepten is wat gewoonlijk wordt bedoeld als iemand over "Houdini" spreekt. Op het moment van schrijven is de lijst met concepten onvolledig en zijn sommige concepten slechts tijdelijke aanduidingen.

De specificaties

Werkjes ( specificatie )

Worklets op zichzelf zijn niet echt nuttig. Het is een concept dat is geïntroduceerd om veel van de latere ontwerpen mogelijk te maken. Als u aan Web Workers dacht toen u "worklet" las, heeft u het niet mis. Ze hebben veel conceptuele overlap. Dus waarom iets nieuws als we al werknemers hebben?

Houdini's doel is om nieuwe API's beschikbaar te stellen waarmee webontwikkelaars hun eigen code kunnen koppelen aan de CSS-engine en de omliggende systemen. Het is waarschijnlijk niet onrealistisch om aan te nemen dat sommige van deze codefragmenten elke keer moeten worden uitgevoerd. enkel. kader . Sommigen van hen moeten dat per definitie doen. Citaat van de Web Worker-specificatie :

Dat betekent dat webwerkers niet levensvatbaar zijn voor de dingen die Houdini van plan is te doen. Daarom werden worklets uitgevonden. Werklets maken gebruik van ES2015-klassen om een ​​verzameling methoden te definiëren, waarvan de handtekeningen vooraf zijn gedefinieerd door het type werklet. Ze zijn licht van gewicht en van korte duur.

CSS Paint-API ( specificatie )

Paint API is standaard ingeschakeld in Chrome 65. Lees de gedetailleerde introductie .

Compositor-werklet

De hier beschreven API is verouderd. Compositor-werklet is opnieuw ontworpen en wordt nu voorgesteld als "Animatiewerklet". Lees meer over de huidige versie van de API .

Hoewel de specificatie van de compositorwerklet naar de WICG is verplaatst en hierop zal worden herhaald, zijn dit de specificaties die mij het meest boeien. Sommige bewerkingen worden door de CSS-engine uitbesteed aan de grafische kaart van uw computer, hoewel dat afhankelijk is van zowel uw grafische kaart als uw apparaat in het algemeen.

Een browser neemt meestal de DOM-boom en besluit op basis van specifieke criteria om sommige takken en subbomen een eigen laag te geven. Deze subbomen schilderen zichzelf erop (misschien met behulp van een verfwerkje in de toekomst). Als laatste stap worden al deze individuele, nu geschilderde lagen op elkaar gestapeld en op elkaar gepositioneerd, met respect voor z-indices, 3D-transformaties en dergelijke, om het uiteindelijke beeld op te leveren dat zichtbaar is op uw scherm. Dit proces wordt compositing genoemd en wordt uitgevoerd door de compositor.

Het voordeel van het compositieproces is dat u niet alle elementen opnieuw hoeft te schilderen wanneer de pagina een klein beetje scrollt. In plaats daarvan kunt u de lagen uit het vorige frame opnieuw gebruiken en de compositor opnieuw uitvoeren met de bijgewerkte schuifpositie. Dit maakt de zaken snel. Dit helpt ons om 60 fps te bereiken.

Compositor-werklet.

Zoals de naam al doet vermoeden, kunt u met het compositor-werklet inhaken in de compositor en invloed uitoefenen op de manier waarop de laag van een element, die al is geverfd, wordt gepositioneerd en gelaagd bovenop de andere lagen.

Om iets specifieker te worden, kunt u de browser vertellen dat u wilt aansluiten bij het compositieproces voor een bepaald DOM-knooppunt en toegang kunt vragen tot bepaalde attributen zoals scrollpositie, transform of opacity . Dit dwingt dit element naar zijn eigen laag en op elk frame wordt jouw code aangeroepen. Je kunt je laag verplaatsen door de transformatie van de lagen te manipuleren en de attributen ervan te wijzigen (zoals opacity ), zodat je fraaie dingen kunt doen met maar liefst 60 fps.

Hier is een volledige implementatie voor parallax-scrollen, met behulp van de compositor-werklet.

// 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 heeft een polyfill geschreven voor het compositor-werklet, zodat u het kunt proberen – uiteraard met een veel grotere impact op de prestaties.

Lay-out werklet ( spec )

Het eerste echte specificatieontwerp is voorgesteld. De implementatie laat nog een tijdje op zich wachten.

Nogmaals, de specificatie hiervoor is vrijwel leeg, maar het concept is intrigerend: schrijf je eigen lay-out! Het is de bedoeling dat het lay-outwerklet u in staat stelt om display: layout('myLayout') te voeren en uw JavaScript uit te voeren om de kinderen van een knooppunt in het vak van het knooppunt te rangschikken.

Natuurlijk is het uitvoeren van een volledige JavaScript-implementatie van flex-box indeling van CSS langzamer dan het uitvoeren van een gelijkwaardige native implementatie, maar je kunt je gemakkelijk een scenario voorstellen waarin bezuinigingen een prestatiewinst kunnen opleveren. Stel je een website voor die uit niets anders bestaat dan tegels, zoals Windows 10 of een lay-out in metselwerkstijl. Er wordt geen gebruik gemaakt van absolute en vaste positionering, en z-index ook niet, en elementen overlappen elkaar nooit of hebben enige vorm van rand of overloop. Het kunnen overslaan van al deze controles bij het opnieuw indelen zou een prestatiewinst kunnen opleveren.

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
        };
    }
});

Getypte CSSOM ( spec )

Typed CSSOM (CSS Object Model of Cascading Style Sheets Object Model) lost een probleem op dat we waarschijnlijk allemaal zijn tegengekomen en waar we net mee hebben leren leven. Laat me dit illustreren met een regel JavaScript:

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

We zijn aan het rekenen, waarbij we een getal naar een string converteren om er een eenheid aan toe te voegen, zodat de browser die string kan parseren en terug kan converteren naar een getal voor de CSS-engine. Dit wordt nog lelijker als je transformaties manipuleert met JavaScript . Niet meer! CSS staat op het punt wat typwerk te krijgen.

Dit ontwerp is een van de meer volwassen versies en er wordt al aan een polyfill gewerkt. (Disclaimer: het gebruik van de polyfill zal uiteraard nog meer rekenkundige overhead toevoegen. Het punt is om te laten zien hoe handig de API is.)

In plaats van strings werk je aan StylePropertyMap van een element, waarbij elk CSS-attribuut zijn eigen sleutel en bijbehorend waardetype heeft. Attributen zoals width hebben LengthValue als waardetype. Een LengthValue is een woordenboek van alle CSS-eenheden zoals em , rem , px , percent , enzovoort. height: calc(5px + 5%) zou een LengthValue{px: 5, percent: 5} opleveren. Sommige eigenschappen, zoals box-sizing accepteren alleen bepaalde trefwoorden en hebben daarom het waardetype KeywordValue . De geldigheid van deze attributen kan vervolgens tijdens runtime worden gecontroleerd.

<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}

Eigenschappen en waarden

( specificatie )

Kent u CSS Custom Properties (of hun onofficiële alias "CSS Variables")? Dit zijn ze, maar dan met types! Tot nu toe konden variabelen alleen stringwaarden bevatten en werd er gebruik gemaakt van een eenvoudige zoek-en-vervang-aanpak. Met dit concept kunt u niet alleen een type voor uw variabelen opgeven, maar ook een standaardwaarde definiëren en het overervingsgedrag beïnvloeden met behulp van een JavaScript-API. Technisch gezien zou dit het ook mogelijk maken om aangepaste eigenschappen te animeren met standaard CSS-overgangen en animaties, wat ook wordt overwogen.

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

Lettertypestatistieken

Lettertypestatistieken zijn precies hoe het klinkt. Wat is het selectiekader (of de selectiekaders) wanneer ik tekenreeks X render met lettertype Y in grootte Z? Wat moet ik doen als ik robijnrode annotaties ga gebruiken? Hier is veel om gevraagd en Houdini zou deze wensen eindelijk in vervulling moeten laten gaan.

Maar wacht, er is meer!

Er staan ​​nog meer specificaties in Houdini's lijst met concepten, maar de toekomst daarvan is nogal onzeker en ze zijn niet veel meer dan tijdelijke aanduidingen voor ideeën. Voorbeelden zijn onder meer aangepast overflow-gedrag, CSS-syntaxis-extensie-API, uitbreiding van native scroll-gedrag en soortgelijke ambitieuze dingen die allemaal dingen op het webplatform mogelijk maken die voorheen niet mogelijk waren.

Demo's

Ik heb de code voor de demo open source gemaakt ( live demo met polyfill).

,

Heb je ooit nagedacht over de hoeveelheid werk die CSS doet? Je wijzigt één attribuut en plotseling verschijnt je hele website in een andere lay-out. Het is een soort magie . Tot nu toe hebben wij – de gemeenschap van webontwikkelaars – alleen maar getuige kunnen zijn van de magie. Wat als we onze eigen magie willen bedenken? Wat als we de tovenaar willen worden ?

Kom Houdini binnen!

De Houdini-taskforce bestaat uit ingenieurs van Mozilla, Apple, Opera, Microsoft, HP, Intel en Google die samenwerken om bepaalde delen van de CSS-engine bloot te stellen aan webontwikkelaars. De taskforce werkt aan een verzameling concepten met als doel deze door het W3C geaccepteerd te krijgen om daadwerkelijke webstandaarden te worden. Ze stelden zichzelf een aantal doelstellingen op hoog niveau en zetten deze om in specificatieontwerpen, waaruit op hun beurt een reeks ondersteunende specificatieontwerpen op een lager niveau voortkwamen.

De verzameling van deze concepten is wat gewoonlijk wordt bedoeld als iemand over "Houdini" spreekt. Op het moment van schrijven is de lijst met concepten onvolledig en zijn sommige concepten slechts tijdelijke aanduidingen.

De specificaties

Werkjes ( specificatie )

Worklets op zichzelf zijn niet echt nuttig. Het is een concept dat is geïntroduceerd om veel van de latere ontwerpen mogelijk te maken. Als u aan Web Workers dacht toen u "worklet" las, heeft u het niet mis. Ze hebben veel conceptuele overlap. Dus waarom iets nieuws als we al werknemers hebben?

Houdini's doel is om nieuwe API's beschikbaar te stellen waarmee webontwikkelaars hun eigen code kunnen koppelen aan de CSS-engine en de omliggende systemen. Het is waarschijnlijk niet onrealistisch om aan te nemen dat sommige van deze codefragmenten elke keer moeten worden uitgevoerd. enkel. kader . Sommigen van hen moeten dat per definitie doen. Citaat van de Web Worker-specificatie :

Dat betekent dat webwerkers niet levensvatbaar zijn voor de dingen die Houdini van plan is te doen. Daarom werden worklets uitgevonden. Werklets maken gebruik van ES2015-klassen om een ​​verzameling methoden te definiëren, waarvan de handtekeningen vooraf zijn gedefinieerd door het type werklet. Ze zijn licht van gewicht en van korte duur.

CSS Paint-API ( specificatie )

Paint API is standaard ingeschakeld in Chrome 65. Lees de gedetailleerde introductie .

Compositor-werklet

De hier beschreven API is verouderd. Compositor-werklet is opnieuw ontworpen en wordt nu voorgesteld als "Animatiewerklet". Lees meer over de huidige versie van de API .

Hoewel de specificatie van de compositorwerklet naar de WICG is verplaatst en hierop zal worden herhaald, zijn dit de specificaties die mij het meest boeien. Sommige bewerkingen worden door de CSS-engine uitbesteed aan de grafische kaart van uw computer, hoewel dat afhankelijk is van zowel uw grafische kaart als uw apparaat in het algemeen.

Een browser neemt meestal de DOM-boom en besluit op basis van specifieke criteria om sommige takken en subbomen een eigen laag te geven. Deze subbomen schilderen zichzelf erop (misschien met behulp van een verfwerkje in de toekomst). Als laatste stap worden al deze individuele, nu geschilderde lagen op elkaar gestapeld en op elkaar gepositioneerd, met respect voor z-indices, 3D-transformaties en dergelijke, om het uiteindelijke beeld op te leveren dat zichtbaar is op uw scherm. Dit proces wordt compositing genoemd en wordt uitgevoerd door de compositor.

Het voordeel van het compositieproces is dat u niet alle elementen opnieuw hoeft te schilderen wanneer de pagina een klein beetje scrollt. In plaats daarvan kunt u de lagen uit het vorige frame opnieuw gebruiken en de compositor opnieuw uitvoeren met de bijgewerkte schuifpositie. Dit maakt de zaken snel. Dit helpt ons om 60 fps te bereiken.

Compositor-werklet.

Zoals de naam al doet vermoeden, kunt u met het compositor-werklet inhaken in de compositor en invloed uitoefenen op de manier waarop de laag van een element, die al is geverfd, wordt gepositioneerd en gelaagd bovenop de andere lagen.

Om iets specifieker te worden, kunt u de browser vertellen dat u wilt aansluiten bij het compositieproces voor een bepaald DOM-knooppunt en toegang kunt vragen tot bepaalde attributen zoals scrollpositie, transform of opacity . Dit dwingt dit element naar zijn eigen laag en op elk frame wordt jouw code aangeroepen. Je kunt je laag verplaatsen door de transformatie van de lagen te manipuleren en de attributen ervan te wijzigen (zoals opacity ), zodat je fraaie dingen kunt doen met maar liefst 60 fps.

Hier is een volledige implementatie voor parallax-scrollen, met behulp van de compositor-werklet.

// 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 heeft een polyfill geschreven voor het compositor-werklet, zodat u het kunt proberen – uiteraard met een veel grotere impact op de prestaties.

Lay-out werklet ( spec )

Het eerste echte specificatieontwerp is voorgesteld. De implementatie laat nog een tijdje op zich wachten.

Nogmaals, de specificatie hiervoor is vrijwel leeg, maar het concept is intrigerend: schrijf je eigen lay-out! Het is de bedoeling dat het lay-outwerklet u in staat stelt om display: layout('myLayout') te voeren en uw JavaScript uit te voeren om de kinderen van een knooppunt in het vak van het knooppunt te rangschikken.

Natuurlijk is het uitvoeren van een volledige JavaScript-implementatie van flex-box indeling van CSS langzamer dan het uitvoeren van een gelijkwaardige native implementatie, maar je kunt je gemakkelijk een scenario voorstellen waarin bezuinigingen een prestatiewinst kunnen opleveren. Stel je een website voor die uit niets anders bestaat dan tegels, zoals Windows 10 of een lay-out in metselwerkstijl. Er wordt geen gebruik gemaakt van absolute en vaste positionering, en z-index ook niet, en elementen overlappen elkaar nooit of hebben enige vorm van rand of overloop. Het kunnen overslaan van al deze controles bij het opnieuw indelen zou een prestatiewinst kunnen opleveren.

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
        };
    }
});

Getypte CSSOM ( spec )

Typed CSSOM (CSS Object Model of Cascading Style Sheets Object Model) lost een probleem op dat we waarschijnlijk allemaal zijn tegengekomen en waar we net mee hebben leren leven. Laat me dit illustreren met een regel JavaScript:

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

We zijn aan het rekenen, waarbij we een getal naar een string converteren om er een eenheid aan toe te voegen, zodat de browser die string kan parseren en terug kan converteren naar een getal voor de CSS-engine. Dit wordt nog lelijker als je transformaties manipuleert met JavaScript . Niet meer! CSS staat op het punt wat typwerk te krijgen.

Dit ontwerp is een van de meer volwassen versies en er wordt al aan een polyfill gewerkt. (Disclaimer: het gebruik van de polyfill zal uiteraard nog meer rekenkundige overhead toevoegen. Het punt is om te laten zien hoe handig de API is.)

In plaats van strings werk je aan StylePropertyMap van een element, waarbij elk CSS-attribuut zijn eigen sleutel en bijbehorend waardetype heeft. Attributen zoals width hebben LengthValue als waardetype. Een LengthValue is een woordenboek van alle CSS-eenheden zoals em , rem , px , percent , enzovoort. height: calc(5px + 5%) zou een LengthValue{px: 5, percent: 5} opleveren. Sommige eigenschappen, zoals box-sizing accepteren alleen bepaalde trefwoorden en hebben daarom het waardetype KeywordValue . De geldigheid van deze attributen kan vervolgens tijdens runtime worden gecontroleerd.

<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}

Eigenschappen en waarden

( specificatie )

Kent u CSS Custom Properties (of hun onofficiële alias "CSS Variables")? Dit zijn ze, maar dan met types! Tot nu toe konden variabelen alleen stringwaarden bevatten en werd er gebruik gemaakt van een eenvoudige zoek-en-vervang-aanpak. Met dit concept kunt u niet alleen een type voor uw variabelen opgeven, maar ook een standaardwaarde definiëren en het overervingsgedrag beïnvloeden met behulp van een JavaScript-API. Technisch gezien zou dit het ook mogelijk maken om aangepaste eigenschappen te animeren met standaard CSS-overgangen en animaties, wat ook wordt overwogen.

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

Lettertypestatistieken

Lettertypestatistieken zijn precies hoe het klinkt. Wat is het selectiekader (of de selectiekaders) wanneer ik tekenreeks X render met lettertype Y in grootte Z? Wat moet ik doen als ik robijnrode annotaties ga gebruiken? Hier is veel om gevraagd en Houdini zou deze wensen eindelijk in vervulling moeten laten gaan.

Maar wacht, er is meer!

Er staan ​​nog meer specificaties in Houdini's lijst met concepten, maar de toekomst daarvan is nogal onzeker en ze zijn niet veel meer dan tijdelijke aanduidingen voor ideeën. Voorbeelden zijn onder meer aangepast overflow-gedrag, CSS-syntaxis-extensie-API, uitbreiding van native scroll-gedrag en soortgelijke ambitieuze dingen die allemaal dingen op het webplatform mogelijk maken die voorheen niet mogelijk waren.

Demo's

Ik heb de code voor de demo open source gemaakt ( live demo met polyfill).