How to animate the selected link with dotted lines

I wanted to animate the selected link with dotted animation. How can I do that, I tried with below code it’s not working.

 this.myDiagram = $(go.Diagram, this.diagramDiv.nativeElement, {
      layout: new TableLayout(),
      "SelectionMoved": this.relayoutDiagram.bind(this),
      initialContentAlignment: go.Spot.Top,
      "animationManager.isEnabled": true,
    });

this.myDiagram.linkTemplate = 
      $(go.Link, {
        routing: go.Routing.AvoidsNodes,
        corner: 5,
        toShortLength: 4,
        selectable: true,
      })
      .bind('fromSpot')
      .bind('toSpot')
      .add( 
        $(go.Shape, {
          name: 'SHAPE-LINK',
          strokeWidth: 3,
        })
        .bind("stroke", "color")
        .bind("stroke", "isSelected", function(sel) {
          return sel ? "red" : "green"; // Blue when selected
        }),
        $(go.Shape, { toArrow: "OpenTriangle", strokeWidth: 3 }).bind("stroke", "color")
      );

// Store active animations
    var animations = new Map();

    // Listen for selection changes
    this.myDiagram.addDiagramListener("ChangedSelection", function(e) {
      console.log('Show animation ');
      // Stop all existing animations
      animations.forEach(function(anim, link) {
        anim.stop();
        animations.delete(link);
      });

      // Get the selected link
      var selectedLink = e.diagram.selection.first();
      console.log('selectedLink ', selectedLink);
      if (selectedLink instanceof go.Link) {
        var shape = selectedLink.findObject("SHAPE-LINK"); // Name the Shape in the link template
        if (shape) {
          // Create an animation
          console.log('animation started ', shape);
          var animation = new go.Animation();
          animation.easing = go.Animation.EaseLinear;
          animation.duration = 20; // Animation duration in milliseconds
          animation.add(shape, "strokeDashOffset", 0, -8); // Move dashes by 8 pixels
          animation.reversible = true; // Loops back and forth
          animation.runCount = Infinity; // Loop indefinitely

          // Start the animation and store it
          animation.start();
          animations.set(selectedLink, animation);
          console.log('animation ended');
        }
      }
      console.log('animations ', animations);
    });

One way to implement it is to have a “ChangedSelection” DiagramEvent listener. Here’s what I would do:

<!DOCTYPE html>
<html>
<head>
  <title>Animated Selected Links</title>
  <!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
  Selected Links will have an animated Shape.strokeDashArray.
  <script src="https://cdn.jsdelivr.net/npm/gojs/release/go-debug.js"></script>
  <script id="code">
// this Animation animates all selected Links
let myAnimation = null;

const myDiagram =
  new go.Diagram("myDiagramDiv", {
      "ChangedSelection": e => {
        const anim = new go.Animation();
        anim.duration = 500;
        anim.easing = go.Animation.EaseLinear;
        anim.runCount = Infinity;
        let animlinks = 0;
        e.diagram.links.each(link => {
          if (link.isSelected) {
            link.path.strokeDashArray = [4, 4];
            anim.add(link.path, "strokeDashOffset", 4+4, 0);
            animlinks++;
          } else {
            link.path.strokeDashArray = null;
          }
        });
        if (myAnimation) myAnimation.stop();
        if (animlinks > 0) {
          myAnimation = anim;
          myAnimation.start();
        } else {
          myAnimation = null;
        }
      },
      "undoManager.isEnabled": true
    });

myDiagram.nodeTemplate =
  new go.Node("Auto")
    .add(
      new go.Shape({ fill: "white" })
        .bind("fill", "color"),
      new go.TextBlock({ margin: 8 })
        .bind("text")
    );

myDiagram.linkTemplate =
  new go.Link({ selectionAdorned: false })
    .add(
      new go.Shape({ strokeWidth: 1.5, strokeDashArray: [4, 4] }),
      new go.Shape({ toArrow: "OpenTriangle" })
    );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, text: "Alpha", color: "lightblue" },
  { key: 2, text: "Beta", color: "orange" },
  { key: 3, text: "Gamma", color: "lightgreen" },
  { key: 4, text: "Delta", color: "pink" }
],
[
  { from: 1, to: 2 },
  { from: 1, to: 3 },
  { from: 2, to: 2 },
  { from: 3, to: 4 },
  { from: 4, to: 1 }
]);
  </script>
</body>
</html>

Thanks Walter. The way I achieved this is, I initially wanted solid lines, when user selects I make them dotted and apply the animations. Finally its working many thanks.

<script>
function init() {
  let myAnimation = null;
  var $ = go.GraphObject.make;
  var diagram = $(go.Diagram, "myDiagramDiv", {
    "ChangedSelection": e => {
      const anim = new go.Animation();
      anim.duration = 500;
      anim.easing = go.Animation.EaseLinear;
      anim.runCount = Infinity;
      let animlinks = 0;
      e.diagram.links.each(link => {
        if (link.isSelected) {
          anim.add(link.path, "strokeDashOffset", 4+4, 0);
          animlinks++;
        }
      });
      if (myAnimation) myAnimation.stop();
      if (animlinks > 0) {
        myAnimation = anim;
        myAnimation.start();
      } else {
        myAnimation = null;
      }
    },
    "undoManager.isEnabled": true
  });

  diagram.nodeTemplate =
    new go.Node("Auto")
    .add(
      new go.Shape({ fill: "white" })
        .bind("fill", "color"),
      new go.TextBlock({ margin: 8 })
        .bind("text")
    );

  // Link template with dotted lines and animation when selected
  diagram.linkTemplate =
    new go.Link({ selectionAdorned: false })
    .add(
      $(go.Shape,
        { strokeWidth: 2 },
        new go.Binding('strokeDashArray', 'isDotted', (isDotted) => (isDotted ? [5, 5] : null))
      ),
      new go.Shape({ toArrow: "OpenTriangle" })
    );
    diagram.model = new go.GraphLinksModel(
      [
        { key: 1, text: "Alpha", color: "lightblue" },
        { key: 2, text: "Beta", color: "orange" },
        { key: 3, text: "Gamma", color: "lightgreen" },
        { key: 4, text: "Delta", color: "pink" }
      ],
      [
        { from: 1, to: 2, isDotted: false },
        { from: 1, to: 3, isDotted: false },
        { from: 2, to: 2, isDotted: false },
        { from: 3, to: 4, isDotted: false },
        { from: 4, to: 1, isDotted: false }
      ]);
    const animation = new go.Animation();
    animation.duration = 500;
    animation.runCount = Infinity;
    diagram.addDiagramListener("ChangedSelection", function(e) {
    e.diagram.startTransaction('updateDottedLinks');
    e.diagram.selection.each((part) => {
      if (part instanceof go.Link) {
        const linkData = part.data;
        // Use getValue to retrieve isDotted (default to false if undefined)
        const currentDotted = linkData.isDotted || false;
        // Toggle isDotted: if not dotted, make it dotted and animate; if dotted, make it solid
        e.diagram.model.setDataProperty(linkData, 'isDotted', !currentDotted);

        // If isDotted is true, start animation for this link
        if (!currentDotted) {
          const shape = part.findObject('LINKSHAPE');
          if (shape) {
            animation.add(shape, 'strokeDashOffset', 0, 10); // Animate offset for marching ants
          }
        }
      }
    });
    e.diagram.commitTransaction('updateDottedLinks');
    // Start the animation if there are any dotted links
    if (e.diagram.links.any((link) => link.data.isDotted)) {
      animation.start();
    } else {
      animation.stop();
    }
  });
}

window.addEventListener('DOMContentLoaded', init);
</script>

OK. But understand that you don’t need to have an “isDotted” data property in your model. You don’t need to have that overhead and code complexity unless you really want that state to be serialized in your model.

I have updated the code above to assume that links are not normally dotted, and that they become dotted only when selected and animated.
It’s just a matter of changing the default value for Shape.strokeDashArray (not setting it in the template) and then in the “ChangedSelection” DiagramEvent listener appropriately updating that property on each Link.