اگر تا امروز برای ساخت یک ویرایشگر گرافیکی یا بازی تحت وب مجبور بودید بین دسترسپذیریِ DOM و عملکردِ Canvas یکی را انتخاب کنید، آن دوچرخهی سخت اکنون شکسته شده است. کروم ۱۴۸ (Chrome 148) آزمایشِ اصلی (Origin Trial) APIِ HTML-in-Canvas را راهاندازی کرد — المانهای HTML اکنون میتوانند مستقیماً در بومهای ۲D، WebGL و WebGPU ترسیم شوند، در حالی که انتخاب متن، جستجوی در صفحه، و قارئههای صفحه بهصورت پیشفرض کار میکنند.
سالها توسعهدهندگان وب در ساخت اپلیکیشنهای بصری پیچیده با یک تصمیم معماریِ دو قطبی روبرو بودند. DOM — مثل یک کتابخانهی سازمانیافته که هر کتاب (المان) برچسب، جایگاه و معنا دارد — دسترسپذیری، انتخاب متن، ترجمه، و پشتیبانی افزونهها را بهصورت پیشفرض میدهد. Canvas و WebGL — مثل بومهای نقاشی که پیکسلبهپیکسل کنترل میشوند — عملکرد گرافیکی بالا میدهند اما UI را در یک شبکه پیکسلیِ استاتیک حبس میکنند که ویژگیهای مرورگر در آن کار نمیکنند. اپلیکیشنهایی مثل Google Docs و Figma سالها با موتورهای رندر سفارشی این محدودیت را دور میزدند، که حجم باندل و پیچیدگی مهندسی را بالا میبرد. به گزارش تیم کروم، این شکاف همواره مانعِ ادغامِ محتوای معنایی در صحنههای گرافیکی پیشرفته بوده است. همانطور که در تحلیل قبلی ما دربارهی معماری رندر در اپلیکیشنهای وب پیچیده اشاره کردیم، جدا بودنِ دو جهان رندر هزینهی سنگینی برای تیمهای محصول داشته است.
آزمایش اصلی (Origin Trial) در کروم ۱۴۸ تا ۱۵۰ فعال است. توسعهدهندگان میتوانند از امروز آن را با Chrome Canary ۱۴۹ یا جدیدتر و پرچم chrome://flags/#canvas-draw-element تست کنند. برای فعالسازی در محیط تولید، سایتها باید در کنسول توسعهدهنده کروم برای آزمایش اصلی ثبتنام کنند. دوره آزمایش به تیم کروم بازخورد واقعی میدهد تا قبل از احتمالی استانداردسازی ارزیابی شود.
مفهوم اصلی: ویژگی layoutsubtree
مرکز این API یک ویژگی جدید به نام layoutsubtree است که به المان <canvas> اضافه میشود. وقتی این ویژگی حضور دارد، مرورگر فرزندان DOM تو در تو را بهعنوان محتوای مقصد برای رندر در Canvas در نظر میگیرد، در حالی که آنها را در درخت دسترسپذیری (Accessibility Tree) و سیستم رویداد نگه میدارد. یک راهاندازی پایه المانهای فرم، متن، یا هر HTML را در داخل تگ canvas قرار میدهد:
<canvas id="canvas" style="width: 200px; height: 200px;" layoutsubtree>
<div id="form_element">
<label for="name">نام:</label>
<input id="name" type="text">
</div>
</canvas>
این نشانهگذاری به مرورگر میگوید: این زیردرخت را در Canvas ترسیم کن، اما برای تعامل کاربر و تکنولوژیهای کمکی زنده نگه دار.
اندازهگیری صحیح شبکه بوم
برای جلوگیری از خروجی تار، مخزن پشتیبان بوم (Backing Store) باید با نسبت پیکسل دستگاه (Device Pixel Ratio) همخوانی داشته باشد. الگوهای پیشنهادی از ResizeObserver با گزینه device-pixel-content-box زمانی که در دسترس است استفاده میکنند:
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);
این کد رندر تیز روی نمایشگرهای با تراکم بالا (High-DPI) را بدون محاسبههای دستی مقیاس تضمین میکند.
رندر با زمینه ۲D (2D Context)
برای CanvasRenderingContext2D، متد جدید drawElementImage(element, x, y) یک المان DOM را در مختصات مشخص ترسیم میکند. فراخوانی یک DOMMatrix بازمیگرداند که تبدیل اعمالشده را نشان میدهد. بهصورت حیاتی، این تبدیل باید به style.transform المان بازگردانده شود تا مرورگر بداند لایه تعاملی کجاست:
const ctx = canvas.getContext('2d');
const formElement = document.getElementById('form_element');
canvas.onpaint = () => {
ctx.reset();
let transform = ctx.drawElementImage(formElement, 0, 0);
formElement.style.transform = transform.toString();
};
رویداد onpaint هر وقت المان نیاز به بازترسیم دارد — در حین تایپ متن، تغییر انتخاب، یا بهروزرسانی استایل — فیر میشود، بافت بوم را با حالت DOM همگام نگه میدارد.
رندر با WebGL
زمینههای WebGL متد texElementImage2D را دریافت میکنند، جایگزینِ drop-in برای texImage2D که یک المان DOM بهعنوان منبع میپذیرد:
canvas.onpaint = () => {
if (gl.texElementImage2D) {
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, formElement);
}
};
بافت حاصل میتواند در شیدرها (Shaders) مانند هر بافت WebGL دیگری نمونهبرداری شود، محتوای DOM را روی مشهای سهبعدی، ذرات، یا افکتهای پسپردازش فعال میکند.
رندر با WebGPU
WebGPU از queue.copyElementImageToTexture() در صف دستگاه استفاده میکند، مشابه copyExternalImageToTexture:
canvas.onpaint = () => {
root.device.queue.copyElementImageToTexture(
valueElement,
{ texture: targetTexture }
);
};
این مستقیماً در خط لوله رندر WebGPU ادغام میشود، بافتهای هدایتشده با DOM را در شیدرهای محاسبه یا گذرهای رندر امکانپذیر میسازد.
همگامسازی تبدیلات برای WebGL و WebGPU
برخلاف زمینه ۲D، WebGL و WebGPU نمیتوانند موقعیت صفحه را فقط از فراخوانی رندر استنتاج کنند. ماتریس مدل-نمای-پروژه (MVP) شیدر تعیین میکند بافت کجا ظاهر میشود. توسعهدهندگان باید یک تبدیل CSS محاسبه کنند که المان DOM را به مکان رندر شده نگاشت دهد. API متد canvas.getElementTransform(element, screenSpaceTransform) را برای کمک فراهم میکند:
۱. ماتریس MVPِ WebGL را به DOMMatrix تبدیل کنید.
۲. المان HTML را از ابعاد پیکسلی به یک مربع واحد (۰–۱) نرمال کنید، محور Y را معکوس کنید چون Y-up در WebGL با Y-down در CSS متفاوت است.
۳. مربع واحد را به ابعاد viewport بوم نگاشت دهید.
۴. ماتریسها را به ترتیب ضرب کنید: Viewport * MVP * Normalization.
۵. نتیجه را به getElementTransform بدهید و تبدیل بازگشتی را به style.transform المان اعمال کنید.
این محاسبه چندمرحلهای تضمین میکند که کلیک روی یک دکمه سهبعدی به المان HTML واقعی برخورد کند، تمرکز، انتخاب، و دسترسپذیری را حفظ میکند.
ادغام Three.js: HTMLTexture
Three.js پشتیبانی آزمایشی را از طریق THREE.HTMLTexture عرضه کرده، ریاضیات تبدیل را انتزاع میکند:
const material = new THREE.MeshBasicMaterial();
material.map = new THREE.HTMLTexture(uiElement);
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
بافت رویدادهای onpaint و همگامسازی تبدیل را داخلی مدیریت میکند، توسعهدهندگان Three.js را قادر میسازد لایههای تعاملی DOM را با کد پیشفرض کم به صحنههای سهبعدی اضافه کنند.
ادغام PlayCanvas
PlayCanvas از طریق سیستم بافت خود از API پشتیبانی میکند:
canvas.addEventListener('paint', () => {
htmlTexture.setSource(htmlElement);
}, { once: true });
canvas.requestPaint();
canvas.addEventListener('paint', onPaintUpload);
const material = new pc.StandardMaterial();
material.diffuseMap = htmlTexture;
material.update();
موتور حلقه paint و آپلود بافت WebGL را مدیریت میکند، با خط لوله متریال PlayCanvas ادغام میشود.
موارد استفاده نمایشدادهشده
نمونههای اولیه طیف API را نشان میدهند. یک کتاب سهبعدی رندرشده با WebGL از متن واقعی DOM برای صفحات استفاده میکند — فونتها از طریق CSS قابل تعویض، بلافاصله قابل ترجمه، و توسط خزندههای جستجو قابل اندیسگذاری. یک اسلایدر ژلهای WebGPU نور را از طریق یک مدل سهبعدی انکسار میدهد در حالی که به یک <input type="range"> بومی با ویژگی step پاسخ میدهد. یک تابلو تبلیغاتی سهبعدی متحرک یک مداد SVG را مستقیماً در بافت WebGL رندر میکند بدون حلقه انیمیشن سفارشی. یک لایه تایپوگرافی که توسط نشانگر سهبعدی متحرک دچار انحراف میشود، همچنان بهصورت کامل قابل انتخاب و جستجوی در صفحه است.
ویژگیهای مرورگر حفظشده
هدف طراحی API همارزی ویژگی با رندر استاندارد DOM است. محتوا در داخل یک Canvas با layoutsubtree در دسترس درخت دسترسپذیری باقی میماند — قارئههای صفحه بهصورت عادی آن را پردازش میکنند. جستجوی در صفحه تطابقها را در داخل بافتهای WebGL برجسته میکند. افزونههای مرورگر مثل جایگزینکننده متن یا مدیریتکننده رمز عبور بدون تغییر کار میکنند. DevTools المانهای رندرشده در بوم را بازبینی میکند؛ تنظیم CSS در بازبینیکننده بافت سهبعدی را بهصورت زنده بهروزرسانی میکند. تکمیل خودکار، حالت تاریک، حالت خواندن، و زوم مرورگر همگی همانطور که انتظار میرود کار میکنند.
محدودیتهای شناختهشده
دو محدودیت آگاهانه وجود دارد. محتوای iframe از مرجع متقاطع (Cross-origin) به دلایل امنیتی و حریم خصوصی قابل ترسیم نیست. اسکرول در رشته اصلی (Main-thread scrolling) الزامی است — رندر HTML-in-Canvas در رشته جاوااسکریپت اجرا میشود، بنابراین بهروزرسانیهای اسکرول و انیمیشن نمیتوانند مانند اسکرول بومی بهصورت مستقل اجرا شوند. توسعهدهندگان باید هزینه عملکرد اسکرول محتوا در داخل بوم در برابر اسکرول کل بوم را بسنجند.
چرا این API معماری وب را تغییر میدهد
HTML-in-Canvas API یک قید بنیادین را که ده سال معماری اپلیکیشن وب را شکل داده برمیدارد. تیمهای سازنده ویرایشگرهای مبتنی بر بوم، بازیها، یا تجسم داده دیگر نیازی ندارند چیدمان متن، مدیریت ورودی، یا دسترسپذیری را از صفر بازسازی کنند. حجم باندلها کوچک میشود چون منطق UI سفارشی با قابلیتهای بومی مرورگر جایگزین میشود. رعایت دسترسپذیری پیشفرض میشود نه یک فکر ثانویه. موتورهای جستجو و عاملهای هوش مصنوعی (Generative AI) میتوانند متن در صحنههای سهبعدی را بدون ابزار خاص اندیسگذاری کنند. به نظر ما، اگر این API استاندارد شود، مرز بین «اپلیکیشن وب» و «موتور بازی تحت وب» بهصورت محسوسی محو میشود — توسعهدهندگان دیگر مجبور نیستند بین قدرت گرافیکی و غنای معنایی معامله کنند.
منظر رقابتی و پذیرش
هیچ موتور مرورگر دیگری API معادل را عرضه نکرده است. فایرفاکس (Firefox) و سافاری (Safari) در انجمنهای استاندارد مفاهیم مشابه را بحث کردهاند اما پیادهسازی ندارند. آزمایش اصلی کروم این API را برای احتمالی استانداردسازی از طریق گروه جامعۀ واسطهی پلتفرم وب W3C (W3C Web Platform Incubator Community Group) موقعیتدهی میکند. پذیرش اولیه توسط Three.js و PlayCanvas آمادگی فریمورکها را نشان میدهد. آزمایش تا کروم ۱۵۰، حدود شش ماه، ادامه دارد و به اکوسیستم فرصت میدهد تا آزمایش کند و مشکلات را گزارش دهد.
توسعهدهندگان علاقهمند باید برای آزمایش اصلی ثبتنام کنند، دموهای chrome.dev را تست کنند، و مسائل را در ردیاب باگ کروم ثبت کنند. سطح API ممکن است بر اساس بازخورد تکامل یابد — بهویژه در مورد ارگونومی محاسبه تبدیل برای WebGL و WebGPU. شش ماه آینده تعیین میکند که HTML-in-Canvas به یک بدیهه دائمی پلتفرم وب تبدیل میماند یا یک آزمایش اختصاصی کروم باقی میماند.
گام بعدی شما
- برای تست API، کروم Canary ۱۴۹ یا جدیدتر را با پرچم
chrome://flags/#canvas-draw-elementفعال کنید و دموهای chrome.dev را بررسی کنید. - اگر اپلیکیشنی دارید که از رندر سفارشی برای متن در Canvas استفاده میکند، یک نمونه اولیه (Prototype) با
layoutsubtreeبسازید تا کاهش حجم باندل و سادگی کد را بسنجید. - برای پروژههای Three.js یا PlayCanvas، بستههای آزمایشی
THREE.HTMLTextureو سیستم بافت PlayCanvas را امتحان کنید و بازخورد را در ردیاب باگ کروم ثبت کنید.
اما داستان استانداردسازی این API هنوز تمام نشده — نظرات فایرفاکس و سافاری در انجمنهای W3C تعیینکنندهی آیندهی فرامرزیِ این قابلیت خواهد بود.




گفتگو