إطلاق مرحلة التجربة والتقييم لواجهة برمجة التطبيقات HTML-in-Canvas

Thomas Nattestad
Thomas Nattestad

لسنوات، كان على مطوّري الويب اتّخاذ خيار معماري صعب عند إنشاء تطبيقات مرئية معقّدة وتفاعلية للغاية على الويب: هل تعتمد على نموذج العناصر في المستند (DOM) لما يقدّمه من ميزات دلالية غنية، أم تعرض المحتوى مباشرةً على عنصر <canvas> للحصول على أداء رسومات منخفض المستوى؟

باستخدام HTML-in-Canvas API التجريبية الجديدة، المتوفّرة الآن في مرحلة التجربة والتقييم، لن تضطر إلى الاختيار. تتيح لك واجهة برمجة التطبيقات هذه رسم محتوى نموذج العناصر في المستند (DOM) مباشرةً في لوحة رسم ثنائية الأبعاد أو في نسيج WebGL/WebGPU مع الحفاظ على تفاعل واجهة المستخدم وإمكانية الوصول إليها وربطها بميزات المتصفّح المفضّلة لديك. من خلال الجمع بين HTML ومعالجة الرسومات منخفضة المستوى، يمكنك إنشاء تجارب لم يكن من الممكن إنشاؤها سابقًا.

مقارنة بين نموذج العناصر في المستند (DOM) ولوحة الرسم

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

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

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

مزايا نقل نموذج العناصر في المستند (DOM) إلى لوحة الرسم

‫HTML-in-Canvas API هي الجسر الذي يمنحك أفضل ما في كلا الخيارَين. من خلال وضع HTML داخل العنصر <canvas> ومزامنة تحويله، يمكنك التأكّد من أنّ المحتوى يظل تفاعليًا بالكامل وأنّ جميع عمليات التكامل مع المتصفّح تعمل تلقائيًا.

إليك الميزات التي تحصل عليها من خلال السماح لنموذج العناصر في المستند (DOM) بمعالجة واجهة المستخدم داخل عنصر <canvas>:

  • تنسيق النص وتنسيقه: يمكنك تنسيق النص وتنسيقه بسهولة، بما في ذلك النص المتعدد الأسطر أو النص ثنائي الاتجاه مع تطبيق أنماط CSS.
  • عناصر التحكّم في النماذج: يمكنك استخدام عناصر تحكّم في النماذج معبرة وأسهل في الاستخدام مع خيارات تخصيص واسعة النطاق.
  • تحديد النص والنسخ/اللصق والنقر بزر الماوس الأيمن: يمكن للمستخدمين تمييز النص داخل مشاهدك الثلاثية الأبعاد أو النقر بزر الماوس الأيمن على قوائم السياق بشكلٍ أصلي.
  • تحديد النص والنسخ/اللصق والنقر بزر الماوس الأيمن: يمكن للمستخدمين تمييز النص داخل مشاهدك الثلاثية الأبعاد أو النقر بزر الماوس الأيمن على قوائم السياق بشكلٍ أصلي.
  • تسهيل الاستخدام: يتم عرض المحتوى المعروض داخل لوحة الرسم في شجرة تسهيل الاستخدام. يمكن لأنظمة تسهيل الاستخدام تحليل واجهة المستخدم كما تفعل مع HTML العادي، وعرضها على أنظمة مثل برامج قراءة الشاشة.
  • Find-in-page: يمكن للمستخدمين استخدام ميزة "البحث في الصفحة" (Ctrl/Cmd+F) للبحث عن نص، وسيُبرزه المتصفّح مباشرةً داخل نسيج WebGL.
  • Find-in-page: يمكن للمستخدمين استخدام ميزة "البحث في الصفحة" (Ctrl/Cmd+F) للبحث عن نص، وسيُبرزه المتصفّح مباشرةً داخل نسيج WebGL.
  • إمكانية الفهرسة وإمكانية التفاعل مع وكلاء الذكاء الاصطناعي: يمكن لبرامج الزحف على الويب ووكلاء الذكاء الاصطناعي فهرسة النص المعروض في مشاهدك الثنائية والثلاثية الأبعاد وقراءته بسلاسة.
  • التكامل مع الإضافات: تعمل إضافات المتصفّح بشكلٍ أصلي. على سبيل المثال، ستعدّل إضافة استبدال النص تلقائيًا النص المعروض على شبكاتك الثلاثية الأبعاد.
  • التكامل مع "أدوات مطوّري البرامج": يمكنك فحص محتوى لوحة الرسم، بما في ذلك عناصر واجهة مستخدم WebGL/WebGPU مباشرةً في "أدوات مطوّري البرامج في Chrome". يمكنك تعديل نمط CSS في أداة الفحص، وسيتم تعديله على الفور على النسيج الثلاثي الأبعاد.

حالات الاستخدام عالية المستوى

تفتح واجهة برمجة التطبيقات هذه إمكانات مذهلة في عدة مجالات:

  • التطبيقات الكبيرة المستندة إلى لوحة الرسم: يمكن لتطبيقات الويب الكبيرة، مثل "مستندات Google" أو Miro أو Figma، عرض مكوّنات واجهة مستخدم التطبيق المعقّدة بشكلٍ أصلي في مساحات العمل المستندة إلى لوحة الرسم، ما يحسّن إمكانية الوصول ويقلّل من حجم الحزمة.
  • المشاهد والألعاب الثلاثية الأبعاد: يمكن للمواقع الإلكترونية التسويقية وتجارب WebXR الغامرة وألعاب الويب الآن وضع واجهة مستخدم ويب تفاعلية بالكامل في مشاهد ثلاثية الأبعاد، مثل كتاب ثلاثي الأبعاد يستخدم نص نموذج العناصر في المستند (DOM) الحقيقي، أو محطة داخل اللعبة تتيح بشكلٍ أصلي النسخ واللصق.

كيفية استخدام واجهة برمجة التطبيقات

يتم استخدام واجهة برمجة التطبيقات على ثلاث مراحل: إعداد لوحة الرسم وعرض المحتوى في لوحة الرسم وتعديل تحويل CSS لكي يعرف المتصفّح مكان العنصر فعليًا على الشاشة.

المتطلبات الأساسية

تتوفّر HTML-in-Canvas API في مرحلة التجربة والتقييم في Chrome من الإصدار 148 إلى 150. لاختبارها على موقعك الإلكتروني، استخدِم Chrome Canary 149 أو إصدارًا أحدث مع تفعيل العلامة chrome://flags/#canvas-draw-element. لتفعيل واجهة برمجة التطبيقات لمستخدمين آخرين، عليك التسجيل في مرحلة التجربة والتقييم.

الخطوة 1: إعداد لوحة الرسم الأساسية

أولاً، أضِف السمة layoutsubtree إلى علامة <canvas>. يُعلم ذلك المتصفّح بالمحتوى المضمّن داخل لوحة الرسم، ما يجهّزه لعرضه داخل لوحة الرسم وعرضه في أشجار تسهيل الاستخدام.

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

تحديد حجم شبكة لوحة الرسم

لتجنُّب تشوّه المحتوى المعروض، احرص على تحديد حجم شبكة لوحة الرسم بما يتناسب مع عامل مقياس الجهاز.

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: العرض

بالنسبة إلى السياق الثنائي الأبعاد، استخدِم طريقة drawElementImage. يمكنك إجراء ذلك داخل حدث paint، الذي يتم تنشيطه كلما تمت إعادة رسم العنصر، مثلاً أثناء تمييز النص أو بيانات أدخلها المستخدم. من المهم تعديل تحويل CSS للعنصر باستخدام القيمة المعروضة لكي تظل ميزة التفاعل تعمل.

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

بالنسبة إلى WebGL، استخدِم texElementImage2D. تعمل هذه الطريقة بشكلٍ مشابه لطريقة texImage2D، ولكنّها تأخذ عنصر نموذج العناصر في المستند (DOM) كمصدر.

canvas.onpaint = () => {
  if (gl.texElementImage2D) {
    gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, form_element);
  }
};

العرض باستخدام WebGPU

تستخدِم WebGPU طريقة copyElementImageToTexture في قائمة انتظار الجهاز، وهي مشابهة لطريقة copyExternalImageToTexture:

canvas.onpaint = () => {
  root.device.queue.copyElementImageToTexture(
    valueElement,
    { texture: targetTexture }
  );
};

الخطوة 3: تعديل تحويل CSS

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

بالنسبة إلى سياق العرض الثنائي الأبعاد، طبِّق التحويل الذي تعرضه عملية العرض على .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();
};

باستخدام WebGL أو WebGPU، يعتمد موقع العنصر على الشاشة على كيفية استخدام رمز التظليل لنسيج الإخراج، ولا يمكن استنتاجه من سياق عرض لوحة الرسم. ومع ذلك، إذا كان برنامج التظليل يستخدم عرض نموذج الإسقاط النموذجي لرسم النسيج، يمكنك استخدام دالة الملاءمة الجديدة element.getElementTransform() لحساب تحويل يمكن استخدامه بالطريقة نفسها التي يتم بها استخدام القيمة المعروضة من drawElementImage(). لتسهيل ذلك، عليك تنفيذ ما يلي:

  • تحويل مصفوفة WebGL MVP إلى مصفوفة نموذج العناصر في المستند (DOM).
  • تطبيع عنصر HTML: يتم تحديد حجم عناصر HTML بالبكسل (على سبيل المثال، بعرض 200 بكسل). ومع ذلك، تتعامل WebGL عادةً مع الكائنات على أنّها "مربعات وحدة"، مثلاً تتراوح من 0 إلى 1. إذا لم يتم التطبيع، سيظهر الزر الذي يبلغ عرضه 200 بكسل أكبر بـ 200 مرة.
  • الربط بإطار عرض لوحة الرسم: هذه الخطوة هي مرحلة "إعادة التحجيم": يتم فيها توسيع العمليات الحسابية في مساحة الوحدة لتتطابق مع الأبعاد الفعلية بالبكسل لعنصر <canvas> على الشاشة. يتم أيضًا قلب المحور Y، لأنّ الاتجاه للأعلى هو قيمة موجبة في WebGL، ولكن الاتجاه للأسفل هو قيمة موجبة في CSS.
  • حساب التحويل النهائي: اضرب المصفوفات بالترتيب: Viewport * MVP * Normalization. يؤدي دمجها في تحويل نهائي واحد إلى إنشاء "خريطة" تُعلم المتصفّح بالمكان الذي يجب أن تكون فيه طبقة عنصر HTML هذه بالتحديد لتتطابق مع الرسم الثلاثي الأبعاد.
  • تطبيق التحويل على عنصر HTML: ينقل ذلك طبقة عنصر HTML لتكون أعلى وحدات البكسل المعروضة مباشرةً. يضمن ذلك أنّه عندما ينقر المستخدم على زر أو يختار نصًا، فإنّه ينقر على عنصر HTML الحقيقي.
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();
  }
}

دعم المكتبات وأطر العمل

أضافت بعض المكتبات الشائعة الدعم لميزة HTML-in-Canvas.

Three.js

يمكن أن يكون تعديل المصفوفات يدويًا أمرًا شاقًا، ولهذا السبب بدأت أطر العمل في استخدام هذه الميزة. تتوفّر في Three.js ميزة الدعم التجريبي باستخدام THREE.HTMLTexture الجديدة:

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 أيضًا ميزة HTML-in-Canvas باستخدام واجهة برمجة تطبيقات النسيج:

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

العروض التوضيحية

قبل تجربة العروض التوضيحية، تأكّد من ضبط بيئتك بشكلٍ صحيح.

تتوفّر عدة عروض توضيحية تُستخدم كمرجع لاستخدام واجهة برمجة التطبيقات. نلاحظ حاليًا حلولاً إبداعية من المنتدى، تتراوح بين كتب ثلاثية الأبعاد قابلة للترجمة وعناصر واجهة مستخدم تنكسر من خلال مظلّلات الزجاج:

  • الكتاب الثلاثي الأبعاد: كتاب ثلاثي الأبعاد معروض باستخدام WebGL ويستخدم تنسيق HTML لصفحاته. يمكن للمستخدمين تبديل الخطوط باستخدام CSS. بما أنّه يستند إلى نموذج العناصر في المستند (DOM)، تعمل الترجمة المضمّنة على الفور، ويمكن لوكلاء الذكاء الاصطناعي استخراج النص بسهولة أكبر.
  • واجهات مستخدم ثلاثية الأبعاد تفاعلية: شريط تمرير هلامي في WebGPU ينكسر الضوء استنادًا إلى نموذج ثلاثي الأبعاد أساسي، مع الاستمرار في الاستجابة لسمات الخطوة <input type="range"> العادية.
  • النسيج المتحرّك: لوحة إعلانات ثلاثية الأبعاد ديناميكية تعرض قلم رصاص SVG متحركًا باستخدام نموذج العناصر في المستند (DOM) مباشرةً في نسيج WebGL بدون الحاجة إلى حلقة رسوم متحركة مخصّصة.
  • التراكبات الانكسارية: طبقة طباعة تفاعلية مشوّهة بواسطة مؤشر ثلاثي الأبعاد متحرك، ولكن يمكن اختيارها والبحث فيها بالكامل باستخدام ميزة "البحث في الصفحة".

يمكنك الاطّلاع على مجموعة العروض التوضيحية التي أنشأها المنتدى. إذا أردت عرض العرض التوضيحي HTML-in-Canvas في هذه المجموعة، أنشئ طلب سحب لإضافته.

القيود

على الرغم من قوة واجهة برمجة التطبيقات، فإنّها تفرض بعض القيود المدروسة:

  • المحتوى من مصادر متعددة: لأسباب تتعلق بـ الأمان والخصوصية، لا تعمل واجهة برمجة التطبيقات مع محتوى iframe من مصادر متعددة.
  • التمرير في سلسلة التعليمات الرئيسية: يتم رسم HTML-in-Canvas باستخدام JavaScript، ما يعني أنّه لا يمكن تحديث التمرير والرسوم المتحركة بشكلٍ مستقل عن JavaScript، كما هو الحال خارج لوحة الرسم. على المطوّرين التفكير مليًا في خصائص الأداء لوضع محتوى التمرير داخل لوحة الرسم بدلاً من تمرير لوحة الرسم بالكامل.

الملاحظات

إذا كنت تجرّب HTML-in-Canvas API، يهمنا أن نعرف رأيك. يمكنك الاشتراك في مرحلة التجربة والتقييم لتفعيل الميزة على موقعك الإلكتروني أثناء المرحلة التجريبية لمساعدتنا في تحديد تصميم واجهة برمجة التطبيقات. يمكنك أيضًا تسجيل مشكلة لتقديم أي ملاحظات.

الموارد