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