2

Consider having a large (2000x1000) stage with some text in it. The stage gets downscaled to 1000x500 making the text unreadable. Then we try to enlarge the text by zooming it in.

Expected: the text should become readable again at some point.

Actual: the text remains unreadable (blurred) no matter how much we zoom in.

Try zooming the page in (with native browser zoom on desktop) after running the snippet:

const stage = new Konva.Stage({
   container: 'container',
   width: 2000,
   height: 1000,
});

const layer = new Konva.Layer();
stage.add(layer);

const rect = new Konva.Text({
   x : 50, y : 50, width: 100, height: 100,
   fontSize: 12,   
   text: "This text should be readable when the viewport gets downscaled"
});

layer.add(rect).draw();

stage.scale({x: 0.5, y: 0.5});
stage.setAttrs({width: 1000,  height: 500});
stage.draw();
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.6.0/konva.js"></script>
<div id="container"></div>

The quality loss can be avoided by downscaling with CSS only, like this:

const stage = new Konva.Stage({
   container: 'container',
   width: 2000,
   height: 1000,
});

const layer = new Konva.Layer();
stage.add(layer);

const rect = new Konva.Text({
   x : 50, y : 50, width: 100, height: 100,
   fontSize: 12,   
   text: "This text should be readable when the viewport gets downscaled"
});

layer.add(rect).draw();

stage.getChildren().forEach(function(layer) {
    layer.canvas._canvas.style.width = "1000px";
    layer.canvas._canvas.style.height = "500px";

    layer.hitCanvas.setSize(1000, 500);
    layer.hitCanvas.context.scale(0.5, 0.5);
});    

stage.draw();
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.6.0/konva.js"></script>
<div id="container"></div>

Note how text becomes readable at a certain level of zooming.

The workaround breaks Konvajs abstraction. What problems it can potentially cause? Is there a better way, which uses only public methods exposed by Konvajs?

In fabric.js it can be done like this (complete example here):

canvas.setDimensions({width: '1000px', height: '500px'}, {cssOnly: true});
eugenesqr
  • 589
  • 6
  • 19

1 Answers1

1

Konva is a canvas framework. Canvas is a bitmap image unlike vector elements like SVG. So that "blur" should be expected. Technically to fix the issue you can redraw stage with higher pixelRatio on zoom event:

Konva.pixelRatio = 4
stage.draw();

That code will generate more pixels for canvas element. But the page may be very heavy in RAM in this case because Konva will have to produce very large canvas. In most of the mobile apps, you don't need native zooming and you can use responsive design. For zooming the stage, you can use Konva methods.

lavrton
  • 18,973
  • 4
  • 30
  • 63
  • 1
    Increasing pixelRatio after the layer is scaled down won't work, since all the extra pixel data is already lost. The only way to prevent the data loss is using CSS scaling (but only when scaling down). We basically keep the original image, so when we later enlarge it again with zoom, the pixels are still there. This is a working approach, all my tests pass. It breaks the framework abstraction however, which may cause weird bugs in future. Any thoughts on what could possibly go wrong with this approach? – eugenesqr Apr 05 '19 at 06:24
  • What do you mean by "data lost"? Scaling with CSS breaks hit detection for Konva. So it is not recommended. – lavrton Apr 05 '19 at 12:34
  • After scaling a canvas down we end up with a lower quality canvas, because the bitmap behind the canvas is actually changed. If we then scale the canvas back up (either by zooming in or by other means), we won't get the original canvas. I noticed the hit detection problem and updated the code in the question. I'm still unsure about possible caching issues, but it seems working so far. – eugenesqr Apr 05 '19 at 13:11
  • 1
    I've tried using this approach as I have a zoomable canvas container that changes its scale property and on each wheel event I set both the scale and the pixelRatio. The latter is set on the class imported from konva, while my stage is a react-konva component. However, calling draw on it does not cause any redrawing that would result in an image that is not blurry. Is there another way to prevent blurry canvas on parent scaling? – guben May 26 '21 at 09:21
  • Don't know about konva, but shouldn't offscreen stuff not cause issues or does konva not handle culling? – Ryan Leach Jun 13 '21 at 02:30