Web geliştiricileri, yıllardır web'de karmaşık ve yüksek düzeyde etkileşimli görsel uygulamalar oluştururken zor bir mimari seçim yapmak zorunda kalıyordu. Zengin semantik özellikleri için DOM'u mu kullanmalısınız yoksa düşük düzeyli grafik performansı için doğrudan <canvas> öğesine mi oluşturmalısınız?
Artık kaynak denemesinde kullanılabilen yeni deneysel HTML-in-Canvas API sayesinde seçim yapmanız gerekmez. Bu API, kullanıcı arayüzünü etkileşimli, erişilebilir ve favori tarayıcı özelliklerinize bağlı tutarken DOM içeriğini doğrudan 2D tuval veya WebGL/WebGPU dokusuna çizmenize olanak tanır. HTML'yi alt düzey 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ı olacaktır.
DOM, web kullanıcı arayüzünün temelidir. Anlamsal olarak anlaşılmış içerikleri kullanarak zengin arayüzler oluşturmak için hazır metin düzeni çözümleri sunar. Bu sayede kullanıcılar, web sayfalarında kopyalamak için metni vurgulama veya kaydetmek için bir resmi sağ tıklama gibi sık kullanılan işlemleri sorunsuz bir şekilde gerçekleştirebilir. DOM ayrıca erişilebilirlik araçları, çeviri, sayfada bul, okuyucu modu, uzantılar, koyu mod, tarayıcı yakınlaştırma ve otomatik doldurma gibi temel tarayıcı özellikleriyle de entegre olur.
Canvas (ve WebGL/WebGPU) ise son derece gelişmiş 2D ve 3D grafikler için piksel ızgarasını yönetmek üzere alt düzey erişime olanak tanır. Oyunlar ve karmaşık web uygulamaları (ör. Google Dokümanlar veya Figma) bu yüksek performanslı, alt düzey erişimi gerektirir. Tuval temel olarak bir piksel ızgarası olduğundan, duyarlı metin gibi özellikleri desteklemek için karmaşık özel kullanıcı arayüzü mantığı gerekiyordu. Bu da paket boyutunuzu önemli ölçüde artırıyordu. En önemlisi, kullanıcı arayüzü statik bir tuval piksel ızgarasına hapsolduğunda DOM'a entegre edilen tüm güçlü tarayıcı özellikleri tamamen bozulur.
DOM'u Canvas'a getirmenin avantajları
HTML-in-Canvas API, 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.
Kullanıcı arayüzünüzü bir <canvas> öğesi içinde DOM'un işlemesine izin verdiğinizde elde edeceğiniz avantajlar şunlardır:
- Metin düzeni ve biçimlendirme: CSS stilleri uygulanmış çok satırlı veya çift yönlü metinler de dahil olmak üzere basitleştirilmiş metin düzeni ve biçimlendirme.
- Form kontrolleri: Kapsamlı özelleştirme seçenekleriyle daha etkileyici ve kullanımı daha kolay form kontrolleri.
- Metin seçme, kopyalama/yapıştırma ve sağ tıklama: Kullanıcılar, 3D sahnelerinizdeki metinleri vurgulayabilir veya bağlam menülerini sağ tıklayabilir.
- Erişilebilirlik: Tuval içinde oluşturulan içerik, erişilebilirlik ağacına gösterilir. Erişilebilirlik sistemleri, kullanıcı arayüzünü normal HTML gibi ayrıştırabilir ve ekran okuyucular gibi sistemlere sunabilir.
- Sayfada bul: Kullanıcılar, metin aramak için sayfada bul (Ctrl/Cmd+F) özelliğini kullanabilir ve tarayıcı, metni doğrudan WebGL dokularınızda vurgular.
- Dizine eklenebilirlik ve yapay zeka ajanı arayüzüyle uyumluluk: Web tarayıcıları ve yapay zeka ajanları, 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 mesh'lerinizde 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. Denetleyicide bir CSS stilini değiştirin ve 3D dokuda anında güncellenmesini izleyin.
Karmaşık kullanım alanları
Bu API, çeşitli alanlarda inanılmaz bir potansiyel sunar:
- Tuval tabanlı büyük uygulamalar: Ağır web uygulamaları artık karmaşık uygulama kullanıcı arayüzü bileşenlerini yerel olarak tuval tabanlı çalışma alanlarında oluşturabilir. Bu sayede erişilebilirlik artar ve paket ağırlığı azalır.
- 3D sahneler ve tarayıcı oyunları: Pazarlama siteleri, sürükleyici WebXR deneyimleri ve tarayıcı oyunları artık 3D sahnelere tamamen etkileşimli web kullanıcı arayüzü yerleştirebilir. Örneğin, gerçek DOM metni kullanan 3D bir kitap veya kopyalama ve yapıştırmayı yerel olarak destekleyen oyun içi bir terminal.
API nasıl kullanılır?
API'nin kullanımı üç 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 ila 150 sürümlerinde kaynak denemesi aşamasındadır. Sitenizde test etmek için chrome://flags/#canvas-draw-element işaretinin etkin olduğu Chrome Canary 149 veya sonraki bir sürümü kullanın. API'yi diğer kullanıcılar için etkinleştirmek istiyorsanız kaynak denemesine kaydolun.
1. adım: Temel Canvas kurulumu
Öncelikle <canvas> etiketinize layoutsubtree özelliğini ekleyin. Bu sayede tarayıcı, tuvalin içine yerleştirilmiş içeriği algılar, tuvalin içinde gösterilmeye hazırlar ve erişilebilirlik ağaçlarına gösterir.
<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>
Tuval ızgarasını boyutlandırma
Oluşturulan içeriğin bulanık olmasını önlemek için tuval ızgarasını cihaz ölçeklendirme faktörüne uyacak ş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 her 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 ile benzer şekilde çalışır ancak kaynak olarak DOM öğesini kullanı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 yöntemine 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 tuvalde oluşturduğunuza göre, tarayıcıyı öğenin konumu hakkında güncellemeniz gerekir. Bu, tuval ile DOM'un düzeni arasında uzamsal senkronizasyon sağlar. Bu, tarayıcının etkinlik bölgesini (ör. kullanıcının tam olarak nereyi tıkladığı veya fareyle üzerine geldiği) öğenin oluşturulduğu yerle doğru şekilde eşleyebilmesi için önemlidir.
2D bağlamı 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 drawElementImage() tarafından döndürülen değerle aynı şekilde kullanılabilecek bir dönüşümü hesaplamak için yeni kolaylık işlevi element.getElementTransform()'ı kullanabilirsiniz. Bunu kolaylaştırmak için aşağıdakileri yapmanız gerekir:
- WebGL MVP matrisini DOM matrisine dönüştürün.
- HTML öğesini normalleştirin. HTML öğeleri piksel cinsinden boyutlandırılır (ör. 200 piksel genişlik). Ancak WebGL, nesneleri genellikle "birim kareler" olarak ele alır (ör. 0 ile 1 arasında). Normalleştirme yapmazsanız 200 piksellik 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 yeniden genişletir. Ayrıca Y eksenini de tersine çevirir. Çünkü WebGL'de yukarı yön pozitifken CSS'de aşağı yön pozitiftir. - Nihai 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 hizalanması için tam olarak nerede olması gerektiğini söyleyen bir "harita" oluşturur. - Dönüşümü HTML öğesine uygulayın. Bu işlem, HTML öğesi katmanını doğrudan oluşturulan piksellerinin üzerine taşır. Böylece, kullanıcı bir düğmeyi tıkladığında veya metin seçtiğinde gerçek HTML öğesine ulaşır.
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ı, HTML-in-Canvas ö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şi zaten üstleniyor. Three.js, yeni THREE.HTMLTexture kullanarak 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 HTML-in-Canvas'ı da 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'yi kullanmak için 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 yaratıcı çözümler görüyoruz:
- 3D kitap: Sayfaları için HTML düzeni kullanan, WebGL ile oluşturulmuş bir 3D kitap. Kullanıcılar CSS ile yazı tiplerini değiştirebilir. DOM tabanlı olduğu için yerleşik çeviri anında çalışır ve yapay zeka ajanları metni daha az karmaşıklıkla ayıklayabilir.
- Etkileşimli 3D kullanıcı arayüzleri: Standart HTML
<input type="range">adım özelliklerine yanıt verirken temel bir 3D modele göre ışığı kıran bir WebGPU jöle kaydırıcı. - Animasyonlu dokular: Özel bir animasyon döngüsüne ihtiyaç duymadan DOM'u doğrudan WebGL dokusuna dönüştürerek animasyonlu bir SVG kalem oluşturan dinamik bir 3D billboard.
- Kırılma efektli yer paylaşımları: Hareketli bir 3D imleçle bozulan ancak sayfada bulma özelliğiyle tamamen seçilebilen ve aranabilen etkileşimli bir tipografi katmanı.
Topluluk tarafından oluşturulan demo koleksiyonuna göz atın. HTML-in-Canvas demonuzun bu koleksiyonda yer almasını istiyorsanız eklemek için bir pull isteği oluşturun.
Sınırlamalar
API güçlü olsa da bilinçli olarak uygulanan birkaç sınırlaması vardır:
- Çapraz kaynak içerik: API, güvenlik ve gizlilik nedenleriyle çapraz kaynak iframe içeriğiyle çalışmaz.
- Ana iş parçacığı kaydırma: Tuval içindeki HTML, JavaScript ile çizilir. Bu, kaydırma ve animasyonların tuval dışında olduğu gibi JavaScript'ten bağımsız olarak güncellenemeyeceği anlamına gelir. Geliştiriciler, kaydırılabilir içeriği tuvalin içine yerleştirmenin ve tüm tuvalin kaydırılmasının performans özelliklerini dikkatlice değerlendirmelidir.
Geri bildirim
HTML-in-Canvas API'yi deniyorsanız görüşlerinizi öğrenmek isteriz. API tasarımını şekillendirmemize yardımcı olmak için kaynak denemesine kaydolarak özelliği deneysel aşamadayken sitenizde etkinleştirebilirsiniz. Geri bildirim göndermek için sorun bildiriminde de bulunabilirsiniz.
Kaynaklar
- Three.js'de HTML-in-Canvas desteği
- Three.js demosunda Canvas'ta HTML
- PlayCanvas'ta Canvas içinde HTML desteği: Geliştirici belgeleri
- PlayCanvas'ta HTML-in-Canvas demosu
- HTML-in-Canvas: Açıklayıcı
- HTML-in-Canvas için yapay zeka kodlama araçlarına yönelik Modern Web Rehberi
- HTML-in-Canvas için Chrome.dev demoları
- Topluluk tarafından hazırlanan muhteşem HTML-in-Canvas demo koleksiyonu