Heb je ooit nagedacht over de hoeveelheid werk die CSS doet? Je verandert één enkel attribuut en plotseling verschijnt je hele website in een andere lay-out. Het is een soort magie . Tot nu toe hebben wij – de community van webontwikkelaars – de magie alleen maar kunnen aanschouwen en observeren. Wat als we onze eigen magie willen bedenken? Wat als we de magiër willen worden ?
Maak kennis met Houdini!
De Houdini-taskforce bestaat uit engineers van Mozilla, Apple, Opera, Microsoft, HP, Intel en Google die samenwerken om bepaalde onderdelen van de CSS-engine beschikbaar te maken voor webontwikkelaars. De taskforce werkt aan een verzameling concepten met als doel deze door het W3C te laten accepteren als daadwerkelijke webstandaarden. Ze stelden zichzelf een aantal belangrijke doelen en zetten deze om in specificatieontwerpen, die vervolgens leidden tot een reeks ondersteunende, lagere specificatieontwerpen.
De verzameling van deze concepten is wat men meestal bedoelt als men het over "Houdini" heeft. Op het moment van schrijven is de lijst met concepten onvolledig en sommige concepten zijn slechts tijdelijke aanduidingen.
De specificaties
Werkjes ( spec )
Worklets op zichzelf zijn niet echt nuttig. Ze zijn een concept dat is geïntroduceerd om veel van de latere versies mogelijk te maken. Als u bij het lezen van "worklet" aan webworkers dacht, dan hebt u het niet mis. Ze vertonen veel conceptuele overlap. Dus waarom iets nieuws als we al workers 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 elk frame moeten worden uitgevoerd. Sommige moeten dat per definitie wel. Citaat uit de Web Worker-specificatie :
Dat betekent dat webworkers niet geschikt zijn voor de taken die Houdini van plan is uit te voeren. Daarom werden worklets uitgevonden. Worklets maken gebruik van ES2015-klassen om een verzameling methoden te definiëren, waarvan de handtekeningen vooraf zijn gedefinieerd door het type worklet. Ze zijn licht en hebben een korte levensduur.
CSS Paint API ( specificatie )
Paint API is standaard ingeschakeld in Chrome 65. Lees de gedetailleerde introductie .
Compositor-werkje
De hier beschreven API is verouderd. De Compositor-worklet is opnieuw ontworpen en wordt nu voorgesteld als "Animatie-worklet". Lees meer over de huidige versie van de API .
Hoewel de compositor worklet-specificatie naar de WICG is verplaatst en er iteraties op zullen plaatsvinden, is het de specificatie die me het meest aanspreekt. Sommige bewerkingen worden door de CSS-engine uitbesteed aan de grafische kaart van je computer, hoewel dit afhankelijk is van zowel je grafische kaart als je 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 tekenen zichzelf erop (mogelijk in de toekomst met behulp van een paint worklet). Als laatste stap worden al deze individuele, nu getekende, lagen gestapeld en op elkaar geplaatst, rekening houdend met z-indices, 3D-transformaties en dergelijke, om het uiteindelijke beeld op uw scherm te verkrijgen. Dit proces heet compositing en wordt uitgevoerd door de compositor.
Het voordeel van het compositingproces is dat je niet alle elementen zelf hoeft te herschilderen wanneer de pagina een klein beetje scrollt. In plaats daarvan kun je de lagen van het vorige frame hergebruiken en de compositor gewoon opnieuw uitvoeren met de bijgewerkte scrollpositie. Dit zorgt voor snelheid. Dit helpt ons om 60 fps te bereiken.

Zoals de naam al doet vermoeden, kunt u met de compositor-worklet verbinding maken met de compositor en zo invloed uitoefenen op de manier waarop de laag van een element, die al is geverfd, wordt gepositioneerd en over de andere lagen wordt gelegd.
Om wat specifieker te zijn: je kunt de browser vertellen dat je wilt aansluiten op het compositingproces voor een specifiek DOM-knooppunt en toegang kunt vragen tot bepaalde attributen, zoals scrollpositie, transform
of opacity
. Dit forceert dit element naar een eigen laag en bij elk frame wordt je code aangeroepen. Je kunt je laag verplaatsen door de lagen te transformeren en de bijbehorende attributen (zoals opacity
) te wijzigen, waardoor je met maar liefst 60 fps fantastische dingen kunt doen.
Hier is een volledige implementatie voor parallax-scrolling met behulp van de compositor-worklet.
// 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 de compositor-worklet, dus u kunt het uitproberen – uiteraard met een veel hogere prestatie-impact.
Lay-out worklet ( spec )
Het eerste echte concept is ingediend. De implementatie laat nog wel even op zich wachten.
Ook hier is de specificatie praktisch leeg, maar het concept is intrigerend: schrijf je eigen layout! De layout worklet zou je in staat moeten stellen om display: layout('myLayout')
uit te voeren en je JavaScript te gebruiken om de kinderen van een node in de box van de node te plaatsen.
Natuurlijk is het draaien van een volledige JavaScript-implementatie van CSS' flex-box
lay-out langzamer dan het draaien van een equivalente native implementatie, maar het is gemakkelijk voor te stellen dat bezuinigingen een prestatiewinst kunnen opleveren. Stel je een website voor die alleen uit tegels bestaat, zoals Windows 10 of een masonry-stijl lay-out. Absolute en vaste positionering wordt niet gebruikt, evenmin als z-index
, en elementen overlappen elkaar nooit of hebben geen enkele vorm van rand of overloop. Het kunnen overslaan van al deze controles bij het opnieuw lay-outen 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 waar we waarschijnlijk allemaal wel eens mee te maken hebben gehad en waar we gewoon mee leren leven. Laat me dit illustreren met een regel JavaScript:
$('#someDiv').style.height = getRandomInt() + 'px';
We zijn aan het rekenen, we zetten een getal om in een string 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 erger wanneer je transformaties met JavaScript manipuleert . Niet meer! CSS krijgt zometeen wat typwerk.
Deze versie is een van de meer volwassen versies en er wordt al aan een polyfill gewerkt. (Disclaimer: het gebruik van de polyfill brengt uiteraard nog meer rekenkracht met zich mee. Het doel 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. Het instellen van height: calc(5px + 5%)
levert een LengthValue{px: 5, percent: 5}
. Sommige eigenschappen, zoals box-sizing
accepteren alleen bepaalde trefwoorden en hebben daarom een KeywordValue
waardetype. 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
( spec )
Ken je CSS Custom Properties (of hun onofficiële alias "CSS-variabelen")? Dit zijn ze, maar dan met typen! Tot nu toe konden variabelen alleen tekenreekswaarden hebben en werd een eenvoudige zoek-en-vervang-aanpak gebruikt. Met dit concept kun je niet alleen een type voor je variabelen specificeren, maar ook een standaardwaarde definiëren en het overervingsgedrag beïnvloeden met behulp van een JavaScript API. Technisch gezien zou dit ook de mogelijkheid bieden om aangepaste eigenschappen te animeren met standaard CSS-overgangen en -animaties, iets wat ook wordt overwogen.
["--scale-x", "--scale-y"].forEach(function(name) {
document.registerProperty({
name: name,
syntax: "<number>",
inherits: false,
initialValue: "1"
});
});
Lettertype-metriek
Lettertypemetriek is precies wat het lijkt. Wat is de bounding box (of de bounding boxes) wanneer ik string X render met lettertype Y op grootte Z? Wat als ik Ruby-annotaties gebruik? Hier is veel om gevraagd en Houdini zou deze wensen eindelijk moeten kunnen vervullen.
Maar wacht, er is meer!
Er staan nog meer specificaties in Houdini's lijst met ontwerpen, maar de toekomst daarvan is nogal onzeker en ze zijn niet veel meer dan tijdelijke ideeën. Voorbeelden zijn onder andere aangepast overloopgedrag, een API voor CSS-syntaxisuitbreiding, uitbreiding van native scrollgedrag en vergelijkbare 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 behulp van polyfill).