Animate position and zoom change when double clicking


we are having an issue which calculating a position & scale change when double clicking on the diagram. We are trying to implement that when double clicking on the diagram the viewport will center to the click point and set the scale to 1 and do a transition animation from the current viewport size and position to this new one.

We have tried several different ways of calculating this animation but most of them have the issue when trying to change the position when the scale is also changing as the calculation is done before the scale and thus the viewport either overshoots or undershoots the new position.

The latest method we have tried is the following:

  private onBackgroundDoubleClicked(event: DiagramEvent): void {
    const { lastInput } = event.diagram;
    const clickPoint = lastInput.documentPoint;

    const newDiagramPoint = new Point(
      clickPoint.x - this.diagram.viewportBounds.width / 2,
      clickPoint.y - this.diagram.viewportBounds.height / 2

    const animation = new Animation();
    animation.add(this.diagram, 'scale', this.diagram.scale, 1);
    animation.add(this.diagram, 'position', this.diagram.position, newDiagramPoint);

We also tried to use scrollToRect but this does not seem to allow scaling to be animated at the same time (presumably due to it handling its own animation?).

Are we thinking about this the wrong way when trying to implement this or is there a built in function that we have missed?

That’s the right idea. First, I should point out that Diagram.scrollToRect is not supposed to animate. Second, Diagram.viewportBounds is in document coordinates, which means it accounts for the Diagram.scale at that time.

      $(go.Diagram, ...,
          scrollMargin: 200,
          doubleClick: function(e) {
            var diag = e.diagram;
            var pt = diag.lastInput.documentPoint;
            var sc = 2.5;  // target scale
            var w = diag.viewportBounds.width * diag.scale / sc;
            var h = diag.viewportBounds.height * diag.scale / sc;
            var pos = new go.Point(pt.x - w/2, pt.y - h/2);  // target position
            var anim = new go.Animation();
            anim.easing = go.Animation.EaseLinear;
            anim.add(diag, "scale", diag.scale, sc);
            anim.add(diag, "position", diag.position, pos);

Thanks for the help!

Taking the current diagram scale into account works.