على مدار سنوات، كان على مطوّري الويب اتّخاذ خيار صعب بشأن التصميم عند إنشاء تطبيقات مرئية معقّدة وتفاعلية للغاية على الويب. هل يجب الاعتماد على نموذج المستند (DOM) لما يوفّره من ميزات دلالية غنية، أو العرض مباشرةً في العنصر <canvas> للحصول على أداء رسومات منخفض المستوى؟
باستخدام HTML-in-Canvas API التجريبية الجديدة، والتي تتوفّر الآن في مرحلة التجربة والتقييم، لن تحتاج إلى الاختيار. تتيح لك واجهة برمجة التطبيقات هذه رسم محتوى DOM مباشرةً في لوحة عرض ثنائية الأبعاد أو نسيج WebGL/WebGPU مع إبقاء واجهة المستخدم تفاعلية ويمكن الوصول إليها ومتوافقة مع ميزات المتصفّح المفضّلة لديك. من خلال الجمع بين HTML ومعالجة الرسومات المنخفضة المستوى، يمكنك إنشاء تجارب لم تكن ممكنة في السابق.
الفرق بين DOM وCanvas
لفهم قوة واجهة برمجة التطبيقات الجديدة هذه، من المفيد إلقاء نظرة على نقاط القوة النسبية لكل من DOM وCanvas.
نموذج المستند هو العنصر الأساسي في واجهة مستخدم الويب. تقدّم هذه الأداة حلولاً جاهزة لتخطيط النصوص، وذلك باستخدام محتوى مفهوم دلاليًا لإنشاء واجهات تفاعلية متقدّمة. يتيح ذلك للمستخدمين تنفيذ عمليات شائعة على صفحات الويب بسلاسة، مثل تمييز نص لنسخه أو النقر بزر الماوس الأيمن على صورة لحفظها. تتكامل عناصر DOM أيضًا مع ميزات المتصفّح الأساسية، مثل أدوات تسهيل الاستخدام والترجمة والبحث في الصفحة ووضع القراءة والإضافات والوضع الداكن والتكبير والتصغير في المتصفّح والتعبئة التلقائية.
من ناحية أخرى، تتيح لوحة العرض (وWebGL/WebGPU) الوصول إلى مستوى منخفض لتشغيل شبكة من وحدات البكسل من أجل عرض رسومات ثنائية وثلاثية الأبعاد متطورة للغاية. تتطلّب الألعاب وتطبيقات الويب المعقّدة (مثل "مستندات Google" أو Figma) إمكانية الوصول إلى هذا المستوى المنخفض من الأداء العالي. بما أنّ لوحة العرض هي في الأساس شبكة من وحدات البكسل، كان دعم ميزات مثل النص سريع الاستجابة يتطلّب منطقًا معقّدًا مخصّصًا لواجهة المستخدم، ما يؤدي إلى زيادة حجم الحِزمة بشكل كبير. والأهم من ذلك، أنّ جميع ميزات المتصفّح الفعّالة المدمجة في نموذج المستند (DOM) تتوقّف تمامًا عندما تكون واجهة المستخدم محصورة داخل شبكة وحدات بكسل ثابتة على لوحة الرسم.
مزايا نقل DOM إلى Canvas
إنّ واجهة برمجة التطبيقات HTML-in-Canvas هي الجسر الذي يتيح لك الاستفادة من أفضل ما في كلا الخيارين. من خلال وضع HTML داخل العنصر <canvas> ومزامنة عملية التحويل، يمكنك التأكّد من أنّ المحتوى يظل تفاعليًا بالكامل وأنّ جميع عمليات الدمج في المتصفّح تعمل تلقائيًا.
إليك المزايا التي ستحصل عليها من خلال السماح لنموذج المستند (DOM) بالتعامل مع واجهة المستخدم داخل العنصر <canvas>:
- تنسيق النص وتخطيطه: تنسيق النص وتخطيطه بشكل مبسط، بما في ذلك النص المتعدد الأسطر أو الثنائي الاتجاه مع تطبيق أنماط CSS
- عناصر التحكّم في النماذج: عناصر تحكّم في النماذج معبّرة وأسهل في الاستخدام مع خيارات تخصيص واسعة النطاق
- اختيار النص ونسخه ولصقه والنقر بزر الماوس الأيمن: يمكن للمستخدمين تمييز النص داخل المشاهد الثلاثية الأبعاد أو النقر بزر الماوس الأيمن على قوائم السياق بشكلٍ أصلي.
- سهولة الاستخدام: يتم عرض المحتوى الذي يتم عرضه داخل لوحة العرض في شجرة سهولة الاستخدام. يمكن لأنظمة تسهيل الاستخدام تحليل واجهة المستخدم كما تفعل مع HTML العادي، وعرضها على أنظمة مثل برامج قراءة الشاشة.
- البحث في الصفحة: يمكن للمستخدمين استخدام ميزة "البحث في الصفحة" (Ctrl/Cmd+F) للبحث عن نص، وسيُبرزه المتصفّح مباشرةً في مواد WebGL.
- إمكانية الفهرسة والتوافق مع وكلاء الذكاء الاصطناعي: يمكن لبرامج الزحف على الويب ووكلاء الذكاء الاصطناعي فهرسة النصوص المعروضة في مشاهدك الثنائية والثلاثية الأبعاد وقراءتها بسلاسة.
- تكامل الإضافات: تعمل إضافات المتصفّح بشكلٍ أصلي. على سبيل المثال، تعمل إضافة استبدال النص تلقائيًا على تعديل النص المعروض على شبكاتك الثلاثية الأبعاد.
- دمج "أدوات مطوّري البرامج": يمكنك فحص محتوى لوحة العرض، بما في ذلك عناصر واجهة مستخدم WebGL/WebGPU مباشرةً في "أدوات مطوّري البرامج في Chrome". عدِّل نمط CSS في "أداة الفحص"، وشاهِد التعديل يظهر على الفور في النسيج الثلاثي الأبعاد.
حالات الاستخدام العالية المستوى
تتيح واجهة برمجة التطبيقات هذه إمكانات رائعة في عدة مجالات:
- التطبيقات المستندة إلى لوحة العرض الكبيرة: يمكن الآن لتطبيقات الويب الثقيلة عرض عناصر واجهة مستخدم التطبيق المعقّدة بشكل أصلي في مساحات العمل المستندة إلى لوحة العرض، ما يحسّن إمكانية الوصول ويقلّل من حجم الحِزمة.
- المشاهد والألعاب الثلاثية الأبعاد: يمكن للمواقع الإلكترونية التسويقية وتجارب 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 في لوحة العرض".
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 في لوحة العرض باستخدام واجهة برمجة التطبيقات الخاصة بها للصور:
// 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، يهمّنا معرفة رأيك. يمكنك الاشتراك في مرحلة التجربة والتقييم لتفعيل الميزة على موقعك الإلكتروني أثناء مرحلتها التجريبية لمساعدتنا في تحديد تصميم واجهة برمجة التطبيقات. يمكنك أيضًا تسجيل مشكلة لتقديم أي ملاحظات.
الموارد
- إتاحة HTML في Canvas في Three.js
- عرض توضيحي لـ HTML-in-Canvas في Three.js
- إتاحة HTML في Canvas في PlayCanvas: مستندات للمطوّرين
- عرض توضيحي لـ "HTML في Canvas" في PlayCanvas
- HTML-in-Canvas: شرح
- إرشادات الويب الحديثة لأدوات الترميز المستندة إلى الذكاء الاصطناعي لإنشاء HTML في Canvas
- العروض التوضيحية على Chrome.dev بشأن HTML-in-Canvas
- مجموعة رائعة من العروض التوضيحية لـ HTML في Canvas من إعداد المنتدى