Robolisk
Robolisk

Reputation: 1792

react canvas.scale doesn't seem to behaving correctly when being set

I have a component that creates a canvas another and another that controls what happens to it on events:

const Parent = () => {
    const [canvasRef, zoom, setZoom] = useCanvas();

    const onMouseWheel = (e) => {
        let tmpZoom = zoom + (e.deltaY / 1000);
        setZoom(tmpZoom);
    }

    return (
        <canvas
            ref={canvasRef}
            width={canvasDimentions.width} // 1080
            height={canvasDimentions.height} // 1920
            onWheelCapture={onMouseWheel}
        />

    );
}


const useCanvas = () => {
    const [zoom, setZoom] = useState(1);
    const canvasRef = useRef(null);
    useEffect(() => {
        const canvasObj = canvasRef.current;
        const ctx = canvasObj.getContext('2d');
        ctx.clearRect(0, 0, canvasDimentions.width * zoom, canvasDimentions.height * zoom); // width * height intial 1920, 1080
        console.log(zoom);
        ctx.scale(zoom, zoom)
        drawSquare(); // draws a simple 100 x 100 square
    },[zoom])
    return [canvasRef, zoom, setZoom];
}

What's happening whenever I do this is the zoom does get set correctly (the zoom slowly increases in value correctly within my console.log(zoom), and decreases when I scroll in the opposite direction.

The strange behaviour I'm getting with ctx.scale(zoom, zoom) is that let's say I zoom out: (1, 0.9, 0.8 .. etc) and then zoom in the other direction (0.8, 0.9, 1, 1.1 ... etc) it'll keep zooming out instead of zooming the other way (back in), or it will randomly jump to the correct value.

I understand that useState is asynchronous but if that's the case, how would my console.log of zoom just before passing it scale be correct each and every time?

Upvotes: 0

Views: 416

Answers (1)

Kaiido
Kaiido

Reputation: 136608

This has nothing to do with reactjs, that's just how the 2D context scale method works.

ctx.scale(x, y) does take the current transformation matrix and multiplies its scaleX and scaleY member by x and y.

So if you call scale(0.7, 0.7) then scale(0.8, 0.8), even that second call will actually make the context's transform smaller:

At the beginning scaleX and Y are 1, at the first call they become 0.7 (1 x 0.7), then they become 0.56 (0.7 x 0.8).

Maybe you'd prefer the setTransform method which sets the transformation matrix absolutely.
Using this method you could just do

ctx.setTransform(zoom, 0, 0, zoom, 0, 0);

and here going from 0.7 to 0.8 would produce your expected result of zooming-in.

Upvotes: 1

Related Questions