Here’s three tokens going back up the path from Delta to Alpha:
Here’s the complete stand-alone sample:
<!DOCTYPE html>
<html>
<head>
<title>MultiPath Animation</title>
<!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
<button id="myTestButton">Test 1</button><button id="myTestButton2">Test 2</button>
<textarea id="mySavedModel" style="width:100%;height:250px"></textarea>
<script src="go.js"></script>
<script id="code">
const $ = go.GraphObject.make;
const myDiagram =
new go.Diagram("myDiagramDiv",
{
layout: new go.LayeredDigraphLayout({ direction: 90 }),
"undoManager.isEnabled": true,
"ModelChanged": e => { // just for demonstration purposes,
if (e.isTransactionFinished) { // show the model data in the page's TextArea
document.getElementById("mySavedModel").textContent = e.model.toJson();
}
}
});
myDiagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape, "Capsule",
{ fill: "white", strokeWidth: 0, spot1: go.Spot.TopLeft, spot2: new go.Spot(1, 1, -10, 0) },
new go.Binding("fill", "color")),
$(go.Panel, "Horizontal",
$(go.Panel, "Spot",
$(go.Shape, "Circle", { width: 20, height: 20, fill: "white", strokeWidth: 0, margin: 4 }),
$(go.TextBlock, { scale: 0.75 },
new go.Binding("text", "value", v => v + "%"))
),
$(go.TextBlock, { width: 100 },
new go.Binding("text"))
)
);
myDiagram.linkTemplate =
$(go.Link,
{ curve: go.Link.Bezier, fromEndSegmentLength: 30, toEndSegmentLength: 30 },
$(go.Shape, { isPanelMain: true, stroke: "#666", strokeWidth: 5 }),
$(go.Shape, { isPanelMain: true, stroke: "#999", strokeWidth: 3 }),
$(go.Shape, { isPanelMain: true, stroke: "white", strokeWidth: 3, strokeDashArray: [6, 6] }),
$(go.Shape, { toArrow: "Standard", fill: "#666", stroke: "#666" })
);
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, text: "Alpha", color: "lightblue", value: 80 },
{ key: 2, text: "Beta", color: "orange", value: 40 },
{ key: 3, text: "Gamma", color: "lightgreen", value: 40 },
{ key: 4, text: "Delta", color: "pink", value: 10 }
],
[
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 3, to: 4 },
{ from: 4, to: 1 }
]);
function animatePaths(fromtos, color) {
const links = [];
fromtos.forEach(fromto => {
const from = myDiagram.findNodeForKey(fromto.from);
if (!from) return;
const to = myDiagram.findNodeForKey(fromto.to);
if (!to) return;
const link = from.findLinksTo(to).first();
if (!link) return;
links.push(link);
});
if (links.length === 0) return;
const token =
$(go.Part,
{ locationSpot: go.Spot.Center, layerName: "Adornment", _link: null, _frac: 0.0 },
$(go.Shape, "Circle",
{ width: 12, height: 12, fill: color || "red", strokeWidth: 0 })
);
myDiagram.add(token);
function animateToken() {
if (!token._link) {
token._link = links.shift();
if (token._link) {
token._length = token._link.path.geometry.flattenedTotalLength;
} else {
myDiagram.remove(token);
return;
}
}
if (token._frac >= 1.0 || !token._length) { // next link
token._link = null;
token._frac = 0.0;
} else {
const link = token._link;
token.location = link.path.getDocumentPoint(link.path.geometry.getPointAlongPath(token._frac));
token._frac += 5/token._length; // each tick moves a constant distance
}
requestAnimationFrame(animateToken);
}
requestAnimationFrame(animateToken); // start right now
}
document.getElementById("myTestButton").addEventListener("click", e => {
animatePaths([
{ from: 1, to: 2 }
], "blue");
});
document.getElementById("myTestButton2").addEventListener("click", e => {
animatePaths([
{ from: 1, to: 3 },
{ from: 3, to: 4 },
{ from: 4, to: 1 }
], "red");
});
</script>
</body>
</html>