Search code examples
reactjskonvajsreact-konvakonvakonvajs-reactjs

Limited workspace in React-Konva


I work with React-Konva and am trying to make a limited size workspace (like in photoshop) with scalability. So that when you scale the workspace window starts to take up more and more space on the screen. But after writing the function, the smoothness of scaling is gone.

Here is more or less working code:

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

const App = () => {
  const [zoom, setZoom] = useState(1);
  const stageRef = useRef(null);

  const stageWidth = 500;
  const stageHeight = 500;

    const handleScale = (event: Konva.KonvaEventObject<WheelEvent>) => {
        const stage = stageRef.current

        if (!stage) {
            throw new Error('stage is undefined')
        }

        event.evt.preventDefault()

        if (!event.evt.ctrlKey && !event.evt.altKey && !event.evt.metaKey) {
            return
        }

        const oldScale = stage.scaleX() || 1
        const pointer = stage.getPointerPosition()

        if (!pointer) {
            throw new Error('pointer is undefined')
        }

        const minScale = 0.1 // Minimal possible scaling
        const maxScale = 10 // Maximum possible scaling
        const scaleSpeed = 0.015 // Scaling speed

        const newScale = Math.min(
            Math.max(oldScale - event.evt.deltaY * scaleSpeed, minScale),
            maxScale
        )

        stage.scale({ x: newScale, y: newScale })

        const newPosition = {
            x: pointer.x - ((pointer.x - stage.x()) / oldScale) * newScale,
            y: pointer.y - ((pointer.y - stage.y()) / oldScale) * newScale
        }

        stage.position(newPosition)
        stage.batchDraw()

        setZoom(newScale)
    }

  return (
        <Stage
          width={stageWidth * zoom}
          height={stageHeight * zoom}
          ref={stageRef}
          onWheel={handleScale}
        >
          <Layer>
            {/* Other shapes */}
            <Rect x={50} y={50} width={100} height={100} fill="red" draggable />
          </Layer>
        </Stage>
  );
};

export default App;

Is it possible to implement a limited size working area with the ability to increase or decrease its size on the screen? And if so, how?


Solution

  • With this, width={stageWidth * zoom} height={stageHeight * zoom} you may increase the size of your stage too much. Large stage consumes a lot of memory and CPU resources to render. So, it is better to keep the size of the stage as small, as possible.

    You can add a scrolling mechanism into your application right into stage logic. So, you can keep the stage small and allow users to navigate it.

    You can find several scrolling demos in the official docs: https://konvajs.org/docs/sandbox/Canvas_Scrolling.html