Positioning Konva stage when scaling in ans out

30 Views Asked by At

I am trying to create a HTML page that has a large image (6744 x 3212), the user should be able to drag the image to see the different parts of the image. In addition, the user can use buttons to zoom in and out. The Konva library is used to display the image on a stage, then the stage and image are manipulated to change the part of the image shown and the amount to zoom in and out.

I started out just setting up the image to draggable and restrict the image to not scroll out of bounds, but now that I am adding zooming in and out, the calculations for keeping the stage scale and position are not being calculated correctly. The image jumps after the image is dragged after being zoomed in. Look at the zoomStage function for the scaling details.

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://code.jquery.com/jquery-3.7.1.js"
            integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4="
            crossorigin="anonymous"></script>
    <script src="https://unpkg.com/[email protected]/konva.min.js"></script>
    <meta charset="utf-8" />
    <title>Konva Drag Bounds and Zoom Demo</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            background-color: #f0f0f0;
        }

        #container {
            width: calc(90% - 22px);
            height: calc(90vh - 22px);
            overflow: hidden;
            margin: 10px 10px 10px 10px;
            padding: 10px 10px 10px 10px;
            border: 1px solid grey;
        }
        .buttons {
            margin:  20px 0 0 20px;
            max-width: 400px;
        }
    </style>

    <script>
        function getDistance(p1, p2)
        {
            return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
        }

        function getCenter(p1, p2)
        {
            return {
                x: (p1.x + p2.x) / 2,
                y: (p1.y + p2.y) / 2,
            };
        }

        function isTouchEnabled()
        {
            return ( 'ontouchstart' in window ) ||
                    ( navigator.maxTouchPoints > 0 ) ||
                    ( navigator.msMaxTouchPoints > 0 );
        }

    </script>
</head>

<body>
<div id="container"></div>
<div class='buttons'>
    <button id='stageZoomIn'>Zoom in</button>
    <button id='stageZoomOut'>Zoom out</button>
</div>

<script>
    let width = window.innerWidth;
    let height = window.innerHeight;

    let image;

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

    let layer = new Konva.Layer();
    let textLayer = new Konva.Layer ();
    stage.add(layer);
    stage.add (textLayer);

    text = new Konva.Text ({
        x: 30,
        y: 50,
        fontFamily: 'sans-serif',
        fontSize: 14,
        text: '',
        fill: 'black',
    });
    textLayer.add (text);

    let imageObj = new Image();
    imageObj.src = 'img/map_image.png';
    imageObj.onload = function()
    {
        image = new Konva.Image({
            x: 0,
            y: 0,
            image: imageObj,
            width: 6744,
            height: 3212,
            draggable: true,
        });
        layer.add(image);

        image.on ('click', function () {
            let pos = image.getAbsolutePosition();
            let sp = stage.position();
            text.text ('image x: ' + pos.x.toFixed (4) + ', y: ' + pos.y.toFixed (4) +
                    '\nspos x: ' + sp.x + ', y: ' + sp.y +
                    '\nscale: ' + stage.scaleX () +
                    '\nstage: ' + stage.width () + ', ' + stage.height ());
        });

        image.on ('dragmove', function ()
        {
            let pos = image.getAbsolutePosition();

            // ensure the user cannot drag past the origin.
            if (pos.x > 0.0) pos.x = 0;
            if (pos.y > 0.0) pos.y = 0;

            // ensure user cannot drag past the edge of the image.
            if (pos.x <= -(image.width - stage.width))  pos.x (-(image.width - stage.width));
            if (pos.y <= -(image.height - stage.height)) pos.y (-(image.height - stage.height));

            image.x(pos.x);
            image.y(pos.y);
        });
    };

    let scale = 1.0;
    $("#stageZoomIn").on("click", function () {
        console.log ("zoom in click");
        let pos = stage.getPointerPosition();
        scale =  zoomStage (stage, pos, 0.1);
    });

    $("#stageZoomOut").on("click", function () {
        console.log ("zoom out click");
        let pos = stage.getPointerPosition();
        scale = zoomStage (stage, pos, -0.1);
    });


    function zoomStage (stg, zoomPoint, scaleInc)
    {
        stg.preventDefault(true);

        // remember the scale before new zoom is applied - we are scaling
        // same in x & y so either will work
        let oldScale = stg.scaleX();

        // compute the distance to the zoom point before applying zoom
        let mousePointDist = {
            x: zoomPoint.x - (stg.x() / oldScale),
            y: zoomPoint.y - (stg.y() / oldScale)
        };

        // compute new scale
        const newScale = oldScale + scaleInc;

        // apply new zoom to stage
        stage.scale({ x: newScale, y: newScale });

        // Important - move the stage so that the zoomed point remains
        // visually in the same place
        let newPos = {
            x: mousePointDist.x - (zoomPoint.x / newScale),
            y: mousePointDist.y - (zoomPoint.y / newScale)
        };

        // Apply position to stage
        stg.position (newPos);

        // return the new zoom factor.
        return newScale;
    }
</script>
</body>
</html>
0

There are 0 best solutions below