Search code examples
javascriptcanvasdrawingchromiumimage-scaling

Why canvas loses scaling while drawing images on it?


I set up a canvas and apply a setTransform to scale it smaller for specific devices. On one chromium 55 device this canvas first works correctly but after some drawings it suddenly first disappears from view and after returning it continues drawing without the scaling. What could be the reason?

While drawing I use drawImage. I was able to figure out that the problem that breaks is within the step that draws data from images to the canvas, but I don't have any good ideas how to debug or overcome the issue.

So steps are

  1. Set up canvas, set scaling if needed (scaling is from 1080p to 720p)
  2. Draw images on canvas
  3. Clear canvas
  4. Draw new images on canvas
  5. Something changes, the image clears and when it redraws, the scaling is not applied anymore

I have set up different checks to handle the asynchronous processes and possible errors, but I don't get any errors. I tried to figure out if it could be due to memory issues, but there seems to be no significant problems when I run it on dev. The error happens sooner if I draw more frequently, so I get to do less drawing if I do it faster before the bug. The amazing thing is that this problem had existed earlier and was thought to be corrected, but while refactoring it reappeared. Last time it just stopped occurring. I compared the changes and no significant logic is changed. In refactoring I had to change sizes, and divide code into different code blocks, but I retraced steps and the logic is the same.

Any ideas I could try?

This is not a minimum reproducible as bug is not occurring on regular pc browsers:

const scaleFactor = 1280/1920
const ctx = element.getContext('2d')
  ctx.setTransform(
    scaleFactor,
    0,
    0,
    scaleFactor,
    0,
    0
  )
  ctx.save()

 //Here we do some other stuff e.g. fetch data. Then we call this function
 drawImage(
      ctx,
      centerX,
      centerY,
      imgWidth,
      imgHeight,
      source,
      imageElement,
      checksum
    ) {
      return new Promise((resolve) => {
        let broken = false
        try {
          if (
            imageElement &&
            imageElement.getAttribute('data-loaded') === 'true'
          ) {
            ctx.drawImage(
               imageElement, centerX, centerY, imgWidth, imgHeight
            )
            if (checksum) 
               imageElement.setAttribute('data-checksum', checksum)
            return resolve(imageElement)
          }
        } catch (e) {
          // The image element might be in a "broken" state and the draw fails
          broken = true
        }

        const img = this.cacheImage(
          source,
          imgWidth,
          imgHeight,
          checksum,
          (image) => {
            if (
              !checksum ||
              image.getAttribute('data-checksum') ===
                this.renderChecksum.toString()
            ) {
              ctx.drawImage(image, centerX, centerY, img.width, img.height)
              resolve(image)
            }
          },
          broken
        )
      })
    },
//Before redrawing we call 

ctx.clearRect()

Solution

  • I figured it out. The problem was that the canvas itself was not scaled accordingly, so this one old browser started malfunctioning while trying to draw transformed data on it. The issue gets resolved by scaling the width and height of the canvas with the scaleFactor before setting the transform.

    Issue was hard to solve as all other devices we have tested do not have any issues with drawing data in the corner of the large canvas element.