पेश है 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 सीन में टेक्स्ट को हाइलाइट कर सकते हैं या कॉन्टेक्स्ट मेन्यू पर राइट-क्लिक कर सकते हैं.
  • सुलभता: कैनवस में रेंडर किया गया कॉन्टेंट, सुलभता ट्री में दिखता है. सुलभता सिस्टम, यूज़र इंटरफ़ेस को सामान्य एचटीएमएल की तरह पार्स कर सकते हैं. साथ ही, इसे स्क्रीन रीडर जैसे सिस्टम में दिखा सकते हैं.
  • Find-in-page: उपयोगकर्ता, टेक्स्ट खोजने के लिए, पेज में खोजें (Ctrl/Cmd+F) का इस्तेमाल कर सकते हैं. ब्राउज़र, इसे सीधे WebGL टेक्सचर में हाइलाइट करेगा.
  • Find-in-page: उपयोगकर्ता, टेक्स्ट खोजने के लिए, पेज में खोजें (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 के साथ एक्सपेरिमेंट किया है, तो हमें इसके बारे में बताएं! ऑरिजिन ट्रायल के लिए साइन अप करके, अपनी साइट पर इस सुविधा को चालू किया जा सकता है. इससे हमें एपीआई डिज़ाइन को बेहतर बनाने में मदद मिलेगी. कोई भी सुझाव/राय या शिकायत देने के लिए, समस्या की शिकायत भी की जा सकती है.

संसाधन