Selama bertahun-tahun, developer web harus membuat pilihan arsitektur yang sulit saat membangun aplikasi visual yang kompleks dan sangat interaktif di web. Haruskah Anda mengandalkan DOM karena fitur semantiknya yang kaya atau merender langsung ke elemen <canvas> untuk performa grafis tingkat rendah?
Dengan HTML-in-Canvas API eksperimental baru—yang kini tersedia dalam uji coba origin—Anda tidak perlu memilih. API ini memungkinkan Anda menggambar konten DOM langsung ke kanvas 2D atau tekstur WebGL/WebGPU sekaligus menjaga UI tetap dapat berinteraksi, diakses, dan terhubung ke fitur browser favorit Anda. Dengan menggabungkan HTML dengan pemrosesan grafis tingkat rendah, Anda dapat menciptakan pengalaman yang sebelumnya tidak mungkin dilakukan.
DOM versus Canvas
Untuk memahami keunggulan API baru ini, sebaiknya lihat keunggulan relatif DOM dan Canvas.
DOM adalah elemen pokok UI web. Jetpack Compose menawarkan solusi tata letak teks siap pakai, menggunakan konten yang dipahami secara semantik untuk membuat antarmuka yang kaya. Hal ini memungkinkan pengguna melakukan operasi umum di seluruh halaman web dengan lancar—hal-hal yang sering kita anggap remeh, seperti menandai teks untuk disalin, atau mengklik kanan gambar untuk menyimpannya. DOM juga terintegrasi dengan fitur browser penting: alat aksesibilitas, terjemahan, temukan dalam halaman, mode pembaca, ekstensi, mode gelap, zoom browser, dan isi otomatis.
Di sisi lain, Canvas (dan WebGL/WebGPU) memungkinkan akses tingkat rendah untuk menggerakkan petak piksel untuk grafis 2D dan 3D yang sangat canggih. Game dan aplikasi web yang kompleks (seperti Google Dokumen atau Figma) memerlukan akses tingkat rendah yang berperforma tinggi ini. Karena kanvas pada dasarnya adalah petak piksel, fitur pendukung seperti teks responsif biasanya memerlukan logika UI kustom yang kompleks, sehingga meningkatkan ukuran paket Anda secara drastis. Yang terpenting, semua fitur browser canggih yang terintegrasi ke dalam DOM akan rusak sepenuhnya saat UI terjebak di dalam petak piksel kanvas statis.
Keuntungan membawa DOM ke Canvas
HTML-in-Canvas API adalah jembatan yang memberi Anda yang terbaik dari kedua dunia. Dengan menempatkan HTML di dalam elemen <canvas> dan menyinkronkan transformasinya, Anda memastikan konten tetap sepenuhnya interaktif, dan semua integrasi browser berfungsi secara otomatis.
Berikut adalah hal yang Anda dapatkan dengan mengizinkan DOM menangani UI Anda di dalam elemen <canvas>:
- Tata letak dan pemformatan teks: Tata letak dan pemformatan teks yang disederhanakan, termasuk teks multibaris atau dua arah dengan gaya CSS yang diterapkan.
- Kontrol formulir: Kontrol formulir yang ekspresif dan lebih mudah digunakan dengan opsi penyesuaian yang lengkap.
- Pemilihan teks, salin/tempel, dan klik kanan: Pengguna dapat menandai teks di dalam tampilan 3D Anda, atau mengklik kanan menu konteks secara native.
- Aksesibilitas: Konten yang dirender di dalam kanvas ditampilkan ke hierarki aksesibilitas. Sistem aksesibilitas dapat mengurai UI seperti yang dilakukan pada HTML normal, dan mengeksposnya ke sistem seperti pembaca layar.
- Temukan di halaman: Pengguna dapat menggunakan temukan di halaman (Ctrl/Cmd+F) untuk menelusuri teks, dan browser akan menandainya langsung dalam tekstur WebGL Anda.
- Dapat diindeks dan dihubungkan dengan agen AI: Web crawler dan agen AI dapat mengindeks dan membaca teks yang dirender ke dalam scene 2D dan 3D Anda dengan lancar.
- Integrasi ekstensi: Ekstensi browser berfungsi secara native. Misalnya, ekstensi penggantian teks akan otomatis memperbarui teks yang dirender pada mesh 3D Anda.
- Integrasi DevTools: Anda dapat memeriksa konten kanvas, termasuk untuk elemen UI WebGL/WebGPU langsung di Chrome DevTools. Sesuaikan gaya CSS di inspector, dan lihat perubahannya langsung diterapkan pada tekstur 3D.
Kasus penggunaan tingkat tinggi
API ini membuka potensi luar biasa di beberapa domain:
- Aplikasi berbasis kanvas yang besar: Aplikasi web yang berat kini dapat merender komponen UI aplikasi yang kompleks secara native ke dalam ruang kerja berbasis kanvas, sehingga meningkatkan aksesibilitas dan mengurangi ukuran paket.
- Adegan dan game 3D: Situs pemasaran, pengalaman WebXR imersif, dan game web kini dapat menempatkan UI web yang sepenuhnya dapat berinteraksi ke dalam adegan 3D—seperti buku 3D yang menggunakan teks DOM asli, atau terminal dalam game yang secara native mendukung penyalinan dan penempelan.
Cara menggunakan API
Penggunaan API terjadi dalam tiga fase: Menyiapkan kanvas, merender ke dalam kanvas, dan memperbarui transformasi CSS sehingga browser mengetahui posisi fisik elemen di layar.
Prasyarat
HTML-in-Canvas API sedang dalam uji coba origin di Chrome 148 hingga 150. Untuk mengujinya di situs Anda, gunakan Chrome Canary 149 atau yang lebih baru dengan tanda chrome://flags/#canvas-draw-element diaktifkan. Untuk mengaktifkan API bagi pengguna lain, daftar ke Uji Coba Origin.
Langkah 1: Penyiapan Canvas dasar
Pertama, tambahkan atribut layoutsubtree ke tag <canvas> Anda. Tindakan ini membuat browser mengetahui konten yang disusun bertingkat di dalam kanvas, menyiapkannya untuk ditampilkan di dalam kanvas, dan mengeksposnya ke hierarki aksesibilitas.
<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>
Menentukan ukuran petak kanvas
Untuk menghindari konten yang dirender menjadi buram, pastikan untuk menyesuaikan ukuran petak kanvas agar sesuai dengan faktor skala perangkat.
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);
Langkah 2: Rendering
Untuk konteks 2D, gunakan metode drawElementImage. Lakukan ini di dalam peristiwa paint, yang dipicu setiap kali elemen digambar ulang—misalnya, selama penandaan teks atau input pengguna. Sangat penting untuk memperbarui transformasi CSS elemen dengan nilai yang ditampilkan agar interaktivitas terus berfungsi.
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...
};
Merender dengan WebGL
Untuk WebGL, gunakan texElementImage2D. Fungsinya mirip dengan texImage2D, tetapi menggunakan elemen DOM sebagai sumber.
canvas.onpaint = () => {
if (gl.texElementImage2D) {
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, form_element);
}
};
Merender dengan WebGPU
WebGPU menggunakan metode copyElementImageToTexture pada antrean perangkat, yang serupa dengan copyExternalImageToTexture:
canvas.onpaint = () => {
root.device.queue.copyElementImageToTexture(
valueElement,
{ texture: targetTexture }
);
};
Langkah 3: Perbarui transformasi CSS
Setelah merender elemen ke dalam kanvas, Anda harus memberi tahu browser tentang lokasinya. Hal ini memastikan sinkronisasi spasial antara kanvas dan tata letak DOM. Hal ini penting agar browser dapat memetakan zona peristiwa dengan benar—seperti tempat pengguna mengklik atau mengarahkan kursor—dengan tempat elemen dirender.
Untuk kasus konteks 2D, terapkan transformasi yang ditampilkan oleh panggilan rendering ke .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();
};
Dengan WebGL atau WebGPU, lokasi elemen di layar bergantung pada cara tekstur output digunakan oleh kode shader, dan tidak dapat disimpulkan dari konteks rendering kanvas. Namun, jika program shader Anda menggunakan proyeksi tampilan model standar untuk menggambar tekstur, Anda dapat menggunakan fungsi praktis baru element.getElementTransform() untuk menghitung transformasi yang dapat digunakan dengan cara yang sama seperti nilai yang ditampilkan dari drawElementImage(). Untuk memfasilitasi hal ini, Anda perlu melakukan hal berikut:
- Konversikan Matriks MVP WebGL ke Matriks DOM.
- Menormalisasi elemen HTML. Elemen HTML diukur dalam piksel (misalnya, lebar 200 px). Namun, WebGL biasanya memperlakukan objek sebagai "kotak unit", misalnya, berkisar dari 0 hingga 1. Jika Anda tidak menormalisasi, tombol 200 px akan terlihat 200 kali lebih besar.
- Petakan ke area pandang kanvas. Langkah ini adalah fase "penskalaan ulang". Fungsi ini akan meregangkan matematika ruang unit tersebut kembali agar sesuai dengan dimensi piksel sebenarnya dari elemen
<canvas>Anda di layar. Matriks ini juga membalik sumbu Y, karena di WebGL, arah ke atas bernilai positif, tetapi di CSS, arah ke bawah bernilai positif. - Menghitung transformasi akhir. Kalikan matriks secara berurutan:
Viewport * MVP * Normalization.Menggabungkannya menjadi satu transformasi akhir akan menghasilkan "peta" yang memberi tahu browser secara tepat di mana lapisan elemen HTML tersebut harus berada agar sejajar dengan gambar 3D. - Terapkan transformasi ke elemen HTML. Tindakan ini akan memindahkan lapisan elemen HTML agar berada tepat di atas piksel yang dirender. Hal ini memastikan bahwa saat pengguna mengklik tombol atau memilih teks, mereka akan menekan elemen HTML yang sebenarnya.
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();
}
}
Dukungan library dan framework
Beberapa library populer telah menyediakan dukungan untuk fitur HTML-in-Canvas.
Three.js
Memperbarui matriks secara manual bisa jadi membosankan, itulah sebabnya framework sudah mulai digunakan. Three.js memiliki dukungan eksperimental menggunakan THREE.HTMLTexture baru:
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 juga mendukung HTML-in-Canvas menggunakan API teksturnya:
// 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
Sebelum mencoba demo, pastikan lingkungan Anda dikonfigurasi dengan benar.
Ada beberapa demo yang berfungsi sebagai referensi untuk menggunakan API. Kami telah melihat solusi kreatif dari komunitas, mulai dari buku 3D yang dapat diterjemahkan hingga elemen UI yang dibiaskan melalui shader kaca:
- Buku 3D: Buku 3D yang dirender WebGL yang menggunakan tata letak HTML untuk halamannya. Pengguna dapat menukar font dengan CSS. Karena berbasis DOM, terjemahan bawaan berfungsi secara instan, dan agen AI dapat mengekstrak teks dengan lebih mudah.
- UI 3D Interaktif: Penggeser jeli WebGPU yang membiaskan cahaya berdasarkan model 3D yang mendasarinya, sekaligus tetap merespons atribut langkah
<input type="range">HTML standar. - Tekstur animasi: Baliho 3D dinamis yang merender pensil SVG animasi menggunakan DOM langsung ke tekstur WebGL tanpa memerlukan loop animasi kustom.
- Overlay refraktif: Lapisan tipografi interaktif yang didistorsi oleh kursor 3D yang bergerak, tetapi dapat dipilih dan ditelusuri sepenuhnya menggunakan fitur temukan dalam halaman.
Lihat koleksi demo yang dibuat oleh komunitas. Jika Anda ingin demo HTML-in-Canvas Anda ditampilkan dalam koleksi ini, buat permintaan pull untuk menambahkannya.
Batasan
Meskipun efektif, API ini memiliki beberapa batasan yang disengaja:
- Konten lintas origin: Karena alasan keamanan dan privasi, API tidak berfungsi dengan konten iframe lintas origin.
- Scroll thread utama: HTML-in-canvas digambar dengan JavaScript, yang berarti scroll dan animasi tidak dapat diperbarui secara independen dari JavaScript, seperti yang dapat dilakukan di luar canvas. Developer harus mempertimbangkan dengan cermat karakteristik performa dari menempatkan konten scroll di dalam kanvas versus men-scroll seluruh kanvas.
Masukan
Jika Anda bereksperimen dengan HTML-in-Canvas API, kami ingin mendengar pendapat Anda. Anda dapat mendaftar ke uji coba origin untuk mengaktifkan fitur ini di situs Anda saat masih dalam fase eksperimental untuk membantu kami membentuk desain API. Anda juga dapat melaporkan masalah untuk memberikan masukan.
Resource
- Dukungan HTML-in-Canvas di Three.js
- Demo HTML-in-Canvas di Three.js
- Dukungan HTML-in-Canvas di PlayCanvas: Dokumentasi developer
- HTML-in-Canvas di demo PlayCanvas
- HTML-in-Canvas: Penjelasan
- Modern Web Guidance untuk alat coding AI untuk HTML-in-Canvas
- Demo Chrome.dev untuk HTML-in-Canvas
- Koleksi demo HTML-in-Canvas yang luar biasa dari komunitas