HTML-in-Canvas API की मदद से, इंटरैक्टिव और ऐक्सेस किए जा सकने वाले DOM सबट्री को सीधे 2D, WebGL या WebGPU कैनवस कॉन्टेक्स्ट में रेंडर किया जा सकता है.
इस गाइड में, HTML-in-Canvas की तकनीकी जानकारी दी गई है. यह जानकारी, सभी कैनवस कॉन्टेक्स्ट पर लागू होती है:
- एचटीएमएल रेंडर करने के लिए कैनवस तैयार करना
drawableसबट्री को समझनाpaintइवेंट के बारे में जानकारी- किसी एलिमेंट की ज्यामिति को अपडेट करना
- HTML-in-Canvas की सुलभता के बारे में जानकारी
- फ़ॉलबैक रणनीतियों का इस्तेमाल करना
एचटीएमएल रेंडर करने के लिए अपना कैनवस तैयार करना
<canvas> के अंदर एचटीएमएल एलिमेंट रेंडर करने के लिए, ये दो एट्रिब्यूट कॉन्फ़िगर करें:
content="drawable"को<canvas>पर सेट करें: इससे कैनवस एलिमेंट, अपने डिसेंडेंट डीओएम नोड को लेआउट करने के लिए ऑप्ट-इन करता है. ऐसा करने पर, वह उन्हें सिर्फ़ छिपाए गए कॉन्टेंट के तौर पर नहीं दिखाता.- टारगेट एलिमेंट पर
drawableसेट करें: इससे किसी एलिमेंट को कैनवस रेंडरिंग के लिए, इंडिपेंडेंट पेंट रूट के तौर पर मार्क किया जाता है.
<canvas id="mainCanvas" width="600" height="400" content="drawable">
<div id="parentElement" class="card" drawable>
<h2>Container Title</h2>
<p>Descriptive text inside the parent element.</p>
<!-- Nested drawable: excluded from parentElement and drawn independently -->
<button id="childElement" class="btn" drawable>Action Button</button>
</div>
</canvas>
ड्रॉएबल सबट्री को समझना
कैनवस में रेंडर करने के लिए एचटीएमएल तैयार करते समय, आपको यह तय करना होगा कि किन एलिमेंट को अलग से ड्रॉ किया जाना चाहिए. हर drawable एलिमेंट, एक इंडिपेंडेंट पेंट रूट और उसके ड्रॉ किए जा सकने वाले सबट्री को दिखाता है.
ड्रॉएबल सबट्री में, drawable एट्रिब्यूट वाला एलिमेंट और उसके सभी डिसेंडेंट शामिल होते हैं. हालांकि, इसमें ऐसे नेस्ट किए गए डिसेंडेंट शामिल नहीं होते जिनमें drawable भी शामिल हो. उदाहरण के लिए:
<canvas content="drawable">
<!-- Subtree A (Renders parent container and text) -->
<div id="parentElement" drawable>
<h2>Parent Card Heading</h2>
<p>Card body text content.</p>
<!-- Subtree B (Excluded from Subtree A and drawn separately) -->
<button id="nestedChildElement" drawable>
Clickable Button
</button>
</div>
</canvas>
| एलिमेंट | पेंट बाउंड्री | व्यवहार |
|---|---|---|
#parentElement (सबट्री A) |
बैकग्राउंड पैनल, हेडिंग, और पैराग्राफ़ | drawElementImage(parentElement, ...) को कॉल करते समय रेंडर किया गया |
#nestedChildElement (सबट्री B) |
बटन की सतह और टेक्स्ट | #parentElement सबट्री से बाहर रखा गया; इसे अलग drawElementImage(nestedChildElement, ...) कॉल में रेंडर किया गया |
drawable बटन को drawable कार्ड के अंदर नेस्ट किया गया है. साथ ही, हर एक को उसके drawElementImage() कॉल से ड्रॉ किया गया है.ड्रॉएबल एलिमेंट को नेस्ट करने से, सिमैंटिक एचटीएमएल रिलेशनशिप को बनाए रखा जा सकता है. साथ ही, कैनवस सीन पर चाइल्ड एलिमेंट को लेयर करने, उनकी पोज़िशन तय करने या उन्हें ऐनिमेट करने की सुविधा मिलती है.
paint इवेंट के बारे में जानकारी
HTML-in-Canvas API, paint इवेंट को पेश करता है. जब भी किसी drawable डिसेंडेंट पर लेआउट, टेक्स्ट या सीएसएस स्टाइल में बदलाव होता है, तो ब्राउज़र अपने-आप <canvas> पर paint इवेंट भेजता है.
canvas.addEventListener("paint", (event) => {
// Clear the viewport
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Redraw elements individually
ctx.drawElementImage(parentElement, 20, 20);
ctx.drawElementImage(childElement, 40, 90);
});
paint इवेंट, CanvasPaintEvent का एक इंस्टेंस भेजता है. यह स्टैंडर्ड डीओएम Event को बड़ा करता है. साथ ही, इसमें एक changedElements प्रॉपर्टी होती है, जिसमें सिर्फ़ वे drawable डिसेंडेंट होते हैं जो पिछली बार पेंट होने के बाद से विज़ुअल तौर पर बदल गए हैं. जैसे, स्टाइल में बदलाव, लेआउट में बदलाव, टेक्स्ट में बदलाव या फ़ोकस की स्थिति में बदलाव.
canvas.addEventListener("paint", (event) => {
// Array of changed elements
for (const el of event.changedElements) {
console.log(`Element #${el.id} visually changed and needs repainting`);
}
});
फ़्रेम की लाइफ़साइकल
paint इवेंट में लागू किए गए कैनवस ड्रॉइंग कमांड, मौजूदा रेंडरिंग अपडेट में दिखते हैं. हालांकि, paint इवेंट में किए गए DOM बदलाव, अगले फ़्रेम तक नहीं दिखते:
canvas.addEventListener("paint", () => {
// Renders in the CURRENT rendering update:
ctx.drawElementImage(parentElement, 20, 20);
// Modifying DOM styles inside paint schedules a new layout cycle;
// changes will appear in the NEXT frame:
parentElement.style.backgroundColor = "#e0f2fe";
});
नेस्ट किए गए कैनवस
अगर आपके पेज में नेस्ट किए गए <canvas content="drawable"> एलिमेंट हैं, तो ब्राउज़र paint इवेंट को डीओएम ट्री के उल्टे क्रम में भेजता है. इसका मतलब है कि डिसेंडेंट, ऐंसेस्टर से पहले ट्रिगर होते हैं. इससे यह पक्का होता है कि चाइल्ड कैनवस एलिमेंट, पैरंट कैनवस एलिमेंट के कंपोज़िट होने से पहले रेंडर हो जाएं.
मान लें कि इस उदाहरण में, अंदर मौजूद nestedElement एलिमेंट को विज़ुअल तौर पर बदलना चाहिए. उदाहरण के लिए, जब उपयोगकर्ता उस पर पॉइंटर को होल्ड करता है, तो उसका सीएसएस बैकग्राउंड कलर बदल जाता है. सबसे पहले, nestedCanvas कैनवस पर paint इवेंट ट्रिगर होता है. इसका लिसनर, चाइल्ड बफ़र को हटा देता है और अपडेट की गई स्थिति को रेंडर करने के लिए ctx.drawElementImage(nestedElement, ...) को कॉल करता है. इसके बाद, paint इवेंट parentCanvas पर ट्रिगर होता है. इसका लिसनर, सीन में wrapperElement को ड्रॉ करता है. इसमें पहले से अपडेट किया गया nestedCanvas कैनवस शामिल होता है.
<!-- Outer parent canvas -->
<canvas id="parentCanvas" width="800" height="600" content="drawable">
<!-- Wrapper container for nested content -->
<div id="wrapperElement" drawable>
<!-- Nested child canvas -->
<canvas id="nestedCanvas" width="300" height="200" content="drawable">
<div id="nestedElement" class="badge" drawable>
Inner Child Content
</div>
</canvas>
</div>
</canvas>
<script>
const parentCanvas = document.getElementById("parentCanvas");
const parentCtx = parentCanvas.getContext("2d");
const nestedCanvas = document.getElementById("nestedCanvas");
const nestedCtx = nestedCanvas.getContext("2d");
const nestedElement = document.getElementById("nestedElement");
const wrapperElement = document.getElementById("wrapperElement");
// ============================================================================
// 1. Child Canvas Listener (Fires FIRST)
// ============================================================================
nestedCanvas.addEventListener("paint", (event) => {
// Clear and redraw the child canvas with the updated inner element
nestedCtx.clearRect(0, 0, nestedCanvas.width, nestedCanvas.height);
nestedCtx.drawElementImage(nestedElement, 10, 10);
console.log("1. Child nested canvas rendered fresh pixels.");
});
// ============================================================================
// 2. Parent Canvas Listener (Fires SECOND)
// ============================================================================
parentCanvas.addEventListener("paint", (event) => {
// Clear and redraw the parent canvas scene
parentCtx.clearRect(0, 0, parentCanvas.width, parentCanvas.height);
// Draws the wrapper container (including the freshly painted nestedCanvas)
parentCtx.drawElementImage(wrapperElement, 50, 50);
console.log("2. Parent canvas composited the complete scene.");
});
</script>
<canvas content="drawable"> एलिमेंट नेस्ट किए जाते हैं, तो पेंट इवेंट रिवर्स ट्री ऑर्डर में ट्रिगर होते हैं: नेस्ट किया गया कैनवस पहले पेंट होता है, ताकि पैरंट कैनवस अपने अप-टू-डेट पिक्सल ड्रॉ कर सके.paint इवेंट ट्रिगर करना
अगले फ़्रेम पर एक paint इवेंट शेड्यूल करने के लिए, requestPaint() को कॉल करें. भले ही, डीओएम टेक्स्ट या स्टाइल में कोई बदलाव न हुआ हो. इसका इस्तेमाल, मूविंग एलिमेंट वाले इंटरैक्टिव सीन रेंडर करने के लिए किया जा सकता है. उदाहरण के लिए:
let animatedX = 0;
canvas.addEventListener("paint", () => {
animatedX += 1.5; // Move the element horizontally
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawElementImage(staticElement, 20, 20);
ctx.drawElementImage(movingElement, animatedX, 200);
// Request canvas paint on each rendering update for smooth animation
canvas.requestPaint();
});
canvas.requestPaint();
किसी एलिमेंट की ज्यामिति को अपडेट करना
जब किसी कैनवस में एचटीएमएल एलिमेंट को ड्रॉ किया जाता है, तो आपका ड्रॉइंग कोड यह तय करता है कि एलिमेंट कहां दिखेगा और उसका साइज़ कितना बड़ा होगा. सामान्य एचटीएमएल कॉन्टेंट के उलट, एलिमेंट की स्क्रीन पर पोज़िशन और साइज़, उसकी ज्यामिति, सिर्फ़ उसके कंप्यूट किए गए स्टाइल से तय नहीं की जा सकती. ब्राउज़र को किसी एलिमेंट की ज्यामिति की ज़रूरत होती है, ताकि वह ये काम कर सके:
- क्लिक, टैप, और पॉइंटर मूवमेंट जैसे इनपुट इवेंट को सही एलिमेंट पर भेजें. इसे हिट टेस्टिंग कहा जाता है.
- लेआउट क्वेरी से सटीक नतीजे दिखाएं, जैसे कि
getBoundingClientRect(). - स्क्रीन पर मौजूद कॉन्टेंट को बड़ा करके दिखाने की सुविधा जैसी सहायक टेक्नोलॉजी को एलिमेंट की सीमाओं की जानकारी दें.
2D में, drawElementImage() डिफ़ॉल्ट रूप से ज्यामिति को अपने-आप अपडेट करता है. एलिमेंट की कंप्यूट की गई स्टाइल और कॉन्टेक्स्ट के मौजूदा ट्रांसफ़ॉर्म के साथ-साथ, पास किए गए आर्ग्युमेंट से यह तय किया जा सकता है कि एलिमेंट कहां दिखेगा.
WebGL और WebGPU कॉन्टेक्स्ट में, ब्राउज़र ज्यामिति का पता नहीं लगा सकता. एचटीएमएल एलिमेंट को टेक्सचर में कॉपी किया जाता है. इसके बाद, आपका शेडर प्रोग्राम उस टेक्सचर को सीन पर किसी भी तरह से मैप कर सकता है. ब्राउज़र को यह बताने के लिए कि एलिमेंट कहां दिखता है, updateElementGeometry() को ट्रांसफ़ॉर्म पास करें.
updateElementGeometry() को कॉल करना बंद कर देता है, तब ब्राउज़र को यह पता नहीं चलता कि 3D कार्ड पर एचटीएमएल एलिमेंट कहां हैं. कार्ड घूमता रहता है, लेकिन क्लिक करने पर अब भी वहीं पहुंचा जाता है जहां पहले पहुंचा जाता था.ज्यामिति को अपडेट करने के लिए, मेथड में स्पेशल ट्रांसफ़ॉर्म पास करें. अलग-अलग कॉन्टेक्स्ट के लिए ट्रांसफ़ॉर्म बनाने का तरीका जानें:
- जियॉमेट्री को 2D में सिंक करना.
- WebGL में ज्यामिति को सिंक करना.
- WebGPU में जियॉमेट्री को सिंक करना.
हिट-टेस्ट स्टैकिंग ऑर्डर मैनेज करना
डिफ़ॉल्ट रूप से, जब भी किसी एलिमेंट की ज्यामिति अपडेट की जाती है, तो ब्राउज़र उसे इंटरैक्शन स्टैक में सबसे ऊपर ले आता है (आखिरी बार ड्रॉ किया गया = सबसे ऊपर मौजूद क्लिक टारगेट). preserveHitTestOrder का इस्तेमाल करके, इस व्यवहार को कंट्रोल किया जा सकता है:
// Updates the backdrop without stealing clicks from foreground elements
canvas.updateElementGeometry(backgroundElement, {
canvasTransform: customTransform,
preserveHitTestOrder: true
});
| विकल्प | व्यवहार | इस्तेमाल का उदाहरण |
|---|---|---|
preserveHitTestOrder: false (डिफ़ॉल्ट) |
यह एलिमेंट को हिट-टेस्ट स्टैक में सबसे ऊपर ले जाता है. | फ़ोरग्राउंड एलिमेंट, जिन पर सबसे पहले क्लिक मिलने चाहिए. |
preserveHitTestOrder: true |
एलिमेंट को उसकी मौजूदा स्टैक प्राथमिकता पर छोड़ देता है. | बैकग्राउंड के ऐसे एलिमेंट जिन्हें फ़ोरग्राउंड एलिमेंट के बाद अपडेट करना ज़रूरी है. |
जियॉमेट्री रीसेट करें
किसी एलिमेंट को डीओएम से हटाए बिना उसे अन-ड्रॉ करने के लिए, canvas.clearElementGeometry() को कॉल करें. इससे एलिमेंट का कैनवस ट्रांसफ़ॉर्म रीसेट हो जाता है और सुलभता ट्री में इसे ऑफ़स्क्रीन के तौर पर मार्क कर दिया जाता है:
// Remove from canvas hit-testing and mark as offscreen
canvas.clearElementGeometry(childElement);
साइज़ और यूनिट
जब एचटीएमएल को कैनवस में ड्रॉ किया जाता है, तो ब्राउज़र एक साथ तीन कोऑर्डिनेट सिस्टम में काम करता है: सीएसएस पिक्सल, डिवाइस पिक्सल, और कैनवस ग्रिड पिक्सल.
सीएसएस पिक्सल का इस्तेमाल, लेआउट के दौरान कॉन्टेंट को मापने और उसकी पोज़िशन तय करने के लिए किया जाता है. इसके बाद, ब्राउज़र उन्हें devicePixelRatio से स्केल करता है, ताकि पेज को फ़िज़िकल डिवाइस के पिक्सल में रास्टराइज़ किया जा सके. सीएसएस पिक्सल एक लॉजिकल यूनिट है. इसलिए, पोज़िशन और साइज़ अक्सर डिवाइस पिक्सल के फ़्रैक्शन के तौर पर दिखते हैं. उदाहरण के लिए, 10.5 सीएसएस पिक्सल × 1.5 का डीपीआर = 15.75 डिवाइस पिक्सल. स्क्रीन पर सिर्फ़ पूरे पिक्सल को रंग दिया जा सकता है. इसलिए, ब्राउज़र को उनके बीच आने वाली हर चीज़ को हल करना पड़ता है. यह दो तरीकों से ऐसा करता है:
- स्नैपिंग: बॉक्स की सीमाओं, बॉर्डर, और लेयर ऑफ़सेट को डिवाइस के सबसे नज़दीकी पिक्सल के हिसाब से राउंड किया जाता है, ताकि वे साफ़ दिखें.
- एंटी-एलियासिंग: टेक्स्ट ग्लिफ़, कर्व, और बदले गए किनारों के लिए, कुछ हिस्से को आस-पास के पिक्सल में मिला दिया जाता है, ताकि वे अब भी स्मूद और सही जगह पर दिखें.
<canvas> का अपना पिक्सल ग्रिड होता है, जो इसके width और height एट्रिब्यूट से सेट होता है. यह सीएसएस पिक्सल और डिवाइस पिक्सल, दोनों से अलग होता है. इसे दिखाने के लिए, ब्राउज़र कैनवस बिटमैप को फ़िज़िकल पिक्सल ग्रिड पर फिर से सैंपल करता है. साथ ही, हर डिवाइस पिक्सल को भरने के लिए, आस-पास के कैनवस पिक्सल को ब्लेंड करता है. ब्राउज़र, सीएसएस, कैनवस, और डिवाइस पिक्सल के बीच कैसे मैप करता है, यह जानने के लिए devicePixelContentBox के साथ पिक्सल-परफ़ेक्ट रेंडरिंग देखें.
सभी drawable एचटीएमएल एलिमेंट, सीएसएस पिक्सल में लेआउट किए जाते हैं. कैनवस में ड्रॉ किया गया कॉन्टेंट दो कन्वर्ज़न से गुज़र सकता है. पहला, सीएसएस पिक्सल से कैनवस ग्रिड में और दूसरा, कैनवस ग्रिड से फ़िज़िकल पिक्सल में. हर वह चरण जो ठीक से अलाइन नहीं होता है, उसमें ब्लेंडिंग का एक और राउंड जुड़ जाता है. इससे नतीजा धुंधला दिख सकता है. इससे बचने के लिए, कैनवस ग्रिड को फ़िज़िकल पिक्सल ग्रिड से मैच करें. इसके दो फ़ायदे हैं:
- एक जैसा रेंडरिंग: अगर कैनवस, फ़िज़िकल पिक्सल पर 1:1 मैप करता है, तो सीएसएस पिक्सल को कैनवस पिक्सल में बदलने पर वही नतीजा मिलता है जो ब्राउज़र के सीएसएस पिक्सल को सामान्य एचटीएमएल के लिए फ़िज़िकल पिक्सल में बदलने पर मिलता है.
- कोई अतिरिक्त ब्लेंडिंग नहीं: अगर कैनवस, फ़िज़िकल पिक्सल पर 1:1 मैप करता है, तो उसे डिवाइस पिक्सल में बदलने से कोई धुंधलापन नहीं आता.
नीचे दी गई इमेज में, पिक्सल कन्वर्ज़न दिखाया गया है. इसकी वजह से, कॉन्टेंट धुंधला हो जाता है. तीनों ग्रिड एक जैसे नहीं हैं: कैनवस एलिमेंट 360 × 180 सीएसएस पिक्सल का है और कैनवस का साइज़ 300 × 150 कैनवस ग्रिड पिक्सल है. डिवाइस पिक्सल के 1.5 के अनुपात पर, कैनवस 540 × 270 डिवाइस पिक्सल को कवर करता है. एक सीएसएस पिक्सल, 1.5 डिवाइस पिक्सल तक फैला होता है. साथ ही, एक कैनवस पिक्सल, 1.2 सीएसएस पिक्सल या 1.8 डिवाइस पिक्सल (पैनल 1) तक फैला होता है. जब सर्कल बनाया जाता है, तो उसकी पोज़िशन और रेडियस, सीएसएस पिक्सल के बजाय कैनवस पिक्सल में होते हैं. इसलिए, ब्राउज़र उसे 300 × 150 कैनवस बिटमैप में बनाता है और किनारे को ऐंटी-एलियस करता है (पैनल 2). कैनवस को दिखाने के लिए, ब्राउज़र इस बिट मैप को 540 × 270 डिवाइस पिक्सल तक फैलाता है. इसलिए, ब्राउज़र को इंटरपोलेट करना पड़ता है: हर डिवाइस पिक्सल, सबसे नज़दीकी कैनवस पिक्सल का वेटेड मिक्स लेता है. इसमें पहले से ही एंटी-एलियास एज मौजूद होता है. किनारे को दूसरी बार ब्लेंड किया जाता है और यह कई डिवाइस पिक्सल में फैल जाता है. इसलिए, सर्कल धुंधला दिखता है (पैनल 3). अगर कैनवस ग्रिड, डिवाइस के पिक्सल से 1:1 के अनुपात में मैच करता है, तो इस समस्या से बचा जा सकता है. इसके बाद, ब्राउज़र सीधे डिवाइस ग्रिड पर, किनारे को सिर्फ़ एक बार ऐंटी-एलियास करेगा और रीसैंपलिंग के बिना बिट मैप दिखाएगा, ताकि किनारा साफ़ दिखे (पैनल 4):
साफ़ तौर पर रेंडर करने के लिए, कैनवस ग्रिड को कैनवस में शामिल डिवाइस पिक्सल की संख्या से मैच करना चाहिए: पिछले उदाहरण में 540 × 270.
साइज़ बदलने वाले ऑब्ज़र्वर की मदद से, कैनवस ग्रिड का साइज़ तय करना
ग्रिड का सही साइज़, डिवाइस पर निर्भर करता है. साथ ही, लेआउट बदलने, उपयोगकर्ता के पेज को ज़ूम करने या उपयोगकर्ता के विंडो को किसी दूसरे डिसप्ले पर ले जाने पर, यह बदल सकता है. इसलिए, ग्रिड के साइज़ को हार्डकोड करने के बजाय, ब्राउज़र को यह तय करने दें कि साइज़ क्या होना चाहिए.
कैनवस ग्रिड को स्क्रीन पर मौजूद कैनवस के साथ सिंक रखने के लिए, हमेशा ResizeObserver का इस्तेमाल करके इसका साइज़ तय करें. साथ ही, कैनवस ग्रिड को devicePixelContentBoxSize पर अडजस्ट करें:
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);
सीएसएस पिक्सल को कैनवस ग्रिड निर्देशांकों में बदलना
paint हैंडलर अक्सर डीओएम मेज़रमेंट को ड्रॉ करने के लिए कॉल के साथ जोड़ता है. उदाहरण के लिए, किसी एलिमेंट को उसके साइज़ के आधार पर पोज़िशन करना. दोनों में अलग-अलग यूनिट का इस्तेमाल किया जाता है:
- कैनवस पिक्सल, कैनवस ग्रिड के बारे में बताते हैं. इसका साइज़
canvas.widthऔरcanvas.heightहोता है. उदाहरण के लिए,drawElementImage()ड्रॉइंग मेथड केdx,dy,dwidth, औरdheightपैरामीटर या अन्य कैनवस एपीआई पैरामीटर, जैसे किfillRect()औरtranslate(). - सीएसएस पिक्सल, पेज के बारे में बताते हैं.
offsetWidthऔरclientWidthजैसे डीओएम मेज़रमेंट औरoffsetXजैसे पॉइंटर इवेंट कोऑर्डिनेट, सीएसएस पिक्सल में होते हैं.
यहां एपीआई के अलग-अलग पैरामीटर में इस्तेमाल की जाने वाली यूनिट के बारे में खास जानकारी दी गई है:
| वैल्यू | इकाइयां |
|---|---|
canvas.width और canvas.height |
कैनवस ग्रिड पिक्सल |
2D ड्रॉइंग के पैरामीटर, जिनमें drawElementImage() के dx, dy, dwidth, और dheight शामिल हैं |
कैनवस ग्रिड पिक्सल |
canvas.getElementImageDefaultSize() ने width और height को वापस किया |
कैनवस ग्रिड पिक्सल |
सोर्स रेक्टैंगल: drawElementImage() और texElementSubImage2D() के sx, sy, swidth, और sheight; drawElementImageToTexture() के sourceX, sourceY, sourceWidth, और sourceHeight |
सीएसएस पिक्सल |
टेक्स्चर के साइज़: texElementSubImage2D() में से width और height विकल्प; drawElementImageToTexture() में से size |
टेक्सचर पिक्सल. डिफ़ॉल्ट रूप से, कैनवस ग्रिड पिक्सल में एलिमेंट का साइज़, राउंड अप किया जाता है |
एलिमेंट की ज्यामिति: getElementTransform(), और updateElementGeometry() के canvasTransform और canvasClip विकल्प |
सीएसएस पिक्सल |
डीओएम मेज़रमेंट, जैसे कि offsetWidth, clientWidth, getBoundingClientRect(), पॉइंटर इवेंट कोऑर्डिनेट, और साइज़ बदलने वाले ऑब्ज़र्वर एंट्री का contentBoxSize |
सीएसएस पिक्सल |
devicePixelContentBoxSize, साइज़ बदलने वाले ऑब्ज़र्वर की एंट्री है |
डिवाइस पिक्सल |
गलत यूनिट का इस्तेमाल करने पर, हो सकता है कि कैनवस में बनाए गए एचटीएमएल कॉन्टेंट का साइज़ आपकी उम्मीद के मुताबिक न हो. कैनवस में एचटीएमएल ड्रॉ करते समय, आपको सीएसएस पिक्सल वैल्यू को कैनवस ग्रिड पिक्सल में बदलना होगा. ऐसा करने के लिए, कैनवस ग्रिड साइज़ और कैनवस सीएसएस साइज़ के बीच के अनुपात का हिसाब लगाएं: हॉरिज़ॉन्टल तौर पर canvas.width / cssContentWidth और वर्टिकल तौर पर canvas.height / cssContentHeight. रीसाइज़ ऑब्ज़र्वर, अनुपात का हिसाब लगाने के लिए एक सुविधाजनक जगह है. इसके लिए, यह तरीका अपनाएं:
// The number of canvas grid pixels per CSS pixel
let scaleX = 1;
let scaleY = 1;
const observer = new ResizeObserver(([entry]) => {
const dpc = entry.devicePixelContentBoxSize;
const cssContentWidth = entry.contentRect.width;
const cssContentHeight = entry.contentRect.height;
// Best practice: make the canvas grid match the physical pixel grid
if (dpc) {
canvas.width = dpc[0].inlineSize;
canvas.height = dpc[0].blockSize;
} else {
canvas.width = Math.round(cssContentWidth * window.devicePixelRatio);
canvas.height = Math.round(cssContentHeight * window.devicePixelRatio);
}
scaleX = canvas.width / cssContentWidth;
scaleY = canvas.height / cssContentHeight;
});
const supportsDevicePixelContentBox =
typeof ResizeObserverEntry !== 'undefined' &&
'devicePixelContentBoxSize' in ResizeObserverEntry.prototype;
const options = supportsDevicePixelContentBox ? { box: 'device-pixel-content-box' } : {};
observer.observe(canvas, options);
इसके बाद, सीएसएस पिक्सल वैल्यू को ड्रॉइंग मेथड में पास करने से पहले, उन्हें scaleX और scaleY से गुणा करें. उदाहरण के लिए, कैनवस में ड्रॉ किए जा सकने वाले एलिमेंट को बीच में लाने के लिए:
const ctx = canvas.getContext('2d');
const card = document.getElementById('card');
canvas.onpaint = () => {
ctx.reset();
// offsetWidth and offsetHeight are in CSS pixels.
// Convert them to canvas grid coordinates.
const width = card.offsetWidth * scaleX;
const height = card.offsetHeight * scaleY;
const x = (canvas.width - width) / 2;
const y = (canvas.height - height) / 2;
// drawElementImage() takes canvas grid coordinates.
ctx.drawElementImage(card, x, y);
};
आखिर में, drawElementImage() एलिमेंट के बॉर्डर बॉक्स को ड्रॉ करता है. इसलिए, कार्ड के width और height में पैडिंग और बॉर्डर को शामिल करने के लिए, box-sizing: border-box का इस्तेमाल करें. इससे कार्ड को आपके सेट किए गए साइज़ में ड्रॉ किया जा सकेगा:
#card {
/* Make width and height include padding and border */
box-sizing: border-box;
width: 200px;
padding: 16px;
}
HTML-in-Canvas की सुलभता के बारे में जानकारी
आम तौर पर, <canvas content="drawable"> का एचटीएमएल कॉन्टेंट, सुलभता के लिए उसी तरह से एक्सपोज़ किया जाता है जिस तरह सामान्य DOM किया जाता है. इसका मतलब है कि इसे सुलभता ट्री में दिखाया जाता है. हालांकि, अगर aria-hidden="true" या inert जैसे एट्रिब्यूट का इस्तेमाल करके इसे साफ़ तौर पर छिपाया गया है, तो यह नहीं दिखेगा. हालांकि, एक अहम अपवाद है: ऐसा drawable एलिमेंट जिसकी रेंडर की गई ज्यामिति को अब तक सेट या अपडेट नहीं किया गया है.
<canvas content="drawable"> में मौजूद drawable एलिमेंट में रेंडर की गई ज्यामिति तब मौजूद नहीं होती, जब:
- इसे अब तक
drawElementImage()के साथ रेंडर नहीं किया गया है. यह डिफ़ॉल्ट रूप से, ज्यामिति को अपने-आप अपडेट करता है. - इसकी ज्यामिति को
updateElementGeometry()के साथ साफ़ तौर पर अपडेट नहीं किया गया है.
अगर किसी drawable एलिमेंट में रेंडर की गई ज्यामिति नहीं है, तो इस drawable एलिमेंट और इसके drawable सबट्री को ज्यामिति की जानकारी के बिना सुलभता ट्री में रिपोर्ट किया जाता है. उदाहरण के लिए, इससे स्क्रीन रीडर को सिमैंटिक कॉन्टेंट ऐक्सेस करने में मदद मिलती है. साथ ही, विज़ुअल असिस्टिव टूल (जैसे, मैग्निफ़ायर) को ड्रॉ नहीं किए गए एलिमेंट को फ़िल्टर करने का सिग्नल मिलता है.
यहां दिए गए उदाहरण में, इमेज और फ़िगर को अलग-अलग ड्रॉ करते समय, फ़िगर, इमेज, और कैप्शन के सिमैंटिक को सुरक्षित रखा गया है:
<canvas id="canvas" width="400" height="300" content="drawable">
<figure id="figure">
<img drawable id="image" src="..." alt="image alt text" />
<figcaption drawable id="caption">A caption</figcaption>
</figure>
</canvas>
<script>
const ctx = canvas.getContext('2d');
canvas.onpaint = () => {
ctx.reset();
// Note: Because `preserveElementGeometry` defaults to false, the
// `drawElementImage` call automatically updates geometry, ensuring
// the image and caption have geometry information for accessibility.
ctx.drawElementImage(image, 0, 0);
ctx.drawElementImage(caption, 0, 200);
};
</script>
फ़ॉलबैक रणनीतियां
HTML-in-Canvas API को Chrome 157 में लॉन्च किया गया था. ऐसा हो सकता है कि यह सभी ब्राउज़र पर काम न करे. यह पक्का करने के लिए कि आपका समाधान सभी के लिए आसानी से काम करे, देखें कि ब्राउज़र, एपीआई के साथ काम करता है या नहीं. अगर ज़रूरी हो, तो किसी दूसरे समाधान का इस्तेमाल करें:
export function supportsHtmlInCanvas() {
if (typeof document === 'undefined') return false;
const canvas = document.createElement('canvas');
canvas.setAttribute('content', 'drawable');
return canvas.content === 'drawable';
}
अगर एपीआई काम नहीं करता है, तो लागू करने के अपने लक्ष्यों के आधार पर, इनमें से कोई एक विकल्प चुना जा सकता है:
- कोई अन्य यूज़र इंटरफ़ेस (यूआई)
- फ़्लोटिंग एचटीएमएल ओवरले
- three-html-render लाइब्रेरी
- html2canvas
- dom-to-image
अगले चरण
- 2D कॉन्टेक्स्ट में एचटीएमएल रेंडर करने का तरीका जानें.
- WebGL कैनवस में एचटीएमएल रेंडर करने का तरीका जानें.
- WebGPU कैनवस में एचटीएमएल रेंडर करने का तरीका जानें.
- फ़ॉलबैक रणनीतियों के बारे में जानें.
- अपने सुझाव, शिकायत या राय शेयर करने के लिए, GitHub पर समस्या दर्ज करें.
- कम्यूनिटी के बनाए गए HTML-in-Canvas डेमो का कलेक्शन देखें.