Search code examples
javascripttypescripthtml5-canvasmobile-safari

Issues with drawing multiple images onto a canvas within Safari iOS


I am currently running into an issue with drawing multiple images onto a canvas that only happens on Safari iOS. What I'm trying to do is horizontally merge multiple user-uploaded pictures and upload that merged image into an S3 bucket, and I want to preview this merged image to the user before uploading the file.

Here is how I'm drawing the images to the canvas:

async function drawImagesToCanvas(files: FileList) {
  const images = await Promise.all(
    Array.from(files).map(async (file) => {
      const image = new Image();
      image.src = URL.createObjectURL(file);
      await image.decode();
      return image;
    })
  );

  const canvas = document.getElementById("canvas");
  const { maxWidth, maxHeight } = images.reduce((acc, image) => ({
    maxWidth: acc.maxWidth + image.width,
    maxHeight: image.height >= acc.maxHeight ? image.height : acc.maxHeight,
  }));
  canvas.width = maxWidth;
  canvas.height=  maxHeight;

  const ctx = canvas.getContext("2d");
  let xOffset = 0;

  images.forEach((image) => {
    ctx.drawImage(image, xOffset, 0, image.width, image.height);
    xOffset += image.width;
  });
}

The desktop versions of Chrome, Firefox, and Safari are able to display the canvas just fine, and Android Firefox and Android Chrome displays the canvas just fine as well. On Safari iOS however, the canvas appears blank. I am only encountering this error on Safari iOS, and this issue only arises when more than one image is needed to be drawn onto the canvas. The reason why this is an issue for me is because when it's time to upload the merged image to an S3 bucket via a presigned URL, calling the canvas.toBlob() function fails on Safari iOS when more than one image is drawn to the canvas.

One thing that might be important to mention is that the images the user can upload can be large as they can use their device's camera to take and upload that picture. For the requirements I am facing, it is required that I try to keep original image dimensions when combining the images, and have as the highest quality image possible.

Is there any reason why the canvas is behaving like this on Safari iOS? If there is a reason why this is happening only on Safari iOS, are there any workarounds this issue?


Solution

  • After some investigating, it turns out that the issue was related to the sizes of the images. HTML5 Canvass have a maximum size, and this maximum size varies between browsers and also varies depending on the hardware available, as noted by user @jhildenbiddle in the linked StackOverflow answer. In my case, the images that are expected to be uploaded can be quite large and when merging them horizontally I'll most likely exceed the maximum size a majority of the time, causing the canvas.toBlob() to fail.

    For my use case , keeping the original dimensions of each image is required and I've made the decision to combine the images on the backend. I've opted to just use regular <img> elemnts for each uploaded image and display that to the user before submitting.

    It seems like you can determine the maximum canvas size using the npm package canvas-size (created by @jhildenbiddle), so if you needed to know the maximum canvas size at runtime this is available for you.