Fr1ar
Fr1ar

Reputation: 55

HTML5 canvas is flickering in Google Chrome

Seems like an update on google chrome messed up my canvas rendering. I have a pretty simple code that renders an image and text on canvas:

var onDraw = function() {
  context.clearRect(0, 0, 256, 256);
  context.drawImage(image, 0, 0, 256, 256);
  context.fillText('TEST', 0, 20);
  
  requestAnimationFrame(onDraw);
};

This code is terribly flickers on Chrome: https://jsfiddle.net/gp9jxn6q/ (just move your mouse over the page).

There are only 2 ways I found to prevent this behavior:

  1. Call context.clearRect() for the whole canvas. But in this case I can not redraw dirty rectangles only.
  2. Set image-rendering: pixelated for the canvas. In this case all fonts look terrible.

What else can be done with this?

Upvotes: 0

Views: 2251

Answers (1)

Wezelkrozum
Wezelkrozum

Reputation: 1006

This is a bug that started to appear more frequently since Chromium version 83 and even more so since 85 (so in addition to Chrome this also effects Opera and the new Edge). I filed an issue at Chromium a few months ago and they are currently working on a fix: https://bugs.chromium.org/p/chromium/issues/detail?id=1092080

What happens is that the antialiasing is set to "nearest neighbour" in the next monitor frame after the drawImage() call. This can affect the source and destination element, and this affects any CanvasImageSource (image, video, canvas: https://developer.mozilla.org/en-US/docs/Web/API/CanvasImageSource).

It happens randomly because it is probably bound to the performance of the device and timing, because some graphics settings can fix the bug while at the same time they can create the bug on another device.

But I also have some good news. I think I've finally found a workaround you can execute after the drawImage() call that resets the antialiasing: http://jsfiddle.net/u7k5qz2p/

    <div class="chromium-issue-1092080-workaround__wrapper">
        <canvas id="canvas" width="300" height="300"></canvas>
        <div class="chromium-issue-1092080-workaround__overlay"></div>
    </div>
    context.drawImage(image, 0, 0, 256, 256);
    chromiumIssue1092080WorkaroundOverlay.style.transform = `scaleX(${Math.random()})`

What it does is overlay a div on top of the canvas. And after each drawImage() call changes the scaleX in the transform style to trigger a reset of the antialiasing setting in the canvas.

Upvotes: 4

Related Questions