‫HTML في لوحة WebGL

يتيح لك عرض HTML في لوحة WebGL استخدام أشجار DOM الفرعية المباشرة كأنسجة GPU ديناميكية، وتطبيق تأثيرات التظليل على DOM المباشر، ودمج HTML التفاعلي في المشاهد الثلاثية الأبعاد.

يوضّح هذا الدليل كيفية عرض HTML في لوحة WebGL. مزيد من المعلومات حول HTML-in-Canvas في نظرة عامة على واجهة برمجة التطبيقات

يتطلّب عرض HTML في لوحة WebGL اتّباع الخطوات التالية:

  1. جهِّز لوحتك. يمكنك ضبط السمات المناسبة بشكل تعريفي لإعلام المتصفّح بأنّه يجب عرض HTML في لوحة رسم. هذا الإعداد متطابق في سياقات 2D وWebGL وWebGPU. تعرَّف على كيفية إعداد لوحة الرسم وكيفية تحديد عناصر HTML ليتم رسمها بشكل مستقل.
  2. طبِّق رمز HTML كبنية:
    1. تخصيص مساحة تخزين مسبقًا لنسيج وحدة معالجة الرسومات
    2. تحميل لقطة DOM كزخرفة
  3. مزامنة نموذج DOM ووحدات البكسل

رسم عنصر HTML كزخرفة WebGL

في WebGL، يتم تحميل عناصر HTML إلى وحدة معالجة الرسومات كزخارف ثنائية الأبعاد باستخدام texElementSubImage2D().

تخصيص مساحة تخزين مسبقًا للزخارف

على عكس لوحة الرسم الثنائية الأبعاد التي تدير الذاكرة الاحتياطية تلقائيًا، يتطلّب سياق WebGL إدارة صريحة للذاكرة.

قبل تحميل وحدات بكسل DOM، عليك تخصيص ذاكرة وحدة معالجة الرسومات مسبقًا. يتيح ذلك للمتصفّح إجراء تعديلات سريعة على الصور الفرعية بدون الحاجة إلى إعادة تخصيص ذاكرة وحدة معالجة الرسومات (GPU) المكلفة.

يجب تخصيص ذاكرة التخزين المؤقت مسبقًا باستخدام مَعلمات الحجم المحدّدة وضبط srcData على null على النحو التالي:

// Only allocate texture backing once
if (!textureBufferAllocated) {
  // Pre-allocate texture backing with pre-determined size.
  gl.texImage2D(
    /* target */ gl.TEXTURE_2D,
    /* level */ 0,
    /* internalformat */ gl.RGBA8,
    /* width */ myElementWidth,
    /* height */ myElementHeight,
    /* border */ 0,
    /* format */ gl.RGBA,
    /* type */ gl.UNSIGNED_BYTE,
    /* srcData */ null
  );

  textureBufferAllocated = true;
}

لكي يتطابق النسيج مع حجم التنسيق الطبيعي لعنصر HTML، استخدِم canvas.getElementImageDefaultSize() لاسترداد حجمه التلقائي بوحدات بكسل شبكة لوحة الرسم:

const { width, height } = canvas.getElementImageDefaultSize(myElement);
// Pre-allocate texture backing. Only do this once, or whenever the element
// changes size.
gl.texImage2D(
  /* target */ gl.TEXTURE_2D,
  /* level */ 0,
  /* internalformat */ gl.RGBA8,
  /* width */ Math.ceil(width),
  /* height */ Math.ceil(height),
  /* border */ 0,
  /* format */ gl.RGBA,
  /* type */ gl.UNSIGNED_BYTE,
  /* srcData */ null
);

تحميل لقطة DOM كبنية

الخطوة التالية هي تحميل عنصر HTML إلى المادة داخل حلقة العرض أو مستمع paint.

canvas.onpaint = () => {
  // 1. Pre-allocate texture backing first
  // ...

  // 2. Upload HTML snapshot into the WebGL texture
  gl.texElementSubImage2D(
    /* target  */ gl.TEXTURE_2D,
    /* level   */ 0,
    /* xoffset */ 0,
    /* yoffset */ 0,
    /* element */ myElement // or an ElementImage from `captureElementImage()`
  );

  // ...
};

مزامنة الهندسة

على عكس السياقات الثنائية الأبعاد، لا يمكن للمتصفّح أن يستنتج تلقائيًا مكان ظهور الزخرفة على الشاشة في سياق ثلاثي الأبعاد. عليك محاذاة اختبارات مؤشر الماوس في نموذج العناصر في المستند (DOM) ومربّعات الإحاطة الخاصة بأدوات تسهيل الاستخدام مع الشبكة الثلاثية الأبعاد يدويًا.

لإجراء ذلك، أنشئ DOMMatrix يربط مربّع حدود العنصر بموقع بكسل اللوحة المعروضة، ثم طبِّقه باستخدام updateElementGeometry():

canvas.addEventListener("paint", () => {
  // 1. Upload fresh DOM snapshot to GPU texture
  gl.bindTexture(gl.TEXTURE_2D, myTexture);
  gl.texElementSubImage2D(gl.TEXTURE_2D, 0, 0, 0, myElement);

  // 2. Draw your 3D scene
  draw3DScene();

  // 3. Construct the CSS-pixel transform mapping the element's
  // border box to its drawn position on the canvas as needed
  // (for example: scale and translate)
  const canvasTransform = new DOMMatrix();
  // scale X
  canvasTransform.a = canvas.clientWidth / myElement.offsetWidth;
  // scale Y
  canvasTransform.d = canvas.clientHeight / myElement.offsetHeight;
  // translate X (CSS px)
  canvasTransform.e = 50;
  // translate Y (CSS px)
  canvasTransform.f = 100;

  // 4. Update DOM position, accessibility focus rings, and pointer hit-testing
  canvas.updateElementGeometry(myElement, {
    canvasTransform: canvasTransform,
  });
});

الخطوات التالية