bubkoo / html-to-image

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

Generating a Blank Image in Safari Browser #199

Closed ahmedjamshed closed 2 years ago

ahmedjamshed commented 2 years ago

This package is generating a blank image in the Safari browser but it's working good in Chrome and Firefox

Expected Behavior

It should work as good in Safari as well, as it is working on Chrome and Firefox

Current Behavior

Generating a blank image in Safari

Your Environment

Code

  const onSaveClick = async () => {
    try {
      const avatar = document.querySelector("#avatar") as HTMLElement;
      const image = document.querySelector(".traitLayer") as HTMLElement;
      if (avatar === null) {
        return
      }
      const imageConfig = {
        cacheBust: true, pixelRatio: 1, height: image.clientHeight, quality: 0.98,
        width: image.clientWidth, canvasWidth: 3000, canvasHeight: 3000,
      }
      const dataUrl = await toPng(avatar, imageConfig)
      const link = document.createElement('a')
      link.download = 'Avatar.png'
      link.href = dataUrl
      link.click()

    }  catch (err: any) {
      // Nothing here
    }
  }
vivcat[bot] commented 2 years ago

👋 @ahmedjamshed

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.

branlok commented 2 years ago

For me as well, both Firefox and Safari was behaving differently from from expected behaviour in Chrome. The snapshot I try to create would not include the background images of a div whether they are local or external image URL. However, when I manually use the equivalent of your onSaveClick the second time, it would correctly generate my target div with the expected background images.

I tested with duplicating this bit of code, discarding the first generation, and take the second. This workaround actually fixes Firefox. However, Safari seems to still be the same.

      const imgBase64_dump = await toPng(captureRef.current, {
        quality: 0.01,
        pixelRatio: 1,
        height: captureRef.current.scrollHeight,
        canvasWidth: captureRef.current.scrollWidth,
        canvasHeight: captureRef.current.scrollHeight,
      });

      const imgBase64_again = await toPng(captureRef.current, {
        quality: 1,
        pixelRatio: 1,
        height: captureRef.current.scrollHeight,
        canvasWidth: captureRef.current.scrollWidth * 2,
        canvasHeight: captureRef.current.scrollHeight * 2,
      });

      download(imgBase64_again, "generated.png", "image/png");
tdida commented 2 years ago

similar problems

mahen23 commented 2 years ago

I had similar problems on ionic. Then used setTimeout with a delay of 2,000 milliseconds. My images are now generating fine.

sifat009 commented 2 years ago

@branlok Did you find any workaround for safari? I'm having the same issue 😞

branlok commented 2 years ago

@sifat009

I realize readme file does mention "Safari is not supported, as it uses a stricter security model on tag. Suggested workaround is to use toSvg and render on the server."

However, in another attempt to patch a fix for Safari, adding a custom style did help reveal my images that were nested within my target div from first click to download.

 const imgBase64_again = await toPng(captureRef.current, {
        quality: 1,
        pixelRatio: 1,
        height: captureRef.current.scrollHeight,
        canvasWidth: captureRef.current.scrollWidth * 2,
        canvasHeight: captureRef.current.scrollHeight * 2,
        style: {backgroundColor: "transparent"}, //!! this part
      });

Albeit this only worked for local bg images, the same issues occur when i dynamically add external image url.

I suppose try playing around with the styling.

vince760 commented 2 years ago

I have had the same issue and have found somewhat of a workaround in Safari:

` const safariDownload = async () => {

await htmlToImage.toCanvas(document.getElementById('Element you are wanting to download'))
.then((canvas) => {

 const context = canvas.getContext('2d');
 const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
  if (imageData.data[0] !== 0) {
    canvas.id = *Give your canvas and ID*
    document.body.appendChild(canvas);
     canvasToImage('Name of the canvas ID you assigned on canvas.id', {
       name: `Name you want given to the image hen downloaded`,
       type: 'png',
       quality: 1,
     })
     document.body.removeChild(canvas)
  } else {
    safariDownload()

  }

})

}`

Essentially what I did was to create a canvas, added a validation check on that canvas to check for the pigmentation of the returned canvas. If the first element in the array of context is 0 meaning is has no pigmentation then the function runs again. For some reason htmlToImage.toCanvas in Safari doesn't work the first run it works on the second run.

Then temporarily added that canvas to the dom.

Then I used canvasToImage to download it.

Then removed the canvas once the download is complete.

This is not the best solution for this, but until they can rework the Safari issue it is working 100% of the time for me.

vivcat[bot] commented 2 years ago

Hiya! This issue has gone quiet. Spooky quiet. 👻 We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 20 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not-stale" to keep this issue open! As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request.

Thanks for being a part of the Antv community! 💪💯

AMandujano-BI commented 2 years ago

Same problem here.

khojabergen commented 2 years ago

Same problem

mrhieu commented 2 years ago

After specifying width and height attributes of the img tag, the issue has gone. You guys can try that.

imbarwinata commented 2 years ago

Same problem

pranavatoito commented 2 years ago

same problem for me too.

GeekyADAMS commented 2 years ago

This works for me:

https://github.com/bubkoo/html-to-image/issues/214#issuecomment-1002640937

GeekyADAMS commented 2 years ago

Well, as far as I can tell, this is the best solution for now. The problem is that IOS and MacOS find it hard to “paint” the details of an html content when converting to any image format. You would think that adding enough delay to make sure content is fully rendered would solve the problem but it doesn’t, what works is attempting conversion multiple times in a chain, to make sure it captures all the details.

So depending on the level of details in your content, you might need to increase the number of conversion chains and maybe add a lil bit more delay. Maybe a sane thing to do is to create that detect IOS and MacOS devices and then take in a number for how many repeated conversion attempts you want depending on the details of your content to make sure it works every time.

As a matter of fact, this can be demoed manually by attempting one conversion and download process after another and see how the level of details captured increases.

Maybe the library maintainer can incorporate this in a fix release.

vivcat[bot] commented 2 years ago

Hiya! This issue has gone quiet. Spooky quiet. 👻 We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 20 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not-stale" to keep this issue open! As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request.

Thanks for being a part of the Antv community! 💪💯

hlmartin commented 2 years ago

Building off what @vince760 and @GeekyADAMS posted, I came up with this solution which will check the top left and bottom right pixels and retry until they differ. Obviously, this works best when the image you are rendering doesn't have solid colours around the outside, however if you adjust the max attempts you might be able to catch this without wasting too much time.

function safariDownload(attempts) {
  const element = document.getElementById('Element you are wanting to download');
  htmlToImage.toCanvas(element).then(function(canvas) {
    const context = canvas.getContext('2d');
    const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
    if (!pixelsAreEqual(imageData) || attempts >= 10) {
    canvas.id = *Give your canvas an ID*;
    document.body.appendChild(canvas);
    canvasToImage('Name of the canvas ID you assigned on canvas.id', {
       name: `Name you want given to the image hen downloaded`,
       type: 'png',
       quality: 1,
     });
     document.body.removeChild(canvas);
    } else {
      console.log(`Trying Safari rendering again (attempt #${attempts + 1})...`);
      setTimeout(() => (safariDownload(card, options, attempts + 1)), 1000);
    }
  });
}

function pixelsAreEqual(imageData) {
  const len = imageData.data.length;
  const topLeftPixelRgba = [imageData.data[0], imageData.data[1], imageData.data[2], imageData.data[3]];
  const bottomRightPixelRgba = [imageData.data[len - 4], imageData.data[len - 3], imageData.data[len - 2], imageData.data[len - 1]];
  return (topLeftPixelRgba.every((v,i)=> v === bottomRightPixelRgba[i]));
}
vivcat[bot] commented 2 years ago

Hiya! This issue has gone quiet. Spooky quiet. 👻 We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 20 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not-stale" to keep this issue open! As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request.

Thanks for being a part of the Antv community! 💪💯

vivcat[bot] commented 2 years ago

Hey again! It’s been 60 days since anything happened on this issue, so our friendly neighborhood robot (that’s me!) is going to close it. Please keep in mind that I’m only a robot, so if I’ve closed this issue in error, I’m HUMAN_EMOTION_SORRY. Please feel free to comment on this issue or create a new one if you need anything else. As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Thanks again for being part of the Antv community! 💪💯

vivcat[bot] commented 1 year ago

This thread has been automatically locked because it has not had recent activity. Please open a new issue for related bugs and link to relevant comments in this thread.