पेश है HTML-in-Canvas API का ऑरिजिन ट्रायल

Thomas Nattestad
Thomas Nattestad

वेब डेवलपर, वेब पर जटिल और ज़्यादा इंटरैक्टिव विज़ुअल ऐप्लिकेशन बनाते समय, सालों से आर्किटेक्चर से जुड़ा मुश्किल फ़ैसला लेते रहे हैं. उन्हें यह तय करना होता है कि वे रिच सिमैंटिक सुविधाओं के लिए डीओएम का इस्तेमाल करें या लो-लेवल ग्राफ़िक परफ़ॉर्मेंस के लिए सीधे तौर पर <canvas> एलिमेंट पर रेंडर करें.

अब आपको यह फ़ैसला लेने की ज़रूरत नहीं है, क्योंकि HTML-in-Canvas API का नया एक्सपेरिमेंटल वर्शन, ऑरिजिन ट्रायल में उपलब्ध है. इस एपीआई की मदद से, डीओएम कॉन्टेंट को सीधे तौर पर 2D कैनवस या WebGL/WebGPU टेक्सचर में ड्रा किया जा सकता है. साथ ही, यूज़र इंटरफ़ेस को इंटरैक्टिव, सुलभ, और अपने पसंदीदा ब्राउज़र की सुविधाओं से जोड़ा जा सकता है. एचटीएमएल को लो-लेवल ग्राफ़िक प्रोसेसिंग के साथ मिलाकर, ऐसे अनुभव बनाए जा सकते हैं जो पहले मुमकिन नहीं थे.

डीओएम बनाम कैनवस

इस नए एपीआई की ताकत को समझने के लिए, डीओएम और कैनवस, दोनों की खूबियों को देखना ज़रूरी है.

डीओएम, वेब यूज़र इंटरफ़ेस का मुख्य हिस्सा है. यह, टेक्स्ट लेआउट के सलूशन तुरंत उपलब्ध कराता है. साथ ही, रिच इंटरफ़ेस बनाने के लिए, सिमैंटिक तौर पर समझे जाने वाले कॉन्टेंट का इस्तेमाल करता है. इससे उपयोगकर्ता, वेब पेजों पर सामान्य कार्रवाइयां आसानी से कर पाते हैं. जैसे, कॉपी करने के लिए टेक्स्ट को हाइलाइट करना या सेव करने के लिए किसी इमेज पर राइट-क्लिक करना. हम अक्सर इन कार्रवाइयों को सामान्य मान लेते हैं. डीओएम, ब्राउज़र की ज़रूरी सुविधाओं के साथ भी इंटिग्रेट होता है. जैसे, सुलभता टूल, अनुवाद, पेज में ढूंढें, रीडर मोड, एक्सटेंशन, डार्क मोड, ब्राउज़र ज़ूम, और अपने-आप भरने की सुविधा.

दूसरी ओर, कैनवस (और WebGL/WebGPU) की मदद से, ज़्यादा बेहतर 2D और 3D ग्राफ़िक के लिए, पिक्सल के ग्रिड को चलाने के लिए लो-लेवल ऐक्सेस मिलता है. गेम और जटिल वेब ऐप्लिकेशन (जैसे, Google Docs या Figma) के लिए, परफ़ॉर्म करने वाले इस लो-लेवल ऐक्सेस की ज़रूरत होती है. कैनवस, मूल रूप से पिक्सल का ग्रिड होता है. इसलिए, रिस्पॉन्सिव टेक्स्ट जैसी सुविधाओं के लिए, कस्टम यूज़र इंटरफ़ेस लॉजिक की ज़रूरत होती है. इससे बंडल का साइज़ काफ़ी बढ़ जाता है. अहम बात यह है कि डीओएम में इंटिग्रेट की गई ब्राउज़र की सभी सुविधाएं, तब पूरी तरह से काम करना बंद कर देती हैं, जब यूज़र इंटरफ़ेस, स्टैटिक कैनवस पिक्सल ग्रिड में फंस जाता है.

डीओएम को कैनवस में लाने के फ़ायदे

HTML-in-Canvas API, एक ऐसा पुल है जो आपको दोनों दुनिया की बेहतरीन सुविधाएं देता है. <canvas> एलिमेंट में एचटीएमएल रखकर और उसके ट्रांसफ़ॉर्म को सिंक करके, यह पक्का किया जाता है कि कॉन्टेंट पूरी तरह से इंटरेक्टिव बना रहे. साथ ही, ब्राउज़र के सभी इंटिग्रेशन अपने-आप काम करें.

<canvas> एलिमेंट में, यूज़र इंटरफ़ेस को डीओएम से मैनेज कराने पर आपको ये सुविधाएं मिलती हैं:

  • टेक्स्ट लेआउट और फ़ॉर्मैटिंग: टेक्स्ट लेआउट और फ़ॉर्मैटिंग को आसान बनाया गया है. इसमें, सीएसएस स्टाइल लागू करने के साथ-साथ, मल्टीलाइन या बिडायरेक्शनल टेक्स्ट भी शामिल है.
  • फ़ॉर्म कंट्रोल: फ़ॉर्म कंट्रोल को बेहतर बनाया गया है और इसका इस्तेमाल करना आसान है. साथ ही, इसमें पसंद के मुताबिक बनाने के ज़्यादा विकल्प मौजूद हैं.
  • टेक्स्ट चुनना, कॉपी/पेस्ट करना, और राइट-क्लिक करना: उपयोगकर्ता, 3D सीन में टेक्स्ट को हाइलाइट कर सकते हैं या कॉन्टेक्स्ट मेन्यू पर राइट-क्लिक कर सकते हैं.
  • टेक्स्ट चुनना, कॉपी/पेस्ट करना, और राइट-क्लिक करना: उपयोगकर्ता, 3D सीन में टेक्स्ट को हाइलाइट कर सकते हैं या कॉन्टेक्स्ट मेन्यू पर राइट-क्लिक कर सकते हैं.
  • सुलभता: कैनवस में रेंडर किया गया कॉन्टेंट, सुलभता ट्री में दिखता है. सुलभता सिस्टम, यूज़र इंटरफ़ेस को सामान्य एचटीएमएल की तरह पार्स कर सकते हैं. साथ ही, इसे स्क्रीन रीडर जैसे सिस्टम में दिखा सकते हैं.
  • पेज में ढूंढें: उपयोगकर्ता, टेक्स्ट खोजने के लिए पेज में ढूंढें (Ctrl/Cmd+F) का इस्तेमाल कर सकते हैं. साथ ही, ब्राउज़र इसे सीधे तौर पर WebGL टेक्सचर में हाइलाइट करता है.
  • इंडेक्स करने की सुविधा और एआई एजेंट इंटरफ़ेस: वेब क्रॉलर और एआई एजेंट, 2D और 3D सीन में रेंडर किए गए टेक्स्ट को आसानी से इंडेक्स और पढ़ सकते हैं.
  • एक्सटेंशन इंटिग्रेशन: ब्राउज़र एक्सटेंशन, मूल रूप से काम करते हैं. उदाहरण के लिए, टेक्स्ट-रिप्लेसमेंट एक्सटेंशन, 3D मेश पर रेंडर किए गए टेक्स्ट को अपने-आप अपडेट करता है.
  • DevTools इंटिग्रेशन: Chrome DevTools में, WebGL/WebGPU यूज़र इंटरफ़ेस एलिमेंट के लिए, कैनवस कॉन्टेंट की जांच की जा सकती है. इंस्पेक्टर में सीएसएस स्टाइल में बदलाव करें और देखें कि यह 3D टेक्सचर पर तुरंत अपडेट हो जाता है!

इस्तेमाल के मुख्य उदाहरण

इस एपीआई की मदद से, कई डोमेन में बेहतरीन संभावनाएं मिलती हैं:

  • कैनवस पर आधारित बड़े ऐप्लिकेशन: Google Docs, Miro या Figma जैसे बड़े वेब ऐप्लिकेशन अब अपने कैनवस-ड्रिवन वर्कस्पेस में, जटिल ऐप्लिकेशन यूज़र इंटरफ़ेस कॉम्पोनेंट को मूल रूप से रेंडर कर सकते हैं. इससे सुलभता बेहतर होती है और बंडल का साइज़ कम होता है.
  • 3D सीन और गेम: मार्केटिंग साइटें, इमर्सिव WebXR अनुभव, और वेब गेम अब 3D सीन में पूरी तरह से इंटरैक्टिव वेब यूज़र इंटरफ़ेस रख सकते हैं. जैसे, 3D किताब, जिसमें असली डीओएम टेक्स्ट का इस्तेमाल किया गया हो या इन-गेम टर्मिनल, जो कॉपी और पेस्ट करने की सुविधा को मूल रूप से सपोर्ट करता हो.

एपीआई का इस्तेमाल कैसे करें

एपीआई का इस्तेमाल तीन चरणों में किया जाता है: कैनवस सेट अप करना, कैनवस में रेंडर करना, और सीएसएस ट्रांसफ़ॉर्म को अपडेट करना, ताकि ब्राउज़र को पता चल सके कि एलिमेंट, स्क्रीन पर कहां मौजूद है.

ज़रूरी शर्तें

HTML-in-Canvas API, Chrome 148 से 150 में ऑरिजिन ट्रायल में है. अपनी साइट पर इसे टेस्ट करने के लिए, Chrome Canary 149 या इसके बाद के वर्शन का इस्तेमाल करें. साथ ही, chrome://flags/#canvas-draw-element फ़्लैग को चालू करें. अन्य उपयोगकर्ताओं के लिए एपीआई को चालू करने के लिए, ऑरिजिन ट्रायल के लिए रजिस्टर करें.

पहला चरण: कैनवस का सामान्य सेटअप

सबसे पहले, अपने <canvas> टैग में layoutsubtree एट्रिब्यूट जोड़ें. इससे ब्राउज़र को कैनवस में नेस्ट किए गए कॉन्टेंट के बारे में पता चलता है. साथ ही, यह कॉन्टेंट को कैनवस में दिखाने और सुलभता ट्री में दिखाने के लिए तैयार करता है.

<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);

दूसरा चरण: रेंडर करना

2D कॉन्टेक्स्ट के लिए, drawElementImage तरीके का इस्तेमाल करें. paint इवेंट में ऐसा करें. यह इवेंट तब ट्रिगर होता है, जब एलिमेंट फिर से ड्रा होता है. उदाहरण के लिए, टेक्स्ट को हाइलाइट करने या उपयोगकर्ता के इनपुट के दौरान. इंटरैक्टिविटी को चालू रखने के लिए, एलिमेंट के सीएसएस ट्रांसफ़ॉर्म को रिटर्न वैल्यू के साथ अपडेट करना ज़रूरी है.

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 की तरह ही काम करता है, लेकिन इसमें डीओएम एलिमेंट को सोर्स के तौर पर लिया जाता है.

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 }
  );
};

तीसरा चरण: सीएसएस ट्रांसफ़ॉर्म अपडेट करना

एलिमेंट को कैनवस में रेंडर करने के बाद, आपको ब्राउज़र को यह बताना होगा कि यह कहां मौजूद है. इससे कैनवस और डीओएम के लेआउट के बीच, स्पेस से जुड़ा सिंक पक्का होता है. यह ज़रूरी है, ताकि ब्राउज़र, इवेंट ज़ोन को सही तरीके से मैप कर सके. जैसे, उपयोगकर्ता कहां क्लिक करता है या माउस घुमाता है. साथ ही, यह भी पता चल सके कि एलिमेंट कहां रेंडर किया गया है.

2D कॉन्टेक्स्ट के मामले में, रेंडरिंग कॉल से मिले ट्रांसफ़ॉर्म को .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() से मिली रिटर्न वैल्यू की तरह इस्तेमाल किया जा सकता है. इसके लिए, आपको यह करना होगा:

  • WebGL MVP मैट्रिक्स को डीओएम मैट्रिक्स में बदलना.
  • एचटीएमएल एलिमेंट को सामान्य बनाना. एचटीएमएल एलिमेंट का साइज़ पिक्सल में होता है. उदाहरण के लिए, 200 पिक्सल चौड़ा. हालांकि, WebGL आम तौर पर ऑब्जेक्ट को "यूनिट स्क्वेयर" के तौर पर लेता है. उदाहरण के लिए, 0 से 1 तक. अगर सामान्य नहीं बनाया जाता है, तो आपका 200 पिक्सल वाला बटन, 200 गुना बड़ा दिखेगा.
  • कैनवस व्यूपोर्ट पर मैप करना. यह चरण, "रीस्केलिंग" फ़ेज़ है. यह यूनिट-स्पेस मैथ को स्क्रीन पर मौजूद आपके <canvas> एलिमेंट के असली पिक्सल डाइमेंशन से मैच करने के लिए स्ट्रेच करता है. यह Y-ऐक्सिस को भी फ़्लिप करता है, क्योंकि WebGL में, ऊपर की ओर पॉज़िटिव होता है. वहीं, सीएसएस में, नीचे की ओर पॉज़िटिव होता है.
  • फ़ाइनल ट्रांसफ़ॉर्म कैलकुलेट करना. मैट्रिक्स को इस क्रम में गुणा करें: Viewport * MVP * Normalization. इन्हें एक फ़ाइनल ट्रांसफ़ॉर्म में मिलाकर, एक "मैप" बनता है. इससे ब्राउज़र को यह पता चलता है कि 3D ड्राइंग के साथ अलाइन करने के लिए, एचटीएमएल एलिमेंट लेयर को कहां रखना चाहिए.
  • एचटीएमएल एलिमेंट पर ट्रांसफ़ॉर्म लागू करना. इससे एचटीएमएल एलिमेंट लेयर, रेंडर किए गए पिक्सल के ठीक ऊपर आ जाती है. इससे यह पक्का होता है कि जब कोई उपयोगकर्ता किसी बटन पर क्लिक करता है या टेक्स्ट चुनता है, तो वह असली एचटीएमएल एलिमेंट पर क्लिक करता है.
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();

डेमो

डेमो आज़माने से पहले, पक्का करें कि आपका एनवायरमेंट सही तरीके से कॉन्फ़िगर किया गया हो.

एपीआई का इस्तेमाल करने के लिए, कई डेमो उपलब्ध हैं. इन्हें रेफ़रंस के तौर पर इस्तेमाल किया जा सकता है. हमें कम्यूनिटी से क्रिएटिव सलूशन मिल रहे हैं. इनमें, अनुवाद की जा सकने वाली 3D किताबों से लेकर, यूज़र इंटरफ़ेस एलिमेंट तक शामिल हैं. ये एलिमेंट, ग्लास शेडर के ज़रिए रिफ़्रैक्ट होते हैं:

  • 3D किताब: यह WebGL-रेंडर की गई 3D किताब है, जिसमें पेजों के लिए एचटीएमएल लेआउट का इस्तेमाल किया गया है. उपयोगकर्ता, सीएसएस की मदद से फ़ॉन्ट बदल सकते हैं. यह डीओएम पर आधारित है. इसलिए, इसमें पहले से मौजूद अनुवाद की सुविधा तुरंत काम करती है. साथ ही, एआई एजेंट, टेक्स्ट को आसानी से एक्सट्रैक्ट कर सकते हैं.
  • इंटरैक्टिव 3D यूज़र इंटरफ़ेस: यह WebGPU जेली स्लाइडर है, जो 3D मॉडल के आधार पर लाइट को रिफ़्रैक्ट करता है. साथ ही, यह सामान्य एचटीएमएल <input type="range"> स्टेप एट्रिब्यूट के हिसाब से काम करता है.
  • ऐनिमेटेड टेक्सचर: यह डाइनैमिक 3D बिलबोर्ड है, जो कस्टम ऐनिमेशन लूप की ज़रूरत के बिना, सीधे तौर पर डीओएम का इस्तेमाल करके WebGL टेक्सचर में ऐनिमेटेड एसवीजी पेंसिल रेंडर करता है.
  • रिफ़्रैक्टिव ओवरले: यह इंटरैक्टिव टाइपोग्राफ़ी लेयर है, जो मूविंग 3D कर्सर से डिस्टॉर्ट होती है. हालांकि, इसे पेज में ढूंढें सुविधा का इस्तेमाल करके पूरी तरह से चुना और खोजा जा सकता है.

कम्यूनिटी की ओर से बनाए गए डेमो का कलेक्शन देखें. अगर आपको इस कलेक्शन में, HTML-in-Canvas का डेमो शामिल करना है, तो इसे जोड़ने के लिए पुल का अनुरोध बनाएं.

सीमाएं

यह एपीआई भले ही पावरफ़ुल हो, लेकिन इसकी कुछ सीमाएं हैं:

  • क्रॉस-ऑरिजिन कॉन्टेंट: सुरक्षा और निजता की वजहों से, यह एपीआई क्रॉस-ऑरिजिन iframe कॉन्टेंट के साथ काम नहीं करता.
  • मुख्य थ्रेड पर स्क्रोल करना: HTML-in-Canvas को JavaScript की मदद से ड्रा किया जाता है. इसका मतलब है कि स्क्रोलिंग और ऐनिमेशन, JavaScript से अलग से अपडेट नहीं हो सकते. जैसे, वे कैनवस के बाहर हो सकते हैं. डेवलपर को इस बात पर ध्यान देना चाहिए कि स्क्रोलिंग कॉन्टेंट को कैनवस में रखने और पूरे कैनवस को स्क्रोल करने में, परफ़ॉर्मेंस के मामले में क्या अंतर होता है.

सुझाव/राय दें या शिकायत करें

अगर आप HTML-in-Canvas API के साथ एक्सपेरिमेंट कर रहे हैं, तो हमें अपने अनुभव के बारे में बताएं! ऑरिजिन ट्रायल के लिए साइन अप करके, अपनी साइट पर इस सुविधा को चालू किया जा सकता है. इससे हमें एपीआई डिज़ाइन को बेहतर बनाने में मदद मिलेगी. कोई भी सुझाव/राय या शिकायत देने के लिए, समस्या की शिकायत भी की जा सकती है.

संसाधन