bubkoo / html-to-image

✂️ Generates an image from a DOM node using HTML5 canvas and SVG.
MIT License
5.65k stars 524 forks source link

Can not capture the deault content inside a slot, and styling of progress bar is missing #470

Open cWenyu opened 1 month ago

cWenyu commented 1 month ago

Missing default slot content and progress element styling

I have a sample page that includes basic HTML elements, shadow dom, slots, and a progress bar, and I want to convert the DOM element into an image but the result is wrong. The default content (text, div) inside the slot is missing and also the styling of the progress bar is wrong.

Expected Behavior

Convert the default slot content and capture the progress bar styling, as image below: image

Current Behavior

htmlToImage (1)

Possible Solution

To solve the content inside the slot, need to check if the slot element has child elements and attach theme to children Array: https://github.com/bubkoo/html-to-image/blob/128dc3edfde95d6ac636f2756630f5cbd6f7c8df/src/clone-node.ts#L78

I don't have the solution yet for the progress bar.

Steps To Reproduce

Copy the following code into index.html and open the file in Chrome or Firefox, click button, and an image will be downloaded.

<!DOCTYPE html>
<html>

<head>
  <title>Copy HTML to Clipboard</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js"></script>
  <style>
    #content-to-copy {
      background-color: white;
      width: 300px;
      height: fit-content;
      border: 1px solid #000;
      padding: 20px;
    }

    h1 {
      background-color: blue;
      color: chartreuse;
    }

    #shadow-host {
      margin: 20px 0;
    }

    #lightdom-div {
      background-color: #000;
      color: aliceblue;
    }
  </style>
</head>

<body>
  <div id="content-to-copy">
    <h1>Hello World</h1>
    <p>This is a sample content with <strong>HTML</strong> elements.</p>
    <div id="shadow-host"></div>

    <!-- Define the custom element -->
    <my-element>
      <div slot="content" id="lightdom-div">Slotted div element inside custom element shadow dom</div>
    </my-element>

    <!-- progress tag -->
    <label for="file">Progress bar</label>
    <progress id="file" value="32" max="100"> 32% </progress>
  </div>
  <button id="download-button">Download HTML as Image</button>
  <script>

    // Define a class for the custom element
    class MyElement extends HTMLElement {
      constructor() {
        super();
        const shadow = this.attachShadow({ mode: 'open' });
        shadow.innerHTML = `
        <style>
            div {
              color: blue;
              border: 1px solid black;
              padding: 10px;
              margin: 10px;
            }

            slot[name='title'] > *{
              color: purple;
              background-color: aqua;
            }
          </style>

          <span>Custom Element</span>
          <div>
            <slot name="title">
              <div>A div element inside title slot.</div>
            </slot>

            <slot name="content"></slot>

            <slot name="footer">Default footer content</slot>
          </div>
        `;
      }
    }

    // Register the custom element
    customElements.define('my-element', MyElement);

    const body = document.querySelector('body');

    const shadowHost = document.querySelector('#shadow-host');
    const shadowRoot = shadowHost.attachShadow({ mode: 'open', slotAssignment: 'manual' });

    // shadow div
    const shadowContent = document.createElement('div');
    shadowContent.id = 'shadow-content';
    shadowContent.textContent = 'Attached shadow DOM content';

    // append slot elements
    shadowRoot.appendChild(shadowContent);

    const content = document.querySelector('#content-to-copy');

    function downloadElementAsImg() {
      htmlToImage.toCanvas(content).then(canvas => {

        // create a link and download image
        const a = document.createElement('a');

        canvas.toBlob((blob) => {
          const url = URL.createObjectURL(blob);

          a.download = "htmlToImage.png";
          a.href = url;
          a.style.display = 'none';
          body.appendChild(a);
          a.click();

          body.removeChild(a);
        });
      });
    }

    document.getElementById('download-button').addEventListener('click', downloadElementAsImg);
  </script>
</body>

</html>

Your Environment

vivcat[bot] commented 1 month ago

👋 @cWenyu

Thanks for opening your first issue here! If you're reporting a 🐞 bug, please make sure you include steps to reproduce it. To help make it easier for us to investigate your issue, please follow the contributing guidelines.

We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can.