على مدار سنوات، كان على مطوّري الويب اتّخاذ خيار صعب بشأن التصميم عند إنشاء تطبيقات مرئية معقّدة وتفاعلية للغاية على الويب. هل يجب الاعتماد على نموذج المستند (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 في أداة الفحص، ومشاهدة التعديل على الفور في نسيج ثلاثي الأبعاد.
حالات الاستخدام العالية المستوى
تتيح واجهة برمجة التطبيقات هذه إمكانات هائلة في عدة مجالات:
- التطبيقات المستندة إلى لوحة العرض الكبيرة: يمكن الآن لتطبيقات الويب الكبيرة، مثل "مستندات 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 في لوحة العرض".
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 يكسر الضوء استنادًا إلى نموذج ثلاثي الأبعاد أساسي، مع الاستمرار في الاستجابة لسمات الخطوات
<input type="range">العادية في HTML. - النقوش المتحركة: لوحة إعلانات ثلاثية الأبعاد ديناميكية تعرض قلم رصاص SVG متحركًا باستخدام نموذج المستند (DOM) مباشرةً في زخرفة WebGL بدون الحاجة إلى حلقة رسوم متحركة مخصّصة.
- التراكبات الانكسارية: طبقة كتابة تفاعلية مشوّهة بواسطة مؤشر ثلاثي الأبعاد متحرك، ولكن يمكن تحديدها والبحث فيها بالكامل باستخدام ميزة "البحث في الصفحة".
يمكنك الاطّلاع على مجموعة العروض التوضيحية التي أنشأها المنتدى. إذا أردت عرض العرض التوضيحي لـ HTML-in-Canvas في هذه المجموعة، يمكنك إنشاء طلب سحب لإضافته.
القيود
على الرغم من فعالية واجهة برمجة التطبيقات، فإنّها تتضمّن بعض القيود المقصودة:
- المحتوى من مصادر متعددة: لأسباب تتعلّق بالأمان والخصوصية، لا تعمل واجهة برمجة التطبيقات مع محتوى iframe من مصادر متعددة.
- التمرير في سلسلة التعليمات الرئيسية: يتم رسم HTML-in-canvas باستخدام JavaScript، ما يعني أنّه لا يمكن تعديل التمرير والرسوم المتحركة بشكل مستقل عن JavaScript، كما يمكن إجراء ذلك خارج لوحة العرض. على المطوّرين التفكير مليًا في خصائص الأداء عند وضع محتوى قابل للتمرير داخل لوحة عرض مقارنةً بتمرير اللوحة بأكملها.
الملاحظات
إذا كنت تجرّب واجهة برمجة التطبيقات HTML-in-Canvas، يهمّنا معرفة رأيك. يمكنك الاشتراك في مرحلة التجربة والتقييم لتفعيل الميزة على موقعك الإلكتروني أثناء مرحلتها التجريبية لمساعدتنا في تحديد تصميم واجهة برمجة التطبيقات. يمكنك أيضًا تسجيل مشكلة لتقديم أي ملاحظات.
الموارد
- إتاحة استخدام HTML في Canvas في Three.js
- عرض توضيحي لـ HTML-in-Canvas في Three.js
- إتاحة HTML في Canvas في PlayCanvas: مستندات المطوّرين
- عرض توضيحي لـ "HTML في Canvas" في PlayCanvas
- HTML-in-Canvas: شرح
- إرشادات الويب الحديثة لأدوات ترميز الذكاء الاصطناعي في HTML-in-Canvas
- العروض التوضيحية على Chrome.dev بشأن ميزة "HTML في لوحة الرسم"
- مجموعة رائعة من العروض التوضيحية لـ HTML في Canvas من إعداد المنتدى