Dynamic Scroll-Margin based on viewport size

Hi,

I couldn’t find a fitting solution in the docs, but maybe you can help me.

We want a scroll-margin around the diagram nodes.
This margin should be approximately half the viewport.
My problem is that I’m not able to find a setting that adds a margin, which isn’t in absolute pixels. Everything I tried is either fine for zoomed in or zoomed out.

My current workaround is the following:

private updateScrollMargin(e: ViewportBoundsChangedEvent): void {
        const diagram = e.diagram;
        diagram.commit(() => {
            const newMargin = new go.Margin(
               // SCROLL_MARGIN = 0.5
                diagram.viewportBounds.height * SCROLL_MARGIN,
                diagram.viewportBounds.width * SCROLL_MARGIN
            );
            diagram.scrollMargin = newMargin;
        }, null);
    }

which is called everytime the ViewportBounds change.
This generally works fine, but is a bit weird with the scrollbars (they jump around, when zooming).
We also have problems with using diagram.zoomToFit, when using this workaround.

Do you have a suggestion or can point me to a better solution?

Cheers
Niklas

Does this work any better for you?

  new go.Diagram("myDiagramDiv", {
      "ViewportBoundsChanged": e => {
        const divsz = e.subject.newCanvasSize;
        const sc = e.diagram.scale;
        if (!divsz.equals(e.subject.canvasSize) || sc !== e.subject.scale) {
          e.diagram.scrollMargin = new go.Margin(divsz.width/2/sc, divsz.height/2/sc);
        }
      },
      . . .

Note that the primary feature of this alternative “ViewportBoundsChanged” listener is that it doesn’t do anything unless the Diagram.scale or Div size has changed.

Thanks, that seems to work better :)

On further tests it seems we still have issues with the functionality of diagram.commandHandler.zoomToFit.
Without the dynamic scroll margin code, we get the entire model centered in the viewport.
With the dynamic scroll margin code, the model isn’t fully visible and also not correctly centered.
Do you have any further ideas on how to approach this?

Ah, yes, that depends on the Diagram.documentBounds, not on the scrolling bounds. So the “ViewportBoundsChanged” listener should be modifying the Diagram.padding (which changes the documentBounds), not the Diagram.scrollMargin (which does not affect the documentBounds).

      "ViewportBoundsChanged": e => {
        const divsz = e.subject.newCanvasSize;
        const sc = e.diagram.scale;
        if (!divsz.equals(e.subject.canvasSize) || sc !== e.subject.scale) {
          e.diagram.commit(d => {
            d.padding = new go.Margin(divsz.width/2/sc, divsz.height/2/sc);
          }, null);
        }
      },

Mhm, although this puts the entire model inside the viewport, the padding is visible around the entire model when zoomToFit is used. This means the model only takes up the middle of the screen. Not what I was after.

I don’t actually want a padding around the model. I just want the user to able to scroll about half the viewport in either direction.

I just tested scrollMargin (just setting it on the Diagram) with the Example Code from here:
Interactive Diagram for Building Flowcharts | GoJS Diagramming Library

If I add a scrollMargin: new go.Margin(800, 500) in this example I get weird behaviour from zoomToFit (Shift+Z), especially if I’m completely zoomed in mostly inside the ScrollMargin at the point where I press Shift+Z:
example
The animated part is the Shift+Z behaviour.

I would expect the entire model to fill the screen.
Without any padding either top and bottom or left and right to butt up against the viewport bounds.
The scrollMargin would only mean, that I’m able to scroll into whitespace to the sides.
Or am I not understanding the concepts correctly?

I see, we’ll fix this.

This will be out in the next release. Thank you for reporting.

In the meantime, you could make a workaround by writing:

    myDiagram.commandHandler.zoomToFit = function () {
      go.CommandHandler.prototype.zoomToFit.call(this);
      myDiagram.alignDocument(Spot.Center, Spot.Center);
    };