ব্রাউজার ফিচারে ক্যানভাস কন্টেন্ট এক্সপোজ করুন

ওয়েব ফিচার: ক্যানভাসে HTML

নিয়মিত <canvas> কন্টেন্ট স্ক্রিন রিডার, ইন্ডেক্সিং, অনুবাদ টুল, অ্যাক্সেসিবিলিটি সহায়ক টুল, পৃষ্ঠায়-খুঁজুন, প্রিন্ট ইত্যাদির মতো ব্রাউজার ফিচারে দেখানো হয় না। HTML in canvas-এর মাধ্যমে, আপনি সরাসরি ক্যানভাস এলিমেন্টে আসল DOM রেন্ডার করতে পারবেন। <canvas> HTML এলিমেন্টে content="drawable" অ্যাট্রিবিউট যোগ করলে, এটি তার ডিসেন্ডেন্ট HTML এলিমেন্ট লে-আউট করে এবং drawable অ্যাট্রিবিউট সেইসব এলিমেন্টকে চিহ্নিত করে যেগুলি আপনি ক্যানভাসের রেন্ডারিং প্রসঙ্গে রেন্ডার করতে পারবেন। আপনি এটি ব্যবহার করে ক্যানভাসে টেক্সট স্টাইল ও লে-আউট করতে, ব্রাউজার ফিচারে (যেমন অ্যাক্সেসিবিলিটি, অনুবাদ বা পৃষ্ঠায় খোঁজা) ক্যানভাস কন্টেন্ট দেখাতে এবং HTML-এ 2D ও 3D এফেক্ট প্রয়োগ করতে পারবেন।

কীভাবে প্রয়োগ করতে হয়

  1. ব্রাউজারে HTML-ইন-ক্যানভাস কাজ করে কিনা তা চেক করুন:

    if ('content' in HTMLCanvasElement.prototype) {
      // Use HTML in Canvas API
    } else {
      // Use fallback strategy
    }
    
  2. <canvas> HTML এলিমেন্টে content="drawable" অ্যাট্রিবিউট যোগ করুন।

  3. <canvas> এলিমেন্টের মধ্যে আপনার HTML কন্টেন্ট রাখুন এবং আপনার আঁকা প্রতিটি এলিমেন্টে drawable অ্যাট্রিবিউট যোগ করুন। drawable সহ একটি এলিমেন্ট তার সাবট্রি ক্যাপচার করে, নেস্ট করা ডিসেনডেন্ট ছাড়া যা drawable।

    <canvas id="canvas" content="drawable">
      <div id="html-content" drawable></div>
    </canvas>
    
  4. অস্পষ্টতা এড়াতে ডিভাইসের স্কেল ফ্যাক্টরের সাথে ম্যাচ করার জন্য আপনার ক্যানভাস গ্রিড স্কেল করুন:

    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);
    
  5. canvas.onpaint ইভেন্ট হ্যান্ডলারের মধ্যে ক্যানভাসে HTML কন্টেন্ট রেন্ডার করুন। ক্যানভাসে আঁকা হয়ে যাওয়ার পরেই কোনও এলিমেন্ট আঁকা যাবে: paint ইভেন্টের অনুরোধ করতে canvas.requestPaint() কল করুন।

  • 2D কন্টেক্সটে, drawElementImage পদ্ধতি ব্যবহার করুন। এটি CSS ট্রান্সফর্মের আগে, এলিমেন্টের বর্ডার বক্স থেকে শুরু করে, drawable এলিমেন্টের শেষ স্ন্যাপশট এবং এর ড্রয়েবল সাবট্রি আঁকে:
canvas.onpaint = () => {
  ctx.reset();
  // Draw the form element at x:0, y:0
  ctx.drawElementImage(form_element, 0, 0);
};
  • WebGL এবং WebGPU প্রসঙ্গে, আপনি এলিমেন্টটি টেক্সচারে কপি করেন। canvas.captureElementImage(element) থেকে টেক্সচারের সাইজ নির্ধারণ করুন। এর width এবং height ক্যানভাস গ্রিড (ব্যাকিং স্টোর) পিক্সেলে আছে, তাই Math.ceil() দিয়ে রাউন্ড আপ করুন। এলিমেন্টে এখনও কোনও পেইন্ট রেকর্ড না থাকলে captureElementImage() থ্রো করে: requestPaint() কল করুন এবং পরবর্তী paint ইভেন্টে আবার চেষ্টা করুন।
// Returns the element's size in canvas grid pixels, or null if the element
// has no paint record yet.
function getElementImageSize(canvas, element) {
  let elementImage;
  try {
    elementImage = canvas.captureElementImage(element);
  } catch (err) {
    canvas.requestPaint(); // No paint record yet: retry on the next paint event.
    return null;
  }
  const width = Math.ceil(elementImage.width);
  const height = Math.ceil(elementImage.height);
  elementImage.close();
  return width > 0 && height > 0 ? { width, height } : null;
}
  • WebGL কন্টেক্সটে, texImage2D পদ্ধতি ব্যবহার করে প্রথমে টেক্সচার ব্যাকিং প্রি-অ্যালোকেট করুন। এই গাইডের স্নিপেট WebGL 2 কনটেক্সট (canvas.getContext("webgl2")) ব্যবহার করে। পিক্সেল ডেটার পরিবর্তে null সহ texImage2D() কল করে টেক্সচার স্টোরেজ বরাদ্দ করুন, যা কোনও পিক্সেল আপলোড না করেই প্রদত্ত সাইজ এবং ফর্ম্যাটের GPU মেমরি রিজার্ভ করে। এই নিয়মগুলি মেনে চলুন:
    • উপরে যেমন দেখানো হয়েছে, captureElementImage() থেকে টেক্সচারের সাইজ নির্ধারণ করুন।
    • শুধুমাত্র প্রথমবার আপলোড করার সময় বা সাইজ পরিবর্তন হলে লেভেল ০ বরাদ্দ করুন, কারণ আবার বরাদ্দ করলে টেক্সচার মুছে যায়। মিউটেবল স্টোরেজ ব্যবহার করুন: এলিমেন্টের সাইজ পরিবর্তন হলে ইমিউটেবল texStorage2D() স্টোরেজের সাইজ পরিবর্তন করা যায় না।
// Binds the texture, and allocates its storage on the first upload, and when
// the element size changes. `state` stores the allocated size.
function allocateElementTexture(gl, texture, state, width, height) {
  gl.bindTexture(gl.TEXTURE_2D, texture);
  if (state.width === width && state.height === height) return;

  gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // Mipmap level
    gl.RGBA8,         // Internal format: 8 bits per RGBA channel
    width,            // Size in canvas grid pixels
    height,
    0,                // Border: must be 0
    gl.RGBA,          // Format and type that match RGBA8
    gl.UNSIGNED_BYTE,
    null              // null reserves GPU memory without uploading pixels
  );
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  state.width = width;
  state.height = height;
}

const texture = gl.createTexture();
const textureState = { width: 0, height: 0 };
  • তারপর, WebGL কন্টেক্সটে, texElementSubImage2D পদ্ধতি ব্যবহার করে টেক্সচারে HTML কন্টেন্ট রেন্ডার করুন। এই নিয়মগুলি মেনে চলুন:
    • কনফিগারেশনে { width, height } পাস করুন। ক্যানভাস কীভাবে স্কেল করা হয়েছে তা নির্বিশেষে এটি কপিকে বরাদ্দ করা টেক্সচার পূরণ করে।
    • উপরে দেখানো পদ্ধতি অনুযায়ী, সাইজ পরিবর্তন হলে টেক্সচার আবার বরাদ্দ করুন।
// Uploads the element into the texture. Returns false if the element has no
// paint record yet. In that case, it's uploaded on the next paint event.
function uploadElementWebGL(gl, canvas, texture, element, state) {
  const size = getElementImageSize(canvas, element);
  if (!size) return false;
  const { width, height } = size;

  // Bind the texture, and reallocate it if the size changed (see above).
  allocateElementTexture(gl, texture, state, width, height);
  gl.texElementSubImage2D(gl.TEXTURE_2D, 0, 0, 0, element, { width, height });
  return true;
}

canvas.onpaint = () => {
  try {
    uploadElementWebGL(gl, canvas, texture, uiElement, textureState);
  } catch (err) {
    console.error('texElementSubImage2D copy failed:', err);
  }
};
  • WebGPU কন্টেক্সটে, drawElementImageToTexture পদ্ধতি ব্যবহার করুন। এই নিয়মগুলি মেনে চলুন:
    • COPY_DST ও RENDER_ATTACHMENT ব্যবহার করে টেক্সচার তৈরি করুন এবং শেডারে স্যাম্পেল করতে TEXTURE_BINDING যোগ করুন। ভুল ব্যবহারকে WebGPU যাচাইকরণ সংক্রান্ত সমস্যা হিসেবে রিপোর্ট করা হয়, ব্যতিক্রম হিসেবে নয়।
    • টেক্সচারের সাইজ size হিসেবে পাস করুন। size ছাড়া, একটি বড় এলিমেন্ট টেক্সচারে ক্লিপ করা হয়। size-এর মাধ্যমে, এলিমেন্টটি পূরণ করার জন্য স্কেল করা হয়।
    • WebGPU টেক্সচার ছোট বড় করা যাবে না। এলিমেন্টের সাইজ পরিবর্তন হলে টেক্সচার আবার তৈরি করুন এবং এটিকে রেফারেন্স করে এমন বাইন্ড গ্রুপ আবার তৈরি করুন।
// WebGPU textures can't be resized. Create the texture once, and recreate it
// when the element size changes.
function ensureElementTexture(device, state, width, height) {
  if (state.texture?.width === width && state.texture?.height === height) {
    return state.texture;
  }
  state.texture?.destroy();
  state.texture = device.createTexture({
    size: { width, height },
    format: "rgba8unorm",
    usage:
      GPUTextureUsage.TEXTURE_BINDING |
      GPUTextureUsage.COPY_DST |
      GPUTextureUsage.RENDER_ATTACHMENT,
  });
  return state.texture; // Recreate the bind groups that used the previous texture.
}

const textureState = { texture: null };

canvas.onpaint = () => {
  const size = getElementImageSize(canvas, uiElement);
  if (!size) return;
  const previousTexture = textureState.texture;
  const texture = ensureElementTexture(device, textureState, size.width, size.height);
  if (texture !== previousTexture) {
    // New texture: create, or rebuild, the bind groups that reference it.
  }
  try {
    device.queue.drawElementImageToTexture({ source: uiElement }, { texture, size });
  } catch (err) {
    console.error('drawElementImageToTexture copy failed:', err);
  }
};

দৃশ্য রেন্ডার করার জন্য requestAnimationFrame লুপ ব্যবহার করার সময়, HTML কন্টেন্ট ক্যানভাসে রেন্ডার করা হয়েছে কিনা তা নিশ্চিত করতে লুপের মধ্যে canvas.requestPaint() কল করুন। event.changedElements-এ সেইসব এলিমেন্টের তালিকা আছে যেগুলির রেন্ডারিং পরিবর্তন করা হয়েছে। বংশধর HTML এলিমেন্টে কোনও আপডেট করা হলে, আপনি শুধুমাত্র এলিমেন্টটি আবার আঁকছেন বা এর টেক্সচার আবার আপলোড করছেন কিনা তা নিশ্চিত করুন:

function render() {
  // Request to update the canvas
  canvas.requestPaint();
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

canvas.onpaint = (event) => {
  if (event.changedElements && event.changedElements.length > 0) {
    // Redraw the element, or update its texture, as shown above
  }
  // In WebGL and WebGPU, render the scene, and sync the element geometry as shown in step 6
};
  1. এলিমেন্ট জিওমেট্রি সিঙ্ক করুন।

ব্রাউজারকে জানতে হবে যে HTML এলিমেন্টটি কোথায় আঁকা হয়েছে, যাতে এর DOM পজিশন তার আঁকা পিক্সেলের সাথে মেলে। হিট টেস্টিং, যেমন ক্লিক ও টেক্সট বেছে নেওয়া এবং অ্যাক্সেসিবিলিটি এই জিওমেট্রি ব্যবহার করে: একটি drawable এলিমেন্ট যার জিওমেট্রি আপডেট করা হয়নি সেটি এখনও অ্যাক্সেসিবিলিটি ট্রিতে দেখা যায়, তবে জিওমেট্রি তথ্য ছাড়াই। ক্যানভাস শুধুমাত্র 2D drawElementImage() পদ্ধতিতে আঁকা বা updateElementGeometry()-এর সাথে রেজিস্টার করা এলিমেন্ট হিট টেস্ট করে।

  • 2D কন্টেক্সট কেসের জন্য, ব্রাউজার অটোমেটিক এলিমেন্ট সিঙ্ক করে। drawElementImage() DOM হিট-টেস্ট বাউন্ড এবং স্ক্রিন রিডার ফোকাস রিংকে আঁকা কোঅর্ডিনেটের সাথে অ্যালাইন করে, তাই আপনাকে style.transform সেট করতে হবে না। ম্যানুয়ালি জিওমেট্রি ম্যানেজ করতে, যেমন, আপনি যখন একই এলিমেন্ট একাধিকবার আঁকেন, তখন { preserveElementGeometry: true } থেকে drawElementImage()-এ পাস করুন এবং canvas.updateElementGeometry()-এর মাধ্যমে ট্রান্সফর্ম প্রয়োগ করুন।

  • WebGL বা WebGPU সহ 3D কেসের জন্য, ক্যানভাস বলতে পারে না যে HTML এলিমেন্ট কোথায় আঁকা হয়েছে, তাই আপনাকে এটি বলতে হবে। একটি DOMMatrix গণনা করুন যা CSS পিক্সেলে এলিমেন্টের বর্ডার বক্সকে CSS পিক্সেলে ক্যানভাসে ম্যাপ করে। ব্রাউজারকে ভিউপোর্ট ট্রান্সফর্ম ব্যবহার করে 3D কোঅর্ডিনেট স্পেস থেকে CSS কোঅর্ডিনেট স্পেসে ম্যাপ করতে হবে। এটি করার জন্য, নিম্নলিখিত ধাপগুলি অনুসরণ করুন:

    • HTML এলিমেন্ট স্বাভাবিক করুন। HTML এলিমেন্টের সাইজ পিক্সেলে (যেমন, ২০০ পিক্সেল চওড়া) পরিমাপ করা হয়। তবে, WebGL সাধারণত অবজেক্টকে "ইউনিট স্কোয়ার" হিসেবে বিবেচনা করে, যেমন, -০.৫ থেকে ০.৫ পর্যন্ত। আপনি যদি নরমালাইজ না করেন, তাহলে আপনার ২০০ পিক্সেলের বোতামটি ২০০ গুণ বড় দেখাবে। এই ধাপটি Y-অ্যাক্সিসকেও ফ্লিপ করে, কারণ CSS-এ, নিচের দিকটি পজিটিভ, কিন্তু WebGL-এ, উপরের দিকটি পজিটিভ।
    • WebGL MVP ম্যাট্রিক্সকে DOM ম্যাট্রিক্সে কনভার্ট করুন।
    • ক্যানভাস ভিউপোর্টে ম্যাপ করুন। এই ধাপটি হল "রি-স্কেলিং" ফেজ: এটি স্ক্রিনে আপনার <canvas> এলিমেন্টের CSS পিক্সেল ডাইমেনশনের সাথে ম্যাচ করার জন্য ইউনিট-স্পেস ম্যাথকে আবার স্ট্রেচ করে এবং Y-অ্যাক্সিসকে আবার ফ্লিপ করে।
    • চূড়ান্ত ট্রান্সফর্ম হিসেব করুন। এই ক্রমে ম্যাট্রিক্স গুণ করুন: ভিউপোর্ট * MVP * নরমালাইজেশন। এগুলিকে একটি চূড়ান্ত ট্রান্সফর্মে একত্রিত করলে একটি "ম্যাপ" তৈরি হয় যা ব্রাউজারকে ঠিক কোথায় সেই HTML এলিমেন্টটি বসাতে হবে তা বলে দেয় যাতে এটি 3D ড্রয়িংয়ের সাথে অ্যালাইন হয়। ব্রাউজার পার্সপেক্টিভ ডিভাইড পারফর্ম করে।
    // Maps the element's border box (CSS pixels) to the canvas (CSS pixels), for a
    // quad that spans -0.5 to 0.5 in model space and is drawn with the `mvp` matrix.
    function computeCanvasTransform(canvas, element, mvp) {
      const width = element.offsetWidth;
      const height = element.offsetHeight;
    
      // 1. Normalize the HTML element (CSS pixels -> WebGL Model Space)
      const toGLModel = new DOMMatrix()
        // Scale pixels to 1 unit, flip Y (as in CSS it points down, and in WebGL it points up)
        .scale(1 / width, -1 / height, 1 / height)
        // Center the origin: (0,0) becomes (-width/2, -height/2) before scaling
        .translate(-width / 2, -height / 2);
    
      // 2. Convert WebGL MVP Matrix to DOM Matrix
      const mvpDOM = new DOMMatrix(Array.from(mvp));
    
      // 3. Map to the canvas viewport, in CSS pixels
      const clipToCanvasViewport = new DOMMatrix()
        // Move center (0,0) to center of canvas
        .translate(canvas.clientWidth / 2, canvas.clientHeight / 2)
        // Scale normalized clip (-1..1) to viewport size, and flip Y back
        .scale(canvas.clientWidth / 2, -canvas.clientHeight / 2, canvas.clientHeight / 2);
    
      // 4. Multiply: (Clip -> Pixels) * (MVP) * (pixels -> unit square)
      return clipToCanvasViewport.multiply(mvpDOM).multiply(toGLModel);
    }
    

তারপর canvasTransform হিসেবে canvas.updateElementGeometry()-এ ট্রান্সফর্ম পাস করুন। এটি HTML এলিমেন্টকে সরাসরি তার রেন্ডার করা পিক্সেলের উপরে নিয়ে যায়। এর ফলে, ব্যবহারকারী কোনও বোতামে ক্লিক করলে বা টেক্সট বেছে নিলে, তিনি আসলে আসল HTML এলিমেন্টেই ক্লিক করছেন কিনা তা নিশ্চিত করা যায়। এছাড়াও, ব্রাউজার এলিমেন্টের অ্যাক্সেসিবিলিটি বাউন্ডের জন্য ট্রান্সফর্ম ব্যবহার করে, যেমন, স্ক্রিন রিডার ফোকাস রিংয়ের জন্য:

  canvas.updateElementGeometry(targetHTMLElement, {
    canvasTransform: computeCanvasTransform(canvas, targetHTMLElement, htmlElementMVP),
  });

হিট টেস্টিং কন্ট্রোল করতে এইসব বিকল্প ব্যবহার করুন:

  • প্রতিটি updateElementGeometry() কল এলিমেন্টকে হিট-টেস্টিং অর্ডারের সবচেয়ে উপরে নিয়ে যায়। { preserveHitTestOrder: true }-কে তার পজিশনে রাখতে পাস করুন।
  • হিট টেস্টিং থেকে কোনও এলিমেন্ট সরাতে canvas.clearElementGeometry(element) কল করুন, যেমন, আপনি যখন এটি লুকান।

কোডের উদাহরণ

2D ক্যানভাস

<canvas id="canvas" content="drawable" style="width: 400px; height: 200px;">
  <div id="ui-element" drawable>
    <p>
      This text is rendered inside the canvas but is present in the DOM tree.
    </p>
    <input type="email" name="email" placeholder="enter your email" />
    <button type="button">Submit</button>
  </div>
</canvas>

<script>
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");
  const uiElement = document.getElementById("ui-element");

  canvas.onpaint = () => {
    ctx.reset();
    // Draw the HTML element at x:0, y:0. The browser syncs the DOM location
    // with the drawn location.
    ctx.drawElementImage(uiElement, 0, 0);
  };

  // Handle resizing to match device pixels
  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);
    canvas.requestPaint();
  });

  const supportsDevicePixelContentBox =
    typeof ResizeObserverEntry !== "undefined" &&
    "devicePixelContentBoxSize" in ResizeObserverEntry.prototype;
  const options = supportsDevicePixelContentBox
    ? { box: "device-pixel-content-box" }
    : {};
  observer.observe(canvas, options);
</script>

WebGL ক্যানভাস

<canvas id="canvas" content="drawable" style="width: 400px; height: 400px;">
  <div id="ui-element" drawable>
    <p>WebGL UI Element</p>
    <button>Action</button>
  </div>
</canvas>

<script>
  const canvas = document.getElementById("canvas");
  const gl = canvas.getContext("webgl2");
  const uiElement = document.getElementById("ui-element");

  // getElementImageSize(), allocateElementTexture(), and uploadElementWebGL()
  // are defined in step 5.
  // computeCanvasTransform() is defined in step 6.

  // Setup WebGL texture...
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  const textureState = { width: 0, height: 0 };

  canvas.onpaint = () => {
    // 1. Update texture with HTML content
    try {
      uploadElementWebGL(gl, canvas, texture, uiElement, textureState);
    } catch (err) {
      console.error('texElementSubImage2D copy failed:', err);
    }

    // ... Render your 3D scene here, calculating htmlElementMVP matrix ...

    // 2. Sync DOM position, hit testing, and accessibility bounds with 3D scene
    canvas.updateElementGeometry(uiElement, {
      canvasTransform: computeCanvasTransform(canvas, uiElement, htmlElementMVP),
    });
  };
</script>

WebGPU ক্যানভাস

<canvas id="canvas" content="drawable" style="width: 400px; height: 400px;">
  <div id="ui-element" drawable>
    <p>WebGPU UI Element</p>
  </div>
</canvas>

<script>
  const canvas = document.getElementById("canvas");
  const context = canvas.getContext("webgpu");
  const uiElement = document.getElementById("ui-element");

  // getElementImageSize() and ensureElementTexture() are defined in step 5.
  // computeCanvasTransform() is defined in step 6.

  // Setup WebGPU...
  // const device = ...
  const textureState = { texture: null };

  canvas.onpaint = () => {
    // 1. Copy HTML content to a texture that matches the element size
    const size = getElementImageSize(canvas, uiElement);
    if (size) {
      const previousTexture = textureState.texture;
      const texture = ensureElementTexture(device, textureState, size.width, size.height);
      if (texture !== previousTexture) {
        // New texture: create, or rebuild, the bind groups that reference it.
      }
      try {
        device.queue.drawElementImageToTexture({ source: uiElement }, { texture, size });
      } catch (err) {
        console.error('drawElementImageToTexture copy failed:', err);
      }
    }

    // ... Render your 3D scene here, calculating htmlElementMVP matrix ...

    // 2. Sync DOM position, hit testing, and accessibility bounds (same matrix math as WebGL)
    canvas.updateElementGeometry(uiElement, {
      canvasTransform: computeCanvasTransform(canvas, uiElement, htmlElementMVP),
    });
  };
</script>

পেশাদার পদ্ধতি

  • বাধ্যতামূলক: HTML-in-Canvas API ব্যবহার করার আগে ব্রাউজার সাপোর্ট করে কিনা তা চেক করুন।
  • বাধ্যতামূলক: <canvas> এলিমেন্টে সবসময় content="drawable" অ্যাট্রিবিউট যোগ করুন।
  • বাধ্যতামূলক: ক্যানভাসের ডাইরেক্ট চাইল্ড সহ আপনার আঁকা প্রতিটি এলিমেন্টে drawable অ্যাট্রিবিউট যোগ করুন।
  • বাধ্যতামূলক: ক্যানভাসে HTML কন্টেন্ট রেন্ডার করতে onpaint ইভেন্ট হ্যান্ডলার ব্যবহার করুন।
  • বাধ্যতামূলক: ক্যানভাসে HTML কন্টেন্ট রেন্ডার করতে, 2D-তে drawElementImage পদ্ধতি বা WebGPU-তে drawElementImageToTexture পদ্ধতি ব্যবহার করুন।
  • বাধ্যতামূলক: WebGL-এ, একবার texImage2D()-এর সাথে টেক্সচার বাফার প্রি-অ্যালোকেট করুন (যখন এটি শুরু করা হয় বা যখন এলিমেন্টের সাইজ পরিবর্তিত হয়)। শুধুমাত্র সাইজ পরিবর্তন হলে টেক্সচার আবার বরাদ্দ করুন এবং TEXTURE_MIN_FILTER-কে LINEAR-এ সেট করুন।
  • বাধ্যতামূলক: WebGL-এ, texElementSubImage2D পদ্ধতি ব্যবহার করে HTML কন্টেন্টকে টেক্সচারে রেন্ডার করুন যা আপনি আগে থেকে texImage2D()-এর মাধ্যমে বরাদ্দ করেছেন, যার সাইজ captureElementImage() থেকে শুরু হয়।
  • বাধ্যতামূলক: WebGPU-তে, captureElementImage() থেকে টেক্সচারের সাইজ নির্ধারণ করুন এবং সাইজ পরিবর্তন হলে তবেই এটি আবার তৈরি করুন। COPY_DST এবং RENDER_ATTACHMENT ব্যবহার করে টেক্সচার তৈরি করুন এবং টেক্সচারের সাইজ size হিসেবে পাস করুন।
  • বাধ্যতামূলক: রেন্ডার করা কন্টেন্টের সাথে HTML এলিমেন্টের জিওমেট্রি সিঙ্ক করুন। 2D-তে, drawElementImage এটি অটোমেটিক করে। WebGL এবং WebGPU-তে, canvas.updateElementGeometry(element, { canvasTransform }) কল করুন।
  • বাধ্যতামূলক: স্ক্রিনের সাইজ লক্ষ্য করতে এবং ডিভাইসের পিক্সেলের সাথে ম্যাচ করার জন্য ক্যানভাসের সাইজ আপডেট করতে ResizeObserver ব্যবহার করুন।
  • ক্যানভাসে ক্রস-অরিজিন কন্টেন্ট এম্বেড করবেন না, কারণ এটি কাজ করে না।
  • onpaint ইভেন্ট হ্যান্ডলারের মধ্যে ResizeObserver ইনিশিয়ালাইজ করবেন না, কারণ এর ফলে মেমরি লিক হতে পারে।

ফলব্যাক স্ট্র্যাটেজি

HTML-in-Canvas API বর্তমানে সব আধুনিক ব্রাউজারে কাজ করে না, তাই সাধারণত একটি ফলব্যাক স্ট্র্যাটেজি প্রয়োজন হয়।

তবে, এই API-এর উন্নত পারফর্ম্যান্সের সুবিধা বিবেচনা করে, ব্রাউজার এটি সাপোর্ট করলে HTML-in-Canvas ব্যবহার করা উচিত।

ফলব্যাক স্ট্র্যাটেজি ব্যবহারের উপর নির্ভর করে। যেমন, ক্যানভাসে ইন্টার‍্যাক্টিভ HTML কন্টেন্টের জন্য, HTML-ইন-ক্যানভাস কাজ না করলে, CSS ব্যবহার করে ক্যানভাসের উপরে HTML কন্টেন্ট রাখুন।

HTML-ইন-ক্যানভাস পলিফিল

যেসব ব্রাউজারে HTML-ইন-ক্যানভাস API কাজ করে না, সেখানে এটি নকল করতে নিম্নলিখিত পলিফিল স্ক্রিপ্ট ব্যবহার করুন।

  1. লাইব্রেরি ইনস্টল বা এম্বেড করুন:

    # Install
    npm install three-html-render
    
    # Embed
    <script src="https://cdn.jsdelivr.net/npm/three-html-render/dist/polyfill.js"></script>
    
  2. HTML-in-Canvas অনুবাদ করতে installHtmlInCanvasPolyfill() পদ্ধতি রান করুন।