HTML-in-Canvas: Teknik kavramlar

HTML-in-Canvas API, etkileşimli ve erişilebilir DOM alt ağaçlarını doğrudan 2D, WebGL veya WebGPU tuval bağlamlarında oluşturmanıza olanak tanır.

Bu kılavuzda, tüm tuval bağlamlarında ortak olan HTML-in-Canvas teknik kavramları açıklanmaktadır:

Tuvalinizi HTML oluşturmaya hazırlama

HTML öğelerini <canvas> içinde oluşturmak için aşağıdaki iki özelliği yapılandırın:

  • <canvas> üzerinde content="drawable" değerini ayarlayın: Bu, canvas öğesinin alt DOM düğümlerini yalnızca gizli içerik olarak değerlendirmek yerine düzenlemesini sağlar.
  • Hedef öğelerde drawable özelliğini ayarlama: Bu, bir öğeyi tuval oluşturma için uygun bağımsız bir boyama kökü olarak işaretler.
<canvas id="mainCanvas" width="600" height="400" content="drawable">
  <div id="parentElement" class="card" drawable>
    <h2>Container Title</h2>
    <p>Descriptive text inside the parent element.</p>
    <!-- Nested drawable: excluded from parentElement and drawn independently -->
    <button id="childElement" class="btn" drawable>Action Button</button>
  </div>
</canvas>

Çizilebilir alt ağacı anlama

HTML'yi tuvalde oluşturulacak şekilde hazırlarken hangi öğelerin bağımsız olarak çizilmesi gerektiğine karar vermeniz gerekir. Her drawable öğesi, bağımsız bir boyama kökünü ve çizilebilir alt ağacını temsil eder.

Çizilebilir alt ağaç, drawable özelliğine sahip bir öğeden ve tüm alt öğelerinden oluşur. drawable özelliğine sahip iç içe yerleştirilmiş alt öğeler hariç tutulur. Örneğin:

<canvas content="drawable">
  <!-- Subtree A (Renders parent container and text) -->
  <div id="parentElement" drawable>
    <h2>Parent Card Heading</h2>
    <p>Card body text content.</p>
    <!-- Subtree B (Excluded from Subtree A and drawn separately) -->
    <button id="nestedChildElement" drawable>
      Clickable Button
    </button>
  </div>
</canvas>
Öğe Boya sınırı Davranış
#parentElement (Alt ağaç A) Arka plan paneli, başlık ve paragraf drawElementImage(parentElement, ...) çağrıldığında oluşturulur
#nestedChildElement (Alt ağaç B) Düğme yüzeyi ve metin #parentElement alt ağacından hariç tutuldu; ayrı bir drawElementImage(nestedChildElement, ...) çağrısında oluşturuldu
drawable düğmesi, drawable kartının içine yerleştirilmiştir ve her biri kendi drawElementImage() çağrısı tarafından çizilir.

Çizilebilir öğeleri iç içe yerleştirmek, semantik HTML ilişkilerini korumanıza olanak tanırken alt öğeleri tuval sahnenizde bağımsız olarak katmanlama, konumlandırma veya canlandırma özgürlüğü sağlar.

paint etkinliğini anlama

HTML-in-Canvas API, paint etkinliğini kullanıma sunar. Tarayıcı, drawable alt öğelerinden herhangi birinde düzen, metin veya CSS stilleri değiştiğinde <canvas> üzerinde otomatik olarak bir paint etkinliği gönderir.

canvas.addEventListener("paint", (event) => {
  // Clear the viewport
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Redraw elements individually
  ctx.drawElementImage(parentElement, 20, 20);
  ctx.drawElementImage(childElement, 40, 90);
});

paint etkinliği, standart DOM Event'yi genişleten ve yalnızca son boyamadan bu yana görsel olarak değişen (ör. stil değişiklikleri, düzen kaymaları, metin düzenlemeleri veya odak durumu değişiklikleri) drawable alt öğelerini içeren bir changedElements mülküne sahip olan CanvasPaintEvent örneğini gönderir.

canvas.addEventListener("paint", (event) => {
  // Array of changed elements
  for (const el of event.changedElements) {
    console.log(`Element #${el.id} visually changed and needs repainting`);
  }
});
Tarayıcı, çizilebilir bir alt öğenin oluşturulması her değiştiğinde tuvalde boyama işlemini tetikler.

Çerçeve yaşam döngüsü

paint etkinliği içinde yürütülen tuval çizim komutları mevcut oluşturma güncellemesinde görünür ancak paint etkinliği içinde yapılan DOM değişiklikleri sonraki kareye kadar gösterilmez:

canvas.addEventListener("paint", () => {
  // Renders in the CURRENT rendering update:
  ctx.drawElementImage(parentElement, 20, 20);

  // Modifying DOM styles inside paint schedules a new layout cycle;
  // changes will appear in the NEXT frame:
  parentElement.style.backgroundColor = "#e0f2fe";
});

İç içe yerleştirilmiş tuvaller

Sayfanızda iç içe yerleştirilmiş <canvas content="drawable"> öğeleri varsa tarayıcı, paint etkinliklerini ters DOM ağacı sırasına göre gönderir (alt öğeler üst öğelerden önce tetiklenir). Bu sayede, alt tuval öğeleri, üst tuval öğeleri tarafından birleştirilmeden önce oluşturulur.

Aşağıdaki örnekte, içteki nestedElement öğesinin görsel olarak değişmesi gerektiğini varsayalım (örneğin, kullanıcı işaretçiyi üzerinde tuttuğunda CSS arka plan rengi değişir). İlk olarak paint etkinliği nestedCanvas tuvalinde tetiklenir. Dinleyicisi, alt arabelleği temizler ve güncellenen durumu oluşturmak için ctx.drawElementImage(nestedElement, ...) yöntemini çağırır. Ardından paint etkinliği parentCanvas tarihinde tetiklenir. Dinleyicisi, wrapperElement öğesini (güncellenmiş nestedCanvas tuvalini içerir) sahneye çizer.

<!-- Outer parent canvas -->
<canvas id="parentCanvas" width="800" height="600" content="drawable">
  <!-- Wrapper container for nested content -->
  <div id="wrapperElement" drawable>

    <!-- Nested child canvas -->
    <canvas id="nestedCanvas" width="300" height="200" content="drawable">
      <div id="nestedElement" class="badge" drawable>
        Inner Child Content
      </div>
    </canvas>

  </div>
</canvas>

<script>
const parentCanvas = document.getElementById("parentCanvas");
const parentCtx = parentCanvas.getContext("2d");

const nestedCanvas = document.getElementById("nestedCanvas");
const nestedCtx = nestedCanvas.getContext("2d");

const nestedElement = document.getElementById("nestedElement");
const wrapperElement = document.getElementById("wrapperElement");

// ============================================================================
// 1. Child Canvas Listener (Fires FIRST)
// ============================================================================
nestedCanvas.addEventListener("paint", (event) => {
  // Clear and redraw the child canvas with the updated inner element
  nestedCtx.clearRect(0, 0, nestedCanvas.width, nestedCanvas.height);
  nestedCtx.drawElementImage(nestedElement, 10, 10);

  console.log("1. Child nested canvas rendered fresh pixels.");
});

// ============================================================================
// 2. Parent Canvas Listener (Fires SECOND)
// ============================================================================
parentCanvas.addEventListener("paint", (event) => {
  // Clear and redraw the parent canvas scene
  parentCtx.clearRect(0, 0, parentCanvas.width, parentCanvas.height);

  // Draws the wrapper container (including the freshly painted nestedCanvas)
  parentCtx.drawElementImage(wrapperElement, 50, 50);

  console.log("2. Parent canvas composited the complete scene.");
});
</script>
<canvas content="drawable"> öğeleri iç içe yerleştirildiğinde boyama etkinlikleri ters ağaç sırasına göre tetiklenir: Önce iç içe yerleştirilmiş tuval boyanır, böylece üst tuval güncel piksellerini çizebilir.

paint etkinliğini tetikleme

DOM metni veya stilleri değişmemiş olsa bile sonraki karede tek bir paint etkinliği planlamak için requestPaint() yöntemini çağırın. Bu, hareketli öğeler içeren etkileşimli sahneleri oluşturmak için kullanılabilir. Örneğin:

let animatedX = 0;

canvas.addEventListener("paint", () => {
  animatedX += 1.5; // Move the element horizontally
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawElementImage(staticElement, 20, 20);
  ctx.drawElementImage(movingElement, animatedX, 200);
  // Request canvas paint on each rendering update for smooth animation
  canvas.requestPaint();
});

canvas.requestPaint();

Bir öğenin geometrisini güncelleme

Bir HTML öğesini tuvale çizdiğinizde çizim kodunuz, öğenin nerede görüneceğini ve ne kadar büyük olacağını belirler. Normal HTML içeriğinden farklı olarak, öğenin ekrandaki konumu ve boyutu (geometrisi) yalnızca hesaplanan stiline göre belirlenemez. Tarayıcı, aşağıdakileri yapmak için bir öğenin geometrisine ihtiyaç duyar:

  • Tıklama, dokunma ve işaretçi hareketi gibi giriş etkinliklerini doğru öğeye gönderme Buna isabet testi denir.
  • getBoundingClientRect() gibi düzen sorgularından doğru sonuçlar döndürür.
  • Öğenin sınırlarını ekran büyüteçleri gibi yardımcı teknolojilere bildirme

2D'de drawElementImage(), geometriyi varsayılan olarak otomatik şekilde günceller. Öğenin hesaplanan stili ve bağlamın mevcut dönüşümüyle birlikte ilettiğiniz bağımsız değişkenler, öğenin nerede görüneceğini belirlemek için yeterlidir.

WebGL ve WebGPU bağlamlarında tarayıcı, geometriyi belirleyemez. Bir HTML öğesi bir dokuya kopyalanır ve gölgelendirici programınız bu dokuyu sahneye dilediğiniz şekilde eşleyebilir. Tarayıcıya öğenin nerede göründüğünü bildirmek için updateElementGeometry() öğesine bir dönüşüm iletin.

Kod, updateElementGeometry() işlevini çağırmayı bıraktığında tarayıcı artık 3D karttaki HTML öğelerinin nerede olduğunu bilmez. Kart dönmeye devam eder ancak tıklamalar yine de kartın eski konumuna yönlendirilir.

Geometriyi güncellemek için yönteme bir uzamsal dönüşüm iletin. Farklı bağlamlar için nasıl dönüşüm oluşturacağınızı öğrenin:

İsabet testi yığınlama sırasını yönetme

Varsayılan olarak, bir öğenin geometrisi her güncellendiğinde tarayıcı, öğeyi etkileşim yığınının en üstüne getirir (en son çizilen = en üstteki tıklama hedefi). Bu davranışı preserveHitTestOrder kullanarak kontrol edebilirsiniz:

// Updates the backdrop without stealing clicks from foreground elements
canvas.updateElementGeometry(backgroundElement, {
  canvasTransform: customTransform,
  preserveHitTestOrder: true
});
Seçenek Davranış Kullanım alanı
preserveHitTestOrder: false (varsayılan) Öğeyi isabet testi yığınının en üstüne taşır. Önce tıklanması gereken ön plan öğeleri.
preserveHitTestOrder: true Öğeyi mevcut yığın önceliğinde bırakır. Ön plan öğelerinden sonra güncellenmesi gereken arka plan öğeleri.

Geometriyi sıfırla

Bir öğeyi DOM'dan kaldırmadan çizimini geri almak için canvas.clearElementGeometry() yöntemini çağırın. Bu işlem, öğenin tuval dönüşümünü sıfırlar ve erişilebilirlik ağacında ekran dışında olarak işaretler:

// Remove from canvas hit-testing and mark as offscreen
canvas.clearElementGeometry(childElement);

Boyutlandırma ve birimler

HTML'yi bir tuvale çizdiğinizde tarayıcı aynı anda üç koordinat sisteminde çalışır: CSS pikselleri, cihaz pikselleri ve tuval ızgara pikselleri.

CSS pikselleri, düzen sırasında içeriği ölçmek ve konumlandırmak için kullanılır. Ardından tarayıcı, sayfayı fiziksel cihaz piksellerine dönüştürmek için bunları devicePixelRatio ile ölçeklendirir. CSS pikselleri mantıksal bir birim olduğundan, konumlar ve boyutlar genellikle cihaz pikselinin kesirleri olarak sonuçlanır (örneğin, 10,5 CSS piksel × 1,5 DPR = 15,75 cihaz pikseli). Ekranlar yalnızca tam pikselleri renklendirebildiğinden tarayıcının, pikseller arasında kalan her şeyi çözmesi gerekir. Bunu iki şekilde yapar:

  • Yapışma: Kutu sınırları, kenarlıklar ve katman ofsetleri, net kalmaları için en yakın tam cihaz pikseline yuvarlanır.
  • Kenar yumuşatma: Metin glifleri, eğriler ve dönüştürülmüş kenarlar için kısmi kapsama alanı komşu piksellerle harmanlanır. Böylece pürüzsüz ve doğru yerde görünmeye devam ederler.

<canvas>, width ve height özellikleri tarafından ayarlanan kendi piksel ızgarasına sahiptir. Bu ızgara, hem CSS piksellerinden hem de cihaz piksellerinden ayrıdır. Tarayıcı, tuvali görüntülemek için tuval bit eşlemini fiziksel piksel ızgarasında yeniden örnekler ve her cihaz pikselini doldurmak için komşu tuval piksellerini birleştirir. Tarayıcının CSS, tuval ve cihaz pikselleri arasında nasıl eşleme yaptığını öğrenmek için devicePixelContentBox ile piksel açısından mükemmel oluşturma başlıklı makaleyi inceleyin.

Tüm drawable HTML öğeleri CSS pikseli cinsinden düzenlenir. Tuvalde çizilen içerik, CSS piksellerinden tuval ızgarasına ve ardından tuval ızgarasından fiziksel piksellere olmak üzere iki dönüşümden geçebileceğinden, tam olarak hizalanmayan her adım başka bir karıştırma turu ekler ve sonuç bulanık görünebilir. Bunu önlemek için tuval ızgarasını fiziksel piksel ızgarasıyla eşleştirin. Bunun iki amacı vardır:

  • Tutarlı oluşturma: Tuval, fiziksel piksellerle 1:1 eşleşiyorsa CSS piksellerini tuval piksellerine dönüştürmek, tarayıcının normal HTML için CSS piksellerini fiziksel piksellere dönüştürmesiyle aynı sonucu verir.
  • Ek karıştırma yok: Tuval, fiziksel piksellerle 1:1 eşleşiyorsa cihaz piksellerine dönüştürme işlemi bulanıklık eklemez.

Aşağıdaki resimde, bulanık içerikle sonuçlanan bir piksel dönüşümü gösterilmektedir. Üç ızgara hizalanmıyor: Tuval öğesi 360 × 180 CSS pikseli, tuval boyutu ise 300 × 150 tuval ızgara pikseli. 1,5 cihaz piksel oranında, tuval 540 × 270 cihaz pikselini kapsar. Bir CSS pikseli 1,5 cihaz pikselini, bir tuval pikseli ise 1,2 CSS pikselini veya 1,8 cihaz pikselini kapsar (panel 1). Daire çizildiğinde, konumu ve yarıçapı CSS pikselleri değil, tuval pikselleri cinsinden olur. Bu nedenle tarayıcı, daireyi 300 × 150 tuval bit eşlemine çizer ve kenarı yumuşatır (panel 2). Tarayıcı, tuvali görüntülemek için bu bit eşlemi 540 × 270 cihaz pikseline yayar. Bu nedenle tarayıcının interpolasyon yapması gerekir: Her cihaz pikseli, kenar yumuşatma uygulanmış kenarı içeren en yakın tuval piksellerinin ağırlıklı bir karışımını alır. Kenar ikinci kez karıştırılır ve birkaç cihaz pikseline yayılır. Bu nedenle daire bulanık görünür (3. panel). Tuval ızgarası, cihaz pikselleriyle 1:1 eşleşirse bu durum önlenebilir. Tarayıcı daha sonra kenarı doğrudan cihaz ızgarasında yalnızca bir kez yumuşatır ve bit eşlemi yeniden örnekleme yapmadan gösterir. Böylece kenar net kalır (panel 4):

Kavisli bir kenar için piksel ızgaralarını karşılaştıran dört panelli diyagram: 1. Hizalanmamış CSS (360 x 180), tuval (300 x 150) ve cihaz (540 x 270) ızgaraları. 2. Eğri, 300'e 150 piksellik kaba tuval ızgarasında yumuşatılmıştır. 3. Bu bitmap, 540'a 270 cihaz ızgarasına enterpolasyon uygulayarak ikinci kez kenar yumuşatma işlemi yapar ve kenarı bulanıklaştırır. 4. Cihaz ızgarasıyla bire bir eşleşen 540x270 boyutunda bir tuval. Net bir kenar için bir kez kenar yumuşatma uygulanmıştır.
Hizalanmamış CSS, tuval ve cihaz piksel ızgaralarının, 1:1 tuval-cihaz ızgarasına (panel 4) kıyasla nasıl çift kenar yumuşatmaya (panel 1-3) neden olduğu.

Net bir görüntü için tuval ızgarası, tuvalin kapsadığı cihaz pikseli sayısıyla eşleşmelidir: Önceki örnekte 540 × 270.

&quot;DOM'dan merhaba!&quot; yazan bir HTML kartının yan yana karşılaştırması. Bu kart, tuval üzerine çizilmiş HTML'dir.' Sol: Varsayılan 300x150 tuval ızgarasında çizilmiş, bulanık görünen bir resim. Sağ: ResizeObserver ile boyutlandırılmış 800x400 tuval ızgarasına çizilmiş, net görünen bir daire.
DPR'si 1,5 olan bir ekranda aynı tuval (400 x 200 CSS pikseli). Sol: 400 x 200 tuval ızgarası. Sağ: DPR'ye göre ayarlanmış 600 x 300 boyutunda bir ızgara.

Yeniden boyutlandırma gözlemcisiyle tuval ızgarasını boyutlandırma

Doğru ızgara boyutu cihaza bağlıdır ve düzen değiştiğinde, kullanıcı sayfayı yakınlaştırdığında veya pencereyi farklı bir ekrana taşıdığında değişebilir. Bu nedenle, ızgara boyutunu sabit kodlamak yerine tarayıcının size ne olması gerektiğini söylemesine izin verin.

Tuval ızgarasını ekrandaki tuvalle senkronize tutmak için her zaman ResizeObserver ile boyutlandırın ve tuval ızgarasını devicePixelContentBoxSize olarak ayarlayın:

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);

CSS piksellerini tuval ızgarası koordinatlarına dönüştürme

paint işleyici genellikle DOM ölçümlerini çizim çağrılarıyla birleştirir. Örneğin, bir öğeyi boyutuna göre konumlandırmak için. İkisinde farklı birimler kullanılır:

  • Tuval pikselleri, canvas.width ve canvas.height boyutundaki tuval ızgarasını tanımlar. *(Örneğin, drawElementImage() çizim yöntemlerinin dx, dy, dwidth ve dheight parametreleri veya fillRect() ve translate() gibi diğer tuval API'leri parametreleri.)
  • CSS pikselleri sayfayı tanımlar. offsetWidth ve clientWidth gibi DOM ölçümleri ve offsetX gibi işaretçi etkinliği koordinatları CSS pikseli cinsindendir.

Çeşitli API parametreleri tarafından kullanılan birimlere genel bakışı burada bulabilirsiniz:

Değer Birimler
canvas.width ve canvas.height Kanvas ızgara pikselleri
drawElementImage() için dx, dy, dwidth ve dheight dahil olmak üzere 2D çizim parametreleri Kanvas ızgara pikselleri
canvas.getElementImageDefaultSize() tarafından iade edilen width ve height Kanvas ızgara pikselleri
Kaynak dikdörtgenler: sx, sy, swidth ve sheight/drawElementImage() ve texElementSubImage2D(); sourceX, sourceY, sourceWidth ve sourceHeight/drawElementImageToTexture() CSS pikseli
Doku boyutları: width ve height seçenekleri (texElementSubImage2D()); size (drawElementImageToTexture()) Doku pikselleri. Varsayılan olarak, öğenin tuval ızgarası pikseli cinsinden boyutu (yukarı yuvarlanır)
Öğe geometrisi: getElementTransform() ve updateElementGeometry() için canvasTransform ve canvasClip seçenekleri CSS pikseli
offsetWidth, clientWidth, getBoundingClientRect(), işaretçi etkinliği koordinatları ve yeniden boyutlandırma gözlemci girişinin contentBoxSize gibi DOM ölçümleri CSS pikseli
devicePixelContentBoxSize yeniden boyutlandırma gözlemcisi girişi Cihaz pikselleri

Yanlış birimler kullanırsanız tuvalde çizilen HTML içeriğinin boyutu beklentilerinizi karşılamayabilir. HTML'yi tuvale çizerken CSS piksel değerini tuval ızgara piksellerine dönüştürmeniz gerekir. Bunun için, tuval ızgara boyutu ile tuval CSS boyutu arasındaki oranı hesaplayın: Yatay olarak canvas.width / cssContentWidth ve dikey olarak canvas.height / cssContentHeight. Yeniden boyutlandırma gözlemciniz, oranı aşağıdaki gibi hesaplamak için uygun bir yerdir:

// The number of canvas grid pixels per CSS pixel
let scaleX = 1;
let scaleY = 1;

const observer = new ResizeObserver(([entry]) => {
  const dpc = entry.devicePixelContentBoxSize;
  const cssContentWidth = entry.contentRect.width;
  const cssContentHeight = entry.contentRect.height;

  // Best practice: make the canvas grid match the physical pixel grid
  if (dpc) {
    canvas.width = dpc[0].inlineSize;
    canvas.height = dpc[0].blockSize;
  } else {
    canvas.width = Math.round(cssContentWidth * window.devicePixelRatio);
    canvas.height = Math.round(cssContentHeight * window.devicePixelRatio);
  }

  scaleX = canvas.width / cssContentWidth;
  scaleY = canvas.height / cssContentHeight;
});

const supportsDevicePixelContentBox =
  typeof ResizeObserverEntry !== 'undefined' &&
  'devicePixelContentBoxSize' in ResizeObserverEntry.prototype;
const options = supportsDevicePixelContentBox ? { box: 'device-pixel-content-box' } : {};
observer.observe(canvas, options);

Ardından, CSS piksel değerlerini çizim yöntemine iletmeden önce scaleX ve scaleY ile çarpın. Örneğin, çizilebilir bir öğeyi kanvasta ortalamak için:

const ctx = canvas.getContext('2d');
const card = document.getElementById('card');

canvas.onpaint = () => {
  ctx.reset();

  // offsetWidth and offsetHeight are in CSS pixels.
  // Convert them to canvas grid coordinates.
  const width = card.offsetWidth * scaleX;
  const height = card.offsetHeight * scaleY;
  const x = (canvas.width - width) / 2;
  const y = (canvas.height - height) / 2;

  // drawElementImage() takes canvas grid coordinates.
  ctx.drawElementImage(card, x, y);
};

Son olarak, drawElementImage() öğenin kenarlık kutusunu çizdiğinden, kartın width ve height değerlerine dolgu ve kenarlık eklemek için box-sizing: border-box kullanın. Böylece kart, ayarladığınız boyutta çizilir:

#card {
  /* Make width and height include padding and border */
  box-sizing: border-box;
  width: 200px;
  padding: 16px;
}

Canvas'ta HTML'nin erişilebilirliği hakkında bilgi

Genellikle bir <canvas content="drawable"> öğesinin HTML içeriği, normal DOM ile aynı şekilde erişilebilirliğe maruz kalır. Yani aria-hidden="true" veya inert gibi özellikler kullanılarak açıkça gizlenmediği sürece erişilebilirlik ağacında temsil edilir. Ancak önemli bir istisna vardır: Oluşturulan geometrisi henüz oluşturulmamış veya güncellenmemiş bir drawable öğesi.

<canvas content="drawable"> içindeki bir drawable öğesinde, aşağıdaki durumlarda oluşturulmuş geometri yoktur:

  • Geometriyi varsayılan olarak otomatik şekilde güncelleyen drawElementImage() ile henüz oluşturulmamıştır.
  • Geometrisi updateElementGeometry() ile açıkça güncellenmedi.

Bir drawable öğesinde oluşturulmuş geometri yoksa bu drawable öğesi ve drawable alt ağacı, geometri bilgisi olmadan erişilebilirlik ağacına bildirilir. Bu, örneğin ekran okuyucuların semantik içeriğe erişmesine izin verirken görsel destek araçlarına (büyüteçler gibi) çizilmemiş öğeleri filtrelemesini bildirmek için kullanışlıdır.

Aşağıdaki örnekte, resim ve şekil ayrı ayrı çizilirken şekil, resim ve başlığın semantiği korunmaktadır:

<canvas id="canvas" width="400" height="300" content="drawable">
  <figure id="figure">
    <img drawable id="image" src="..." alt="image alt text" />
    <figcaption drawable id="caption">A caption</figcaption>
  </figure>
</canvas>
<script>
  const ctx = canvas.getContext('2d');

  canvas.onpaint = () => {
    ctx.reset();
    // Note: Because `preserveElementGeometry` defaults to false, the
    // `drawElementImage` call automatically updates geometry, ensuring
    // the image and caption have geometry information for accessibility.
    ctx.drawElementImage(image, 0, 0);
    ctx.drawElementImage(caption, 0, 200);
  };
</script>

Yedek stratejiler

HTML-in-Canvas API, Chrome 157 sürümünde kullanıma sunulmuştur ve tüm tarayıcılarda desteklenmeyebilir. Çözümünüzün herkes için sorunsuz çalıştığından emin olmak amacıyla tarayıcının API'yi destekleyip desteklemediğini kontrol edin ve gerekirse alternatif bir çözüme geçin:

export function supportsHtmlInCanvas() {
  if (typeof document === 'undefined') return false;
  const canvas = document.createElement('canvas');
  canvas.setAttribute('content', 'drawable');
  return canvas.content === 'drawable';
}

API desteklenmiyorsa uygulama hedeflerinize bağlı olarak aşağıdaki alternatiflerden birini seçebilirsiniz:

Sonraki adımlar