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

Thomas Nattestad
Thomas Nattestad

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

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

الفرق بين DOM وCanvas

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

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

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

مزايا نقل DOM إلى Canvas

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

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

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

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

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

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

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

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

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

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

الخطوة 1: الإعداد الأساسي لـ Canvas

أولاً، أضِف السمة 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 transform

بعد عرض العنصر في لوحة العرض، عليك تعديل المتصفّح بشأن موقعه. يضمن ذلك المزامنة المكانية بين لوحة العرض وتصميم نموذج المستند (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(). لتسهيل ذلك، عليك اتّباع الخطوات التالية:

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

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

القيود

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

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

الملاحظات

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

الموارد