How to make Konva-React responsive (scaling) canvas according to device size / resolution?

4.5k Views Asked by At

I'm creating a videogame-like app, for this I'm using Konva React to handle my Canvas, this app will be accessible from either a computer and / or mobile devices and I'd want it to scale / resize itself just like in this example that I found through this answer when it was still called KineticJS.

I'm fairly new to React and Konva so, there's a lot of things that I might not know and this lack of knowledge might be the reason behind this issue.

So, I tried to replicate the code in React, but this is still not resizing and instead it's adding scrollbars to see the content which is not what I'm looking for.

I tried the solution on this answer as well from 2 years ago, which does the same as my example here.

import React from 'react';
import { Rect, Layer, Stage } from 'react-konva';

function KCanvas () {
    // Fixed stage size
    var SCENE_BASE_WIDTH = 800
    var SCENE_BASE_HEIGHT = 600

    // Max upscale
    var SCENE_MAX_WIDTH = 1024
    var SCENE_MAX_HEIGHT = 768

    var stageWidth = window.innerWidth % 2 !== 0 ? window.innerWidth - 1 : window.innerWidth;
    var stageHeight = window.innerHeight % 2 !== 0 ? window.innerHeight - 1 : window.innerHeight;
    var stageSize = {width: stageWidth, height: stageHeight};

    var scaleX = Math.min(stageSize.width, SCENE_MAX_WIDTH) / SCENE_BASE_WIDTH;

    var scaleY = Math.min(stageSize.height, SCENE_MAX_HEIGHT) / SCENE_BASE_HEIGHT;

    var minRatio = Math.min(scaleX, scaleY);
    var scale = { x: minRatio, y: minRatio };

    var stagePos = {
        x: (stageSize.width - SCENE_BASE_WIDTH * minRatio) * 0.5,
        y: (stageSize.height - SCENE_BASE_HEIGHT * minRatio) * 0.5
    };
    

    return (
        <Stage 
            size={stageSize}
            scale={scale}
            position={stagePos}>
            <Layer>
            <Rect 
                x={toAbsolute(stage)}
                y={50}
                width={50}
                height={50}
                fill={'green'}
            />
            </Layer>
        </Stage>
    );
}

export default KCanvas;

Enabling useStrictMode(true) as shown in this answer didn't help either.

An example of how my canvas looks like with a little of window resizing enter image description here

And after resizing:

enter image description here

What should be happening is the green rectangle should be scaled down according to window size just like in the 2nd link I posted above:

enter image description here

1

There are 1 best solutions below

2
On BEST ANSWER

Your approach is good. You just need to redraw the stage on window size changes. Like this:

const [size, setSize] = React.useState({ width: window.innerWidth, height: window.innerHeight });

React.useEffect(() => {
  const checkSize = () => {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
  };

  window.addEventListener('resize', checkSize);
  return () => window.removeEventListener('resize', checkSize);

}, []);

// do your calculations for stage properties
var stageWidth = size.width % 2 !== 0 ? size.width - 1 : size.width;
var stageHeight = size.height % 2 !== 0 ? size.height - 1 : size.height;
// ...

Demo: https://codesandbox.io/s/react-konva-responsive-stage-kpmy7?file=/src/index.js