Webontwikkelaars staan al jaren voor een lastige architectonische keuze bij het bouwen van complexe, zeer interactieve visuele applicaties op het web: vertrouw je op de DOM vanwege de rijke semantische mogelijkheden, of render je direct naar het <canvas> -element voor grafische prestaties op laag niveau?
Met de nieuwe experimentele HTML-in-Canvas API – nu beschikbaar in origin trial – hoeft u niet te kiezen. Deze API stelt u in staat om DOM-content rechtstreeks in een 2D-canvas of een WebGL/WebGPU-textuur te tekenen, terwijl de gebruikersinterface interactief, toegankelijk en gekoppeld blijft aan uw favoriete browserfuncties. Door HTML te combineren met grafische verwerking op laag niveau kunt u ervaringen creëren die voorheen onmogelijk waren.
DOM versus Canvas
Om de kracht van deze nieuwe API te begrijpen, is het nuttig om de relatieve sterke punten van zowel de DOM als het Canvas te bekijken.
Het DOM (Domain Object Model) is de basis van web-UI. Het biedt kant-en-klare oplossingen voor tekstlay-out, waarbij semantisch begrepen content wordt gebruikt om rijke interfaces te creëren. Hierdoor kunnen gebruikers naadloos veelvoorkomende bewerkingen uitvoeren op webpagina's – dingen die we vaak als vanzelfsprekend beschouwen, zoals tekst selecteren om te kopiëren of met de rechtermuisknop op een afbeelding klikken om deze op te slaan. Het DOM integreert ook met essentiële browserfuncties: toegankelijkheidstools, vertalen, zoeken op de pagina, leesmodus, extensies, donkere modus, zoomfunctie en automatisch aanvullen.
Canvas (en WebGL / WebGPU ) biedt daarentegen toegang op laag niveau om een raster van pixels aan te sturen voor zeer geavanceerde 2D- en 3D-graphics. Games en complexe webapplicaties (zoals Google Docs of Figma) vereisen deze performante toegang op laag niveau. Omdat het canvas in essentie een raster van pixels is, vereisten ondersteunende functies zoals responsieve tekst voorheen complexe, op maat gemaakte UI-logica, wat de bundelgrootte drastisch vergrootte. Cruciaal is dat alle krachtige browserfuncties die in de DOM zijn geïntegreerd, volledig onbruikbaar worden wanneer de gebruikersinterface vastzit in een statisch raster van canvaspixels.
De voordelen van het integreren van de DOM in Canvas
De HTML-in-Canvas API is de brug die het beste van twee werelden combineert. Door HTML binnen het <canvas> -element te plaatsen en de transformatie ervan te synchroniseren, zorgt u ervoor dat de inhoud volledig interactief blijft en dat alle browserintegraties automatisch werken.
Dit is wat je krijgt als je de DOM je gebruikersinterface laat afhandelen binnen een <canvas> -element:
- Tekstindeling en -opmaak: Vereenvoudigde tekstindeling en -opmaak, inclusief tekst over meerdere regels of tekst in twee richtingen met toegepaste CSS-stijlen.
- Formulierbesturingselementen: Expressieve en gebruiksvriendelijkere formulierbesturingselementen met uitgebreide aanpassingsmogelijkheden.
- Tekst selecteren, kopiëren/plakken en rechtermuisklik: Gebruikers kunnen tekst in uw 3D-scènes markeren of contextmenu's openen met de rechtermuisknop.
- Tekst selecteren, kopiëren/plakken en rechtermuisklik: Gebruikers kunnen tekst in uw 3D-scènes markeren of contextmenu's openen met de rechtermuisknop.
- Toegankelijkheid: Inhoud die binnen het canvas wordt weergegeven, is toegankelijk voor de toegankelijkheidsstructuur. Toegankelijkheidssystemen kunnen de gebruikersinterface op dezelfde manier analyseren als normale HTML en deze beschikbaar stellen aan systemen zoals schermlezers.
- Zoeken op pagina: Gebruikers kunnen de zoekfunctie op pagina ( Ctrl / Cmd + F ) gebruiken om naar tekst te zoeken, waarna de browser deze direct in uw WebGL-texturen markeert.
- Zoeken op pagina: Gebruikers kunnen de zoekfunctie op pagina ( Ctrl / Cmd + F ) gebruiken om naar tekst te zoeken, waarna de browser deze direct in uw WebGL-texturen markeert.
- Indexeerbaarheid en koppeling met AI-agenten: Webcrawlers en AI-agenten kunnen de tekst in uw 2D- en 3D-scènes naadloos indexeren en lezen.
- Integratie met extensies: Browser-extensies werken naadloos. Een extensie voor tekstvervanging zal bijvoorbeeld automatisch de tekst op uw 3D-modellen bijwerken.
- Integratie met DevTools: Je kunt de inhoud van je canvas inspecteren, inclusief WebGL/WebGPU UI-elementen, rechtstreeks in Chrome DevTools. Pas een CSS-stijl aan in de inspector en zie hoe deze direct wordt bijgewerkt op de 3D-textuur!
Gebruiksvoorbeelden op hoog niveau
Deze API ontsluit een ongelooflijk potentieel op diverse gebieden:
- Grote, op canvas gebaseerde applicaties: Zware webapps zoals Google Docs, Miro of Figma kunnen nu complexe UI-componenten rechtstreeks in hun canvas-gebaseerde werkruimtes weergeven, waardoor de toegankelijkheid verbetert en de bestandsgrootte wordt verkleind.
- 3D-scènes en -games: Marketingwebsites, meeslepende WebXR-ervaringen en webgames kunnen nu volledig interactieve web-UI in 3D-scènes plaatsen, zoals een 3D-boek dat gebruikmaakt van echte DOM-tekst, of een in-game terminal die native ondersteuning biedt voor kopiëren en plakken.
Hoe gebruik je de API?
Het gebruik van de API verloopt in drie fasen: het instellen van je canvas, het renderen op het canvas en het bijwerken van de CSS-transformatie zodat de browser weet waar het element zich fysiek op het scherm bevindt.
Voorwaarden
De HTML-in-Canvas API bevindt zich in een Origin-proefversie in Chrome 148 tot en met 150. Om deze op uw site te testen, gebruikt u Chrome Canary 149 of later met de vlag chrome://flags/#canvas-draw-element ingeschakeld. Om de API voor andere gebruikers beschikbaar te maken, kunt u zich registreren voor de Origin-proef .
Stap 1: Basisinstellingen van Canvas
Voeg eerst het attribuut layoutsubtree toe aan je <canvas> -tag. Hierdoor weet de browser welke inhoud zich binnen het canvas bevindt, wordt deze voorbereid om binnen het canvas te worden weergegeven en wordt deze beschikbaar gesteld aan toegankelijkheidsstructuren.
<canvas id="canvas" style="width: 200px; height: 200px;" layoutsubtree>
<div id="form_element">
<label for="name">Name:</label> <input id="name" type="text">
</div>
</canvas>
De rastergrootte van het canvas aanpassen
Om te voorkomen dat de weergegeven inhoud onscherp wordt, moet u ervoor zorgen dat het raster van het canvas overeenkomt met de schaalverfactor van het apparaat.
const observer = new ResizeObserver(([entry]) => {
const dpc = entry.devicePixelContentBoxSize;
canvas.width = dpc ? dpc[0].inlineSize : Math.round(entry.contentRect.width * window.devicePixelRatio);
canvas.height = dpc ? dpc[0].blockSize : Math.round(entry.contentRect.height * window.devicePixelRatio);
});
const supportsDevicePixelContentBox =
typeof ResizeObserverEntry !== 'undefined' &&
'devicePixelContentBoxSize' in ResizeObserverEntry.prototype;
const options = supportsDevicePixelContentBox ? { box: 'device-pixel-content-box' } : {};
observer.observe(canvas, options);
Stap 2: Weergave
Voor een 2D-context gebruik je de drawElementImage methode. Doe dit binnen de paint gebeurtenis, die wordt geactiveerd wanneer het element opnieuw wordt getekend, bijvoorbeeld tijdens het markeren van tekst of gebruikersinvoer. Het is cruciaal om de CSS-transformatie van het element bij te werken met de retourwaarde, zodat de interactiviteit blijft werken.
const ctx = document.getElementById('canvas').getContext('2d');
const form_element = document.getElementById('form_element');
const canvas = document.getElementById('canvas');
canvas.onpaint = () => {
ctx.reset();
// Draw the form element at x:0, y:0
let transform = ctx.drawElementImage(form_element, 0, 0);
// Use the transform returned later on...
};
Renderen met WebGL
Voor WebGL gebruik je texElementImage2D . Deze werkt vergelijkbaar met texImage2D , maar gebruikt het DOM-element als bron.
canvas.onpaint = () => {
if (gl.texElementImage2D) {
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, form_element);
}
};
Renderen met WebGPU
WebGPU gebruikt de methode copyElementImageToTexture op de apparaatwachtrij, analoog aan copyExternalImageToTexture :
canvas.onpaint = () => {
root.device.queue.copyElementImageToTexture(
valueElement,
{ texture: targetTexture }
);
};
Stap 3: Werk de CSS-transformatie bij
Nu je het element op het canvas hebt weergegeven, moet je de browser laten weten waar het zich bevindt. Dit zorgt voor ruimtelijke synchronisatie tussen het canvas en de lay-out van de DOM. Dit is belangrijk zodat de browser de gebeurteniszone – bijvoorbeeld waar de gebruiker precies klikt of met de muis beweegt – correct kan koppelen aan de weergavelocatie van het element.
Voor het 2D-contextgeval past u de transformatie die door de rendering-aanroep wordt geretourneerd toe op de .style.transform property :
const ctx = document.getElementById('canvas').getContext('2d');
const form_element = document.getElementById('form_element');
const canvas = document.getElementById('canvas');
canvas.onpaint = () => {
ctx.reset();
// Draw the form element at x:0, y:0
let transform = ctx.drawElementImage(form_element, 0, 0);
// Sync the DOM location with the drawn location
form_element.style.transform = transform.toString();
};
Met WebGL of WebGPU hangt de positie van een element op het scherm af van hoe de uitvoertextuur door de shadercode wordt gebruikt en kan deze niet worden afgeleid uit de canvas-renderingcontext. Als uw shaderprogramma echter een typische model-view-projectie gebruikt om de textuur te tekenen, kunt u de nieuwe handige functie element.getElementTransform() gebruiken om een transformatie te berekenen die op dezelfde manier kan worden gebruikt als de retourwaarde van drawElementImage() . Om dit mogelijk te maken, moet u het volgende doen:
- Converteer een WebGL MVP-matrix naar een DOM-matrix .
- Normaliseer het HTML-element. HTML-elementen hebben afmetingen in pixels (bijvoorbeeld 200px breed). WebGL behandelt objecten echter meestal als 'eenheidsvierkanten', bijvoorbeeld van 0 tot 1. Als je niet normaliseert, zal je knop van 200px er 200 keer groter uitzien.
- Dit zet de afbeelding om naar de viewport van het canvas. Deze stap is de "herschaling"-fase: de eenheidsruimteberekening wordt teruggebracht naar de werkelijke pixelafmetingen van je
<canvas>-element op het scherm. Ook wordt de Y-as omgedraaid, omdat in WebGL omhoog positief is, maar in CSS omlaag. - Bereken de uiteindelijke transformatie. Vermenigvuldig de matrices in de volgende volgorde:
Viewport * MVP * Normalization.Door ze te combineren tot één uiteindelijke transformatie ontstaat een "kaart" die de browser precies vertelt waar de HTML-elementlaag moet worden geplaatst om uitgelijnd te zijn met de 3D-tekening. - Pas de transformatie toe op het HTML-element. Hierdoor wordt de HTML-elementlaag direct bovenop de weergegeven pixels geplaatst. Dit zorgt ervoor dat wanneer een gebruiker op een knop klikt of tekst selecteert, hij of zij het daadwerkelijke HTML-element bereikt.
if (canvas.getElementTransform) {
// 1. Convert WebGL MVP Matrix to DOM Matrix
const mvpDOM = new DOMMatrix(Array.from(htmlElementMVP));
// 2. Normalize the HTML element (pixels -> 1x1 unit square)
const width = targetHTMLElement.offsetWidth;
const height = targetHTMLElement.offsetHeight;
const cssToUnitSpace = new DOMMatrix()
.scale(1 / width, -1 / height, 1) // Shrink to unit size and flip Y
.translate(-width / 2, -height / 2); // Center the element
// 3. Map to the canvas viewport
const clipToCanvasViewport = new DOMMatrix()
.translate(canvas.width / 2, canvas.height / 2) // Move origin to center
.scale(canvas.width / 2, -canvas.height / 2, 1); // Stretch to canvas dimensions
// 4. Multiply: (Clip -> Pixels) * (MVP) * (pixels -> unit square)
const screenSpaceTransform = clipToCanvasViewport
.multiply(mvpDOM)
.multiply(cssToUnitSpace);
// 5. Apply to the transform
const computedTransform = canvas.getElementTransform(targetHTMLElement, screenSpaceTransform);
if (computedTransform) {
targetHTMLElement.style.transform = computedTransform.toString();
}
}
Bibliotheek- en frameworkondersteuning
Sommige populaire bibliotheken bieden al ondersteuning voor de HTML-in-Canvas-functie.
Three.js
Het handmatig bijwerken van matrices kan omslachtig zijn, daarom springen frameworks hier al op in. Three.js biedt experimentele ondersteuning met behulp van de nieuwe THREE.HTMLTexture :
const material = new THREE.MeshBasicMaterial();
material.map = new THREE.HTMLTexture(uiElement); // Pass the DOM element
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
PlayCanvas
PlayCanvas ondersteunt ook HTML-in-Canvas met behulp van hun texture API:
// Wait for the 'paint' event to set the source
canvas.addEventListener('paint', () => {
htmlTexture.setSource(htmlElement);
}, { once: true });
canvas.requestPaint();
// Keep up to date
canvas.addEventListener('paint', onPaintUpload);
const material = new pc.StandardMaterial();
material.diffuseMap = htmlTexture;
material.update();
Demo's
Voordat u de demo's uitprobeert, moet u ervoor zorgen dat uw omgeving correct is geconfigureerd .
Er zijn verschillende demo's die als referentie dienen voor het gebruik van de API. We zien nu al creatieve oplossingen vanuit de community, variërend van vertaalbare 3D-boeken tot UI-elementen die breken door glasshaders:
- Het 3D-boek : een 3D-boek dat met WebGL wordt weergegeven en gebruikmaakt van HTML-lay-out voor de pagina's. Gebruikers kunnen lettertypen wijzigen met CSS. Omdat het DOM-gebaseerd is, werkt de ingebouwde vertaling direct en kunnen AI-agenten de tekst met minder complexiteit extraheren.
- Interactieve 3D-gebruikersinterfaces : een WebGPU-schuifregelaar die licht breekt op basis van een onderliggend 3D-model, terwijl deze nog steeds reageert op standaard HTML-attributen
<input type="range">. - Geanimeerde texturen : Een dynamisch 3D-billboard dat een geanimeerd SVG-potlood rechtstreeks vanuit de DOM in een WebGL-textuur weergeeft, zonder dat een aangepaste animatielus nodig is.
- Refractieve overlays : een interactieve typografische laag die vervormd wordt door een bewegende 3D-cursor, maar volledig selecteerbaar en doorzoekbaar is met behulp van de zoekfunctie op de pagina.
Bekijk de verzameling demo's die door de community zijn gemaakt. Als je wilt dat jouw HTML-in-Canvas-demo in deze verzameling wordt opgenomen, dien dan een pull request in om deze toe te voegen.
Beperkingen
Hoewel de API krachtig is, kent deze wel een paar bewuste beperkingen:
- Inhoud van andere oorsprong: Om veiligheids- en privacyredenen werkt de API niet met iframe-inhoud van andere oorsprong.
- Scrollen in de hoofdthread: HTML in een canvas wordt getekend met JavaScript, wat betekent dat scrollen en animaties niet onafhankelijk van JavaScript kunnen worden bijgewerkt, zoals dat wel mogelijk is buiten het canvas. Ontwikkelaars moeten de prestatiekenmerken van scrollende content binnen het canvas zorgvuldig overwegen, in tegenstelling tot het scrollen van het hele canvas.
Feedback
Als je experimenteert met de HTML-in-Canvas API, horen we graag van je! Je kunt je aanmelden voor de Origin-proefversie om de functie op je site te gebruiken terwijl deze nog in de experimentele fase is. Zo help je ons bij het vormgeven van het API-ontwerp. Je kunt ook een issue aanmaken om feedback te geven.
Bronnen
- Ondersteuning voor HTML in canvas in Three.js
- HTML-in-Canvas in Three.js demo
- Ondersteuning voor HTML in Canvas in PlayCanvas: ontwikkelaarsdocumentatie
- HTML-in-Canvas in de PlayCanvas-demo
- HTML in canvas: uitleg
- Moderne webrichtlijnen voor AI-codeertools voor HTML-in-Canvas
- Chrome.dev-demo's voor HTML-in-Canvas
- Geweldige verzameling HTML-in-canvas-demo's gemaakt door de community.