Nieuwe mogelijkheden in Chrome 65
CSS Paint API (ook bekend als "CSS Custom Paint" of "Houdini's paint worklet") is standaard ingeschakeld vanaf Chrome 65. Wat is het? Wat kun je ermee doen? En hoe werkt het? Lees dan verder...
Met de CSS Paint API kun je programmatisch een afbeelding genereren wanneer een CSS-eigenschap een afbeelding verwacht. Eigenschappen zoals background-image
of border-image
worden meestal gebruikt met url()
om een afbeeldingsbestand te laden of met ingebouwde CSS-functies zoals linear-gradient()
. In plaats daarvan kun je nu paint(myPainter)
gebruiken om te verwijzen naar een paint-worklet .
Een schilderwerkje schrijven
Om een paint worklet met de naam myPainter
te definiëren, moeten we een CSS paint worklet-bestand laden met CSS.paintWorklet.addModule('my-paint-worklet.js')
. In dat bestand kunnen we de functie registerPaint
gebruiken om een paint worklet-klasse te registreren:
class MyPainter {
paint(ctx, geometry, properties) {
// ...
}
}
registerPaint('myPainter', MyPainter);
Binnen de paint()
callback kunnen we ctx
op dezelfde manier gebruiken als een CanvasRenderingContext2D
, zoals we die kennen van <canvas>
. Als je weet hoe je in een <canvas>
tekent, kun je ook in een paint worklet tekenen! geometry
vertelt ons de breedte en hoogte van het canvas dat we tot onze beschikking hebben. properties
ik later in dit artikel zal uitleggen.
Laten we als inleidend voorbeeld een schaakbord-worklet schrijven en deze gebruiken als achtergrondafbeelding voor een <textarea>
. (Ik gebruik een textarea omdat de grootte ervan standaard aanpasbaar is.):
<!-- index.html -->
<!doctype html>
<style>
textarea {
background-image: paint(checkerboard);
}
</style>
<textarea></textarea>
<script>
CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
paint(ctx, geom, properties) {
// Use `ctx` as if it was a normal canvas
const colors = ['red', 'green', 'blue'];
const size = 32;
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
const color = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.fillStyle = color;
ctx.rect(x * size, y * size, size, size);
ctx.fill();
}
}
}
}
// Register our class under a specific name
registerPaint('checkerboard', CheckerboardPainter);
Als je <canvas>
al eens eerder hebt gebruikt, zal deze code je bekend voorkomen. Bekijk hier de live demo .

Het verschil met een gewone achtergrondafbeelding is dat het patroon op verzoek opnieuw wordt getekend wanneer de gebruiker het tekstvak aanpast. Dit betekent dat de achtergrondafbeelding altijd precies zo groot is als nodig is, inclusief de compensatie voor weergaven met hoge dichtheid.
Dat is best gaaf, maar het is ook vrij statisch. Zouden we elke keer een nieuw werkje willen schrijven als we hetzelfde patroon wilden, maar met vierkantjes van verschillende grootte? Het antwoord is nee!
Uw worklet parametriseren
Gelukkig heeft de paint worklet toegang tot andere CSS-eigenschappen, en daar komen de extra properties
om de hoek kijken. Door de klasse een statisch inputProperties
attribuut te geven, kunt u zich abonneren op wijzigingen in elke CSS-eigenschap, inclusief aangepaste eigenschappen. De waarden worden aan u doorgegeven via de parameter properties
.
<!-- index.html -->
<!doctype html>
<style>
textarea {
/* The paint worklet subscribes to changes of these custom properties. */
--checkerboard-spacing: 10;
--checkerboard-size: 32;
background-image: paint(checkerboard);
}
</style>
<textarea></textarea>
<script>
CSS.paintWorklet.addModule('checkerboard.js');
</script>
// checkerboard.js
class CheckerboardPainter {
// inputProperties returns a list of CSS properties that this paint function gets access to
static get inputProperties() { return ['--checkerboard-spacing', '--checkerboard-size']; }
paint(ctx, geom, properties) {
// Paint worklet uses CSS Typed OM to model the input values.
// As of now, they are mostly wrappers around strings,
// but will be augmented to hold more accessible data over time.
const size = parseInt(properties.get('--checkerboard-size').toString());
const spacing = parseInt(properties.get('--checkerboard-spacing').toString());
const colors = ['red', 'green', 'blue'];
for(let y = 0; y < geom.height/size; y++) {
for(let x = 0; x < geom.width/size; x++) {
ctx.fillStyle = colors[(x + y) % colors.length];
ctx.beginPath();
ctx.rect(x*(size + spacing), y*(size + spacing), size, size);
ctx.fill();
}
}
}
}
registerPaint('checkerboard', CheckerboardPainter);
Nu kunnen we dezelfde code gebruiken voor alle verschillende soorten checkerboards. Maar nog beter: we kunnen nu in DevTools met de waarden spelen tot we de juiste look hebben gevonden.
Browsers die geen Paint Worklet ondersteunen
Op het moment van schrijven heeft alleen Chrome een Paint Worklet geïmplementeerd. Hoewel er positieve signalen zijn van alle andere browserleveranciers, is er weinig vooruitgang geboekt. Om op de hoogte te blijven, controleer regelmatig Is Houdini Ready Yet?. Zorg er in de tussentijd voor dat je progressieve verbetering gebruikt om je code draaiende te houden, zelfs als er geen ondersteuning is voor een Paint Worklet. Om ervoor te zorgen dat alles werkt zoals verwacht, moet je je code op twee punten aanpassen: in de CSS en in de JS.
Ondersteuning voor Paint Worklets in JS kan worden gedetecteerd door het CSS
object te controleren: js if ('paintWorklet' in CSS) { CSS.paintWorklet.addModule('mystuff.js'); }
Voor de CSS-kant heb je twee opties. Je kunt @supports
gebruiken:
@supports (background: paint(id)) {
/* ... */
}
Een compactere truc is om gebruik te maken van het feit dat CSS een volledige eigenschapsdeclaratie ongeldig maakt en vervolgens negeert als er een onbekende functie in zit. Als je een eigenschap twee keer opgeeft – eerst zonder de paint worklet en vervolgens met de paint worklet – krijg je progressieve verbetering:
textarea {
background-image: linear-gradient(0, red, blue);
background-image: paint(myGradient, red, blue);
}
In browsers met ondersteuning voor Paint Worklets overschrijft de tweede declaratie van background-image
de eerste. In browsers zonder ondersteuning voor Paint Worklets is de tweede declaratie ongeldig en wordt deze verwijderd, waardoor de eerste declaratie van kracht blijft.
CSS Paint Polyfill
Voor veel toepassingen is het ook mogelijk om CSS Paint Polyfill te gebruiken, waarmee moderne browsers ondersteuning krijgen voor CSS Custom Paint en Paint Worklets.
Gebruiksscenario's
Er zijn veel toepassingen voor paint worklets, sommige meer voor de hand liggend dan andere. Een van de meest voor de hand liggende is het gebruik van een paint worklet om de grootte van je DOM te verkleinen. Elementen worden vaak puur toegevoegd om verfraaiingen te creëren met behulp van CSS. In Material Design Lite bevat de knop met het rimpeleffect bijvoorbeeld twee extra <span>
-elementen om de rimpel zelf te implementeren. Als je veel knoppen hebt, kan dit oplopen tot een behoorlijk aantal DOM-elementen, wat kan leiden tot slechtere prestaties op mobiele apparaten. Als je het rimpeleffect in plaats daarvan implementeert met een paint worklet , eindig je met 0 extra elementen en slechts één paint worklet. Bovendien heb je iets dat veel gemakkelijker aan te passen en te parametriseren is.
Een ander voordeel van het gebruik van een Paint Worklet is dat een oplossing met een Paint Worklet in de meeste gevallen klein is qua bytes. Natuurlijk is er een keerzijde: je Paint-code wordt uitgevoerd wanneer de grootte van het canvas of een van de parameters verandert. Dus als je code complex is en lang duurt, kan dit problemen veroorzaken. Chrome werkt eraan om Paint Worklets van de hoofdthread te verwijderen, zodat zelfs langlopende Paint Worklets de responsiviteit van de hoofdthread niet beïnvloeden.
Het meest opwindende vooruitzicht vind ik dat de Paint Worklet een efficiënte polyfilling van CSS-functies mogelijk maakt die een browser nog niet heeft. Een voorbeeld hiervan is het polyfillen van conische gradiënten totdat ze standaard in Chrome beschikbaar zijn. Nog een voorbeeld: tijdens een CSS-meeting werd besloten dat je nu meerdere randkleuren kunt gebruiken. Terwijl deze meeting nog gaande was, schreef mijn collega Ian Kilpatrick een polyfill voor dit nieuwe CSS-gedrag met behulp van de Paint Worklet.
Buiten de gebaande paden denken
De meeste mensen beginnen na te denken over achtergrondafbeeldingen en randafbeeldingen wanneer ze meer te weten komen over paint worklets. Een minder intuïtief gebruiksvoorbeeld van paint worklets is mask-image
om DOM-elementen willekeurige vormen te geven. Bijvoorbeeld een diamant :

mask-image
gebruikt een afbeelding ter grootte van het element. Gebieden waar de maskerafbeelding transparant is, zijn ook de elementen transparant. Gebieden waar de maskerafbeelding ondoorzichtig is, zijn ook de elementen ondoorzichtig.
Nu in Chrome
Paint Worklet is al een tijdje beschikbaar in Chrome Canary. In Chrome 65 is het standaard ingeschakeld. Probeer de nieuwe mogelijkheden die Paint Worklet biedt en laat ons zien wat je hebt gemaakt! Bekijk voor meer inspiratie de collectie van Vincent De Oliveira .