Web geliştiriciler, web'de karmaşık ve yüksek düzeyde etkileşimli görsel uygulamalar oluştururken yıllardır zorlu bir mimari seçim yapmak zorunda kalıyor. Zengin semantik özelliklerinden dolayı DOM'u mu kullanmalı yoksa düşük düzeyli grafik performansı için doğrudan <canvas> öğesine mi oluşturmalı?
Şu anda kaynak denemesinde kullanılabilen yeni deneysel HTML-in-Canvas API ile seçim yapmanız gerekmez. Bu API, kullanıcı arayüzünü etkileşimli, erişilebilir ve en sevdiğiniz tarayıcı özelliklerine bağlı tutarken DOM içeriğini doğrudan 2D tuvale veya WebGL/WebGPU dokusuna çizmenize olanak tanır. HTML'yi düşük düzeyli grafik işleme ile birleştirerek daha önce mümkün olmayan deneyimler oluşturabilirsiniz.
DOM ve Canvas
Bu yeni API'nin gücünü anlamak için hem DOM'un hem de Canvas'ın göreceli güçlü yönlerine bakmak faydalı olur.
DOM, web kullanıcı arayüzünün temelidir. Zengin arayüzler oluşturmak için semantik olarak anlaşılan içeriği kullanarak kutudan çıktığı gibi metin düzeni çözümleri sunar. Bu sayede kullanıcılar, web sayfalarında sorunsuz bir şekilde yaygın işlemleri gerçekleştirebilir. Örneğin, kopyalamak için metni vurgulama veya kaydetmek için bir resmi sağ tıklama gibi genellikle göz ardı ettiğimiz işlemler. DOM, temel tarayıcı özellikleriyle de entegre olur: erişilebilirlik araçları, çeviri, sayfada bul, okuyucu modu, uzantılar, koyu mod, tarayıcı yakınlaştırma ve otomatik doldurma.
Diğer yandan Canvas (ve WebGL/WebGPU), son derece gelişmiş 2D ve 3D grafikler için bir piksel ızgarasını yönlendirmek üzere düşük düzeyde erişime olanak tanır. Oyunlar ve karmaşık web uygulamaları (ör. Google Dokümanlar veya Figma) bu yüksek performanslı, düşük düzeyli erişimi gerektirir. Zemin temelde bir piksel ızgarası olduğundan, duyarlı metin gibi özellikleri desteklemek için karmaşık özel kullanıcı arayüzü mantığı kullanılması gerekiyordu. Bu da paket boyutunuzu önemli ölçüde artırıyordu. En önemlisi, kullanıcı arayüzü statik bir tuval piksel ızgarasında sıkıştığında DOM'a entegre edilen tüm güçlü tarayıcı özellikleri tamamen bozulur.
DOM'u Canvas'a taşımanın avantajları
Tuvalde HTML API'si, her iki dünyanın en iyi özelliklerini sunan bir köprüdür. HTML'yi <canvas> öğesinin içine yerleştirip dönüşümünü senkronize ederek içeriğin tamamen etkileşimli kalmasını ve tüm tarayıcı entegrasyonlarının otomatik olarak çalışmasını sağlarsınız.
DOM'un kullanıcı arayüzünüzü bir <canvas> öğesi içinde işlemesine izin vererek elde edeceğiniz avantajlar:
- Metin düzeni ve biçimlendirme: CSS stilleri uygulanmış çok satırlı veya çift yönlü metin de dahil olmak üzere basitleştirilmiş metin düzeni ve biçimlendirme.
- Form kontrolleri: Kapsamlı özelleştirme seçenekleriyle etkileyici ve kullanımı daha kolay form kontrolleri.
- Metin seçimi, kopyalama/yapıştırma ve sağ tıklama: Kullanıcılar, 3D sahnelerinizdeki metinleri vurgulayabilir veya içerik menülerini sağ tıklayabilir.
- Erişilebilirlik: Tuvalin içinde oluşturulan içerik, erişilebilirlik ağacına sunulur. Erişilebilirlik sistemleri, kullanıcı arayüzünü normal HTML gibi ayrıştırabilir ve ekran okuyucular gibi sistemlere sunabilir.
- Sayfada bulma: Kullanıcılar, metin aramak için sayfada bulma (Ctrl/Cmd+F) özelliğini kullanabilir. Tarayıcı, metni doğrudan WebGL dokularınızda vurgular.
- Dizine eklenebilirlik ve yapay zeka aracısı arayüzü: Web tarayıcıları ve yapay zeka aracıları, 2D ve 3D sahnelerinizde oluşturulan metni sorunsuz bir şekilde dizine ekleyip okuyabilir.
- Uzantı entegrasyonu: Tarayıcı uzantıları yerel olarak çalışır. Örneğin, metin değiştirme uzantısı, 3D ağlarınızda oluşturulan metni otomatik olarak günceller.
- Geliştirici Araçları entegrasyonu: WebGL/WebGPU kullanıcı arayüzü öğeleri de dahil olmak üzere tuval içeriğinizi doğrudan Chrome Geliştirici Araçları'nda inceleyebilirsiniz. İnceleyicide bir CSS stilini ayarlayın ve 3D dokuda anında güncellendiğini görün.
Karmaşık kullanım alanları
Bu API, çeşitli alanlarda inanılmaz bir potansiyel sunar:
- Büyük tuval tabanlı uygulamalar: Ağır web uygulamaları artık karmaşık uygulama kullanıcı arayüzü bileşenlerini tuval tabanlı çalışma alanlarında yerel olarak oluşturarak erişilebilirliği artırıyor ve paket ağırlığını azaltıyor.
- 3D sahneler ve oyunlar: Pazarlama siteleri, sürükleyici WebXR deneyimleri ve web oyunları artık 3D sahnelerde tamamen etkileşimli web kullanıcı arayüzü yerleştirebilir. Örneğin, gerçek DOM metni kullanan bir 3D kitap veya kopyalama ve yapıştırmayı doğal olarak destekleyen bir oyun içi terminal.
API nasıl kullanılır?
API'yi kullanma üç aşamada gerçekleşir: tuvalinizi ayarlama, tuvalde oluşturma ve CSS dönüşümünü güncelleme. Böylece tarayıcı, öğenin ekranda fiziksel olarak nerede bulunduğunu bilir.
Ön koşullar
HTML-in-Canvas API, Chrome 148-150 sürümlerinde kaynak denemesi aşamasındadır. Sitenizde test etmek için chrome://flags/#canvas-draw-element flag'i etkinleştirilmiş Chrome Canary 149 veya sonraki sürümlerini kullanın. API'yi diğer kullanıcılar için etkinleştirmek istiyorsanız Origin denemesine kaydolun.
1. adım: Temel Canvas kurulumu
Öncelikle layoutsubtree özelliğini <canvas> etiketinize ekleyin. Bu sayede tarayıcı, tuvalin içine yerleştirilmiş içeriklerin farkına varır, bunları tuvalin içinde gösterilmeye hazırlar ve erişilebilirlik ağaçlarına sunar.
<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>
Kanvas ızgarasını boyutlandırma
Oluşturulan içeriğin bulanık olmaması için tuval ızgarasını cihazın ölçeklendirme faktörüne uygun şekilde boyutlandırdığınızdan emin olun.
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);
2. adım: Oluşturma
2D bağlam için drawElementImage yöntemini kullanın. Bunu, öğe yeniden çizildiğinde (ör. metin vurgulama veya kullanıcı girişi sırasında) tetiklenen paint etkinliğinin içinde yapın. Etkileşimin çalışmaya devam etmesi için öğenin CSS dönüşümünü dönüş değeriyle güncellemek çok önemlidir.
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...
};
WebGL ile oluşturma
WebGL için texElementImage2D kullanırsınız. texImage2D işlevine benzer ancak kaynak olarak DOM öğesini alır.
canvas.onpaint = () => {
if (gl.texElementImage2D) {
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, form_element);
}
};
WebGPU ile oluşturma
WebGPU, cihaz kuyruğunda copyExternalImageToTexture ile benzer şekilde copyElementImageToTexture yöntemini kullanır:
canvas.onpaint = () => {
root.device.queue.copyElementImageToTexture(
valueElement,
{ texture: targetTexture }
);
};
3. adım: CSS dönüşümünü güncelleyin
Öğeyi kanvasa yerleştirdiğinize göre, tarayıcıyı öğenin konumu hakkında güncellemeniz gerekir. Bu, tuval ile DOM'un düzeni arasında mekansal senkronizasyon sağlar. Bu, tarayıcının etkinlik bölgesini (ör. kullanıcının tam olarak nereye tıkladığı veya fareyle üzerine geldiği) öğenin oluşturulduğu yerle doğru şekilde eşleyebilmesi için önemlidir.
2D bağlam durumu için, oluşturma çağrısı tarafından döndürülen dönüşümü .style.transform property öğesine uygulayın:
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();
};
WebGL veya WebGPU ile bir öğenin ekrandaki konumu, çıkış dokusunun gölgelendirici kodu tarafından nasıl kullanıldığına bağlıdır ve tuval oluşturma bağlamından çıkarılamaz. Ancak gölgelendirici programınız dokuyu çizmek için tipik bir model görünümü projeksiyonu kullanıyorsa element.getElementTransform() işlevinden döndürülen değerle aynı şekilde kullanılabilecek bir dönüşüm hesaplamak için yeni kolaylık işlevi drawElementImage()'yi kullanabilirsiniz. Bunu kolaylaştırmak için aşağıdakileri yapmanız gerekir:
- WebGL MVP Matrix'i DOM Matrix'e dönüştürme
- HTML öğesini normalleştirin. HTML öğeleri piksel cinsinden boyutlandırılır (örneğin, 200 piksel genişlik). Ancak WebGL, nesneleri genellikle 0 ile 1 arasında değişen "birim kareler" olarak ele alır. Normalleştirme yapmazsanız 200 piksel boyutundaki düğmeniz 200 kat daha büyük görünür.
- Tuval görüntü alanıyla eşleyin. Bu adım, "yeniden ölçeklendirme" aşamasıdır. Bu, birim alanı matematiğini ekrandaki
<canvas>öğenizin gerçek piksel boyutlarıyla eşleşecek şekilde tekrar genişletir. Ayrıca, WebGL'de yukarı pozitif, CSS'de ise aşağı pozitif olduğundan Y eksenini ters çevirir. - Son dönüşümü hesaplayın. Matrisleri sırayla çarpın:
Viewport * MVP * Normalization.Bunları tek bir nihai dönüşümde birleştirmek, tarayıcıya HTML öğesi katmanının 3D çizimle hizalanmak için tam olarak nerede bulunması gerektiğini söyleyen bir "harita" oluşturur. - Dönüşümü HTML öğesine uygulayın. Bu işlem, HTML öğesi katmanını oluşturulan piksellerin doğrudan üzerine taşır. Bu sayede, kullanıcı bir düğmeyi tıkladığında veya metin seçtiğinde gerçek HTML öğesine dokunmuş olur.
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();
}
}
Kitaplık ve çerçeve desteği
Popüler kitaplıklardan bazıları, tuvalde HTML özelliği için destek sunmaya başladı.
Three.js
Matrisleri manuel olarak güncellemek sıkıcı olabilir. Bu nedenle, çerçeveler bu işe çoktan başladı. Three.js, yeni THREE.HTMLTexture kullanılarak deneysel destek sunar:
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, doku API'sini kullanarak Canvas'ta HTML'yi de destekler:
// 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();
Demolar
Demoları denemeden önce ortamınızın doğru şekilde yapılandırıldığından emin olun.
API'nin kullanımıyla ilgili referans olarak kullanabileceğiniz çeşitli demolar vardır. Topluluktan, çevrilebilir 3D kitaplardan cam gölgelendiricilerle kırılan kullanıcı arayüzü öğelerine kadar çeşitli yaratıcı çözümler görüyoruz:
- 3D kitap: Sayfaları için HTML düzeni kullanan, WebGL ile oluşturulmuş 3D kitap. Kullanıcılar, CSS ile yazı tiplerini değiştirebilir. Yerleşik çeviri, DOM tabanlı olduğundan anında çalışır ve yapay zeka aracıları metni daha az karmaşıklıkla ayıklayabilir.
- Etkileşimli 3D kullanıcı arayüzleri: Alttaki 3D modele göre ışığı kıran ve standart HTML
<input type="range">adım özelliklerine yanıt vermeye devam eden bir WebGPU jöle kaydırıcı. - Animasyonlu dokular: Özel bir animasyon döngüsüne gerek kalmadan, DOM'u doğrudan bir WebGL dokusuna kullanarak animasyonlu bir SVG kalem oluşturan dinamik bir 3D reklam panosu.
- Kırılma katmanları: Hareket eden bir 3D imleç tarafından bozulmuş, ancak sayfada bulma özelliği kullanılarak tamamen seçilebilir ve sayfa içi aranabilir etkileşimli bir tipografi katmanı.
Topluluk tarafından oluşturulan demo koleksiyonuna göz atın. Canvas'ta HTML demosunun bu koleksiyonda yer almasını istiyorsanız pull isteği oluşturarak demoyu ekleyin.
Sınırlamalar
API güçlü olsa da bazı sınırlamaları vardır:
- Çapraz kaynak içerik: API, güvenlik ve gizlilik nedenleriyle çapraz kaynak iframe içeriğiyle çalışmaz.
- Ana iş parçacığında kaydırma: Tuvaldeki HTML, JavaScript ile çizilir. Bu nedenle, kaydırma ve animasyonlar, tuvalin dışında olduğu gibi JavaScript'ten bağımsız olarak güncellenemez. Geliştiriciler, kaydırılan içeriği tuvalin içine yerleştirmenin veya tuvalin tamamını kaydırmanın performans özelliklerini dikkatlice değerlendirmelidir.
Geri bildirim
HTML-in-Canvas API'yi deniyorsanız görüşlerinizi öğrenmek isteriz. API tasarımına yön vermemize yardımcı olmak için özelliği deneysel aşamadayken sitenizde etkinleştirmek üzere kaynak denemesine kaydolabilirsiniz. Geri bildirimde bulunmak için sorun da bildirebilirsiniz.
Kaynaklar
- Three.js'de HTML-in-Canvas desteği
- Three.js demosunda Canvas'ta HTML
- PlayCanvas'ta Canvas'ta HTML desteği: Geliştirici belgeleri
- PlayCanvas demosunda Canvas'ta HTML
- HTML-in-Canvas: Açıklayıcı
- Canvas'ta HTML için yapay zeka kodlama araçlarıyla ilgili modern web rehberi
- Tuvalde HTML için Chrome.dev demoları
- Topluluk tarafından oluşturulan Canvas'ta HTML ile ilgili harika demo koleksiyonu