tsayen / dom-to-image

Generates an image from a DOM node using HTML5 canvas
Other
10.32k stars 1.68k forks source link

Uncaught (in promise) #181

Closed lixiaoran0716 closed 6 years ago

lixiaoran0716 commented 6 years ago

function feedback() { $("#suggestion").val(""); var feedBackNode = document.body; domtoimage.toPng(feedBackNode).then(function (dataUrl) { imgDataUrl = dataUrl; var widthO = feedBackNode.offsetWidth; var heightO = feedBackNode.offsetHeight; var v = 1; var max = 550; if (widthO > heightO) { if (widthO > max) { v = max / widthO; } } else { if (heightO > max) { v = max / heightO; } } var img = new Image(); img.src = dataUrl; $("#feedback").empty(); $("#feedback").append($(img).css("width", widthO v).css("height", heightO v)); }) $("#feedbackModal").modal("show"); }

image

wangweiguo2013 commented 4 years ago

How did you solove this problem?

gvsakhil commented 4 years ago

same issue in 2020

mujtaba-quo commented 3 years ago

Any solution to this issue?

malohtie commented 3 years ago

same in 2021 there s no way to debug this error

mujtaba01 commented 3 years ago

In my case, this was happening because when I was converting multiple dom elements to image in parallel. The issue got resolved after I modified the code and made it sequential.

sakhibgareevrr commented 3 years ago

Same here - not clear why Uncaught (in promise). Not clear why this error happens

parag-coditation commented 3 years ago

Any updates on the solution for this issue ? @lixiaoran0716 if you found any solution please share it with us. Any help will be appreciated.

ggrunwald97 commented 2 years ago

Hi everyone, I ran into this error and there was no clear solution or error message to help debug it so I'm logging my solution here. Hopefully it helps someone down the line.

  const filterNode = node => {
    if (node instanceof Text) {
      return true;
    }
    return !['img'].includes(node.tagName.toLowerCase()) || /^h[123456]$/i.test(node.tagName);
  };

  const node = document.getElementById(id);
  const nodeCloned = node.cloneNode(true);

  domtoimage
    .toBlob(nodeCloned, { filter: filterNode })
    .then(blob => {
      navigator.clipboard.write([
        new ClipboardItem(
          Object.defineProperty({}, blob.type, {
            value: blob,
            enumerable: true,
          }),
        ),
      ]);
    })
    .catch(error => {
      console.log('there was an error', error);
      // ... do catch logic
    });

What I found the issue was for me (this filter solution was on another issue, link here: https://github.com/tsayen/dom-to-image/issues/175#issuecomment-359702807) is that I needed to filter the node before it would let me create the blob. So this part in particular is important: { filter: filterNode }. And then in the filterNode function, I am filtering out just 'img' type tags, since those seem to be the only ones which broke .toBlob. In the code above I am creating the blob and then copying it to clipboard.

Gnietschow commented 2 years ago

I ran into that issues too and tracked it down that is was a timeout problem for me. I tried to export a very large DOM with around 20k elements which apparently took too long.

Internally the html code is put serialized as <foreignObject> into a <svg> and the converted by the method makeImage into an image. The uncaught error came from the onerror callback. As smaller DOMs were working, I assumed that this image loading the <svg> container simply took too long.

I resolved the issue with two measures:

  1. I filtered more aggressively unused elements and reduced the size of the DOM.
  2. I added a style property filter as all styles even default ones get inlined for each element, bloating the resulting code if there are a lot elements. Probably you could write a more intelligent algorithm only saving style changes from default, but in my case as I had mainly <path> elements, I used a whitelist. Therefore I made those changes:

var domtoimage = {
        ...
        impl: {
            ...
            property_whitelist: {},
        }
    };

[...]

function copyProperties(source, target) {
    let properties = util.asArray(source);
    if (original.tagName.toLowerCase() in domtoimage.impl.property_whitelist)
        properties = properties.filter(name => name in domtoimage.impl.property_whitelist[original.tagName.toLowerCase()]);
    properties.forEach(function (name) {
        target.setProperty(
            name,
            source.getPropertyValue(name),
            source.getPropertyPriority(name)
        );
    });
}

and then setting it up in my code with

domtoimage.impl.property_whitelist = {
    'path': Object.assign(...['fill', 'fill-opacity', 'stroke-width', 'stroke', 'stroke-linecap', 'stroke-linejoin', 'stroke-width', 'stroke-opacity', 'shape-rendering'].map(k => ({ [k]: true })));
};
frontierFlight commented 1 year ago

I also encountered the same problem while working on the Angular project, but I have already resolved it. Just use the parent element of the svg tag when passing in domtoimage. toPng.