projectstorm / react-diagrams

a super simple, no-nonsense diagramming library written in react that just works
https://projectstorm.cloud/react-diagrams
MIT License
8.51k stars 1.17k forks source link

zoomToFit doesn't work correctly with negative values #832

Open ghost opened 3 years ago

ghost commented 3 years ago

Problema If the node has an X or Y with a negative value, the function(engine.zoomToFit()) does not work correctly (see GIF)

I expect: Any chart with any value of coordinates and with any number of nodes will be displayed in the viewport on the canvas, and not outside it!

For example:

node1.setPosition(100, 100);
node2.setPosition(-400, 100);

Apr-14-2021 18-04-10

jtamm-red commented 2 years ago
    zoomToFit() {
        const minNodePosition = {x: null, y: null}
        const maxNodePosition = {x: null, y: null}
        _.forEach(this.model.activeNodeLayer.getNodes(), (nodeModel) => {

            if(minNodePosition.x == null || minNodePosition.x > nodeModel.position.x) {
                minNodePosition.x = nodeModel.position.x;
            }
            if(minNodePosition.y == null || minNodePosition.y > nodeModel.position.y) {
                minNodePosition.y = nodeModel.position.y;
            }
            if(maxNodePosition.x == null || maxNodePosition.x < (nodeModel.position.x + nodeModel.width)) {
                maxNodePosition.x = nodeModel.position.x + nodeModel.width;
            }
            if(maxNodePosition.y == null || maxNodePosition.y < (nodeModel.position.y + nodeModel.height)) {
                maxNodePosition.y = nodeModel.position.y + nodeModel.height;
            }
        });
        if(minNodePosition.x == null) {
            return;
        }
        const areaSize = {
            width: Math.abs(minNodePosition.x) + maxNodePosition.x,
            height: Math.abs(minNodePosition.y) + maxNodePosition.y
        }
        let ratio = this.model.getZoomLevel() / 100;
        let xFactor = this.canvas.clientWidth / (areaSize.width * ratio);
        let yFactor = this.canvas.clientHeight / (areaSize.height * ratio);
        if(xFactor > 1.0) {
            xFactor = 1.0
        }
        if(yFactor > 1.0) {
            yFactor = 1.0
        }
        const zoomFactor = xFactor < yFactor ? xFactor : yFactor;
        this.model.setZoomLevel(this.model.getZoomLevel() * zoomFactor);
        ratio = this.model.getZoomLevel() / 100;
        this.model.setOffset(minNodePosition.x * ratio * (-1), minNodePosition.y * ratio * (-1));
        this.repaintCanvas();
    }

I hope that my suggestion works well.