gojs
Version:
Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams
665 lines (618 loc) • 34.1 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="Loading a tree with custom GoJS animation."/>
<link rel="stylesheet" href="../assets/css/style.css"/>
<!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
<title>Treeload animation GoJS Sample</title>
</head>
<body>
<!-- This top nav is not part of the sample code -->
<nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
<div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
<div class="md:pl-4">
<a class="text-white hover:text-white no-underline hover:no-underline
font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
<h1 class="my-0 p-1 ">GoJS</h1>
</a>
</div>
<button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
<svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
<path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
<path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
</svg>
</button>
<div id="topnavList" class="hidden sm:block items-center w-auto mt-0 text-white p-0 z-20">
<ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
<li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
<li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
</ul>
</div>
</div>
<hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
</nav>
<div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
<div id="navSide" class="flex flex-col w-full md:w-48 text-gray-700 bg-white flex-shrink-0"></div>
<!-- * * * * * * * * * * * * * -->
<!-- Start of GoJS sample code -->
<script src="../release/go.js"></script>
<div id="allSampleContent" class="p-4 w-full">
<script id="code">
function init() {
// Since 2.2 you can also author concise templates with method chaining instead of GraphObject.make
// For details, see https://gojs.net/latest/intro/buildingObjects.html
const $ = go.GraphObject.make;
var animationDiagram = null;
// Sliders for customizing the links and duration of the animation
var durationSlider = document.getElementById("duration");
var durationOutput = document.getElementById("durationDisplay");
durationOutput.innerHTML = durationSlider.value;
durationSlider.oninput = function () {
durationOutput.innerHTML = this.value;
}
var cornerSlider = document.getElementById("corner");
var cornerOutput = document.getElementById("cornerDisplay");
cornerOutput.innerHTML = cornerSlider.value;
cornerSlider.oninput = function () {
cornerOutput.innerHTML = this.value;
}
var curvatureSlider = document.getElementById("curvature");
var curvatureOutput = document.getElementById("curvatureDisplay");
curvatureOutput.innerHTML = curvatureSlider.value;
curvatureSlider.oninput = function () {
curvatureOutput.innerHTML = this.value;
}
myDiagram =
new go.Diagram("myDiagramDiv",
{
// We want no default animation when the diagram loads
"animationManager.isInitial": false,
autoScale: go.Diagram.Uniform,
// Disable the diagram during the animation so that the links cannot be moved, which would call compute points
// The Diagram is reset to enabled when the animation is finished
isEnabled: false,
layout: $(go.ForceDirectedLayout,
{
defaultSpringLength: 20, // forces nodes closer together (default: 50)
defaultSpringStiffness: 0.20, // forces nodes closer together (default: 0.05)
isOngoing: false,
isRealtime: false
})
});
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
locationSpot: go.Spot.Center,
resizable: true,
rotatable: true,
opacity: 0
},
new go.Binding("location").makeTwoWay(),
new go.Binding("desiredSize"),
$(go.Shape, "RoundedRectangle", { fill: "lightBlue" }),
$(go.TextBlock, new go.Binding("text", "key"))
);
myDiagram.linkTemplate =
$(go.Link,
{ opacity: 0 },
$(go.Shape),
$(go.Shape, { toArrow: "Standard" })
);
// Generate a tree
var treeArr = [{ key: 0 }];
var childCount = 0;
var currParent = 0;
for (var i = 1; i < 100; i++) {
var newNode = {
key: i,
parent: treeArr[currParent].key
}
treeArr.push(newNode);
childCount++;
// How many Children the node will have
if (childCount > 3) {
childCount = 0;
currParent++;
}
}
myDiagram.model = new go.TreeModel(treeArr);
var queue = [myDiagram.nodes.first()];
// Start with the root node visible
myDiagram.commit(diag => {
diag.nodes.first().opacity = 1;
}, null);
// Begin
animateLinks(queue);
// Given an array of nodes, recursively animate their outgoing links and connected nodes, layer by layer.
// All the nodes in a layer will be stored and therefore animated before the function moves onto the next layer
function animateLinks(q) {
var newQueue = [];
var originalValues = new go.List();
// One animation per layer, which will contain all links in the layer
var animation = new go.Animation();
animation.duration = Number(durationSlider.value);
animation.easing = go.Animation.EaseLinear;
// Each time the function is called, the queue represents the entire layer, iterate through it removing each one
while (q.length > 0) {
var tempNode = q.shift();
// Add animation for all links that stem from the given node
tempNode.linksConnected.each(link => {
if (link.fromNode === tempNode) {
myDiagram.startTransaction("animate link");
originalValues.push({
link: link,
makeGeometry: link.makeGeometry,
oldPoints: link.points,
geometry: link.elt(0).geometry.copy()
});
// If there is an arrow, record the initial segment index and orientation to reset at the end, then animate the scale with the link
if (link.elements.count > 1) {
originalValues.last().oldSegIndex = [];
originalValues.last().oldSegOrientation = link.elt(1).segmentOrientation;
for (var i = 1; i < link.elements.count; i++) {
// Calculate waiting time for the element depending on how far it is along the link
if (link.geometry.figures.length === 0 || link.elt(i).segmentFraction < 0) {
offsetTime = link.elt(i).segmentFraction * animation.duration;
} else {
offsetTime = link.elt(i).segmentIndex / link.points.count * animation.duration
}
// Start scale animations after an offset
animation.add(link.elt(i), "customScale", 0.01, { endValue: link.elt(1).scale, offsetTime: offsetTime });
originalValues.last().oldSegIndex.push(link.elt(i).segmentIndex);
}
}
animation.add(link, "opacity", 0.01, 1)
// Node should fade in while the link is moving
animation.add(link.toNode, "opacity", 0, 1);
// linear links have no geometry, and are treated differently
if (link.geometry.figures.length === 0) {
animation.add(link, "linearLinkAnim", link.geometry, myDiagram.layout)
} else {
animation.add(link, "segmentLinkAnim", link.geometry, myDiagram.layout)
}
// New queue to store the next layer
newQueue.push(link.toNode);
myDiagram.commitTransaction("animate link")
}
});
}
q = newQueue;
// Exit if the next layer is empty, meaning there are no more links to animate
if (q.length === 0) {
myDiagram.isEnabled = true;
return;
}
// Chain animatons for each layer
animation.finished = () => {
myDiagram.startTransaction("change link");
originalValues.each(newVal => {
var link = newVal.link;
if (link.elements.count > 1) {
// Set original values of each part of the link back to what they were
link.elt(1).segmentOrientation = newVal.oldSegOrientation;
for (var i = 1; i < link.elements.count; i++) {
link.elt(i).segmentIndex = newVal.oldSegIndex[i - 1];
}
}
// Set the changed properties of the link back to what they were before the animation.
link.elt(0).geometry = newVal.geometry;
link.makeGeometry = newVal.makeGeometry;
// Setting the points back to what they were will call the default makeGeometry
link.points = newVal.oldPoints;
});
animateLinks(q);
myDiagram.commitTransaction("change link");
}
animation.start();
}
// Functions called by buttons that reset the layout and reanimate the links
document.getElementById('orth').addEventListener('click', orthog);
function orthog() {
myDiagram.startTransaction("change links");
myDiagram.linkTemplate =
$(go.Link,
{
opacity: 0, // links start out invisible
routing: go.Link.Orthogonal,
corner: Number(cornerSlider.value)
},
$(go.Shape),
$(go.Shape, { toArrow: "Standard" })
);
myDiagram.layout.doLayout(myDiagram);
// Root node should start visible
myDiagram.nodes.first().opacity = 1;
myDiagram.commitTransaction("change links");
// Begin to recursively animate links
animateLinks([myDiagram.nodes.first()]);
}
document.getElementById('bez').addEventListener('click', bezierLinks);
function bezierLinks() {
myDiagram.startTransaction("change links");
myDiagram.linkTemplate =
$(go.Link,
{
opacity: 0, // links start out invisible
curve: go.Link.Bezier,
curviness: Number(curvatureSlider.value)
},
$(go.Shape),
$(go.Shape, { toArrow: "Standard" })
);
myDiagram.layout.doLayout(myDiagram);
// Root node should start visible
myDiagram.nodes.first().opacity = 1;
myDiagram.commitTransaction("change links");
// Begin to recursively animate links
animateLinks([myDiagram.nodes.first()]);
}
document.getElementById('linear').addEventListener('click', linearLinks);
function linearLinks() {
myDiagram.startTransaction("change links");
myDiagram.linkTemplate =
$(go.Link,
{ opacity: 0 }, // links start out invisible
$(go.Shape),
$(go.Shape, { toArrow: "Standard" })
);
myDiagram.layout.doLayout(myDiagram);
// Root node should start visible
myDiagram.nodes.first().opacity = 1;
myDiagram.commitTransaction("change links");
// Begin to recursively animate links
animateLinks([myDiagram.nodes.first()]);
}
}
// Animation for changing the scale after a delay, endObj must contain the end scale but also the delay (offsetTime)
go.AnimationManager.defineAnimationEffect('customScale', (part, startValue, endObj, easing, currentTime, duration, animation) => {
if (endObj.offsetTime < currentTime) {
part.scale = easing(currentTime - endObj.offsetTime, startValue, (endObj.endValue - startValue), duration - endObj.offsetTime);
} else {
part.scale = startValue;
}
})
// General animation for links that are made up of path segments, linear links do not have this, so therefore must use a special case
go.AnimationManager.defineAnimationEffect('segmentLinkAnim',
(link, geometry, layout, easing, currentTime, duration, animation) => {
var animationState = animation.getTemporaryState(link);
animationState.currentTime = currentTime;
if (animationState.initial === undefined) {
// Only do these things once
animationState.points = [];
// The shapes geometry requires an offset from the links bounds so that it will line up properly
animationState.offsetX = link.elt(0).actualBounds.x;
animationState.offsetY = link.elt(0).actualBounds.y;
// Points that when added to the list of points will be used to create the bounds of the link, therefore they are the corner points of the original bounds
var boundX = link.actualBounds.x;
var boundY = link.actualBounds.y;
animationState.boundPoint1 = new go.Point(boundX, boundY);
animationState.boundPoint2 = new go.Point(boundX + link.actualBounds.width, boundY + link.actualBounds.height);
//Points used to hold the initial points of a quadratic bezier
animationState.point1 = null;
animationState.startValue = new go.Point(geometry.figures.first().startX, geometry.figures.first().startY);
animationState.endValue = null;
//Point from the link which will be used to create the new points that the arrow will follow along on
animationState.initPoint = link.points.elt(0).copy();
// Assuming the second shape is the arrow which will follow
if (link.elements.count > 1) {
// Change the segment index so that it will follow the segment which is being created
link.elt(1).segmentIndex = 2;
// Set this to none so that the angle can be manually set
link.elt(1).segmentOrientation = go.Link.None;
}
animationState.origPoints = link.points.copy();
// Shift all other objects segment indexes over so that they maintain their positions as the first four points are being used to animate the arrow
for (var i = 2; i < link.elements.count; i++) {
if (link.elt(i).segmentIndex < link.points.count) {
link.elt(i).segmentIndex += 4;
}
}
animationState.currX = 0;
animationState.currY = 0;
animationState.delX = 0;
animationState.delY = 0;
// Add these to the animationState so they can be used in the custom make geometry
animationState.totalDuration = duration;
animationState.easing = easing;
//Set the links makeGeometry to the custom one, passing in the objects that it will use each time it is called
link.makeGeometry = segmentMakeGeometry(animationState, geometry.copy(), link);
// Calculate the duration for all beziers since they all are the same length in the given link
var totalSegmentLength = 0;
var prevValueX = animationState.startValue.x;
var prevValueY = animationState.startValue.y;
var bezSegments = 0;
// find the total length of all the linear segments
geometry.figures.first().segments.each(sgmt => {
if (sgmt.type === go.PathSegment.QuadraticBezier) {
bezSegments++;
} else {
var length = Math.max(Math.abs(prevValueX - sgmt.endX), Math.abs(prevValueY - sgmt.endY));
totalSegmentLength += length;
}
prevValueX = sgmt.endX;
prevValueY = sgmt.endY;
})
// The duration for the Bezier corners on an orthogonal link is calculated by taking a fraction of the total duration based on how long the bezier corners are,
// Then that is divided by how many corners there are to get the average time needed for each segment
animationState.bezierDuration = duration * Math.abs(geometry.flattenedTotalLength - Math.abs(
totalSegmentLength)) /
(bezSegments * geometry.flattenedTotalLength);
animationState.changedSegments = new go.List();
animationState.firstItr = true;
animationState.currSegment = 0;
animationState.elaDuration = 0;
animationState.duration = 0;
animationState.initial = true;
}
animationState.hasTicked = false;
/*
Create a new set of points for the bounds, arrows, and labels.
There are a total of eight points which are used to make two bezier curves. The first one to generate the bounds of the link along
with the position of the arrowhead, and the second one to be used to hold the position of all the labels. The link uses the start and end points of
each four point bezier to calculate its bounds along with the geometries, so the start and end of the first one are the corners of the actual bounds
of the link at the beginning of the animation.
*/
var tempPoints = new go.List();
// Add points for the first bezier, bound point 1 and 2 are used to create the actual bounds of the link
tempPoints.push(animationState.boundPoint1);
var newPoint = new go.Point(animationState.initPoint.x + animationState.currX,
animationState.initPoint.y + animationState.currY);
tempPoints.push(newPoint);
tempPoints.push(newPoint);
tempPoints.push(animationState.boundPoint2);
// Add points from the original bezier which will be used to hold the position of the objects
animationState.origPoints.each(point => {
tempPoints.push(point);
});
// Changing the points will cause GoJS to call the modified makeGeometry which will remake the geometry
link.points = tempPoints;
if (link.elements.count > 1) {
// Set angle of the arrow using the most recent points
var newAngle = Math.atan2(animationState.delY, animationState.delX);
link.elt(1).angle = newAngle * 180 / Math.PI;
}
}
);
go.AnimationManager.defineAnimationEffect('linearLinkAnim',
(link, geometry, endValue, easing, currentTime, duration, animation) => {
var animationState = animation.getTemporaryState(link);
animationState.currentTime = currentTime
if (animationState.initial === undefined) {
// Put properties on the animationState so it can be referenced by the modified makeGeometry function
animationState.duration = duration;
animationState.easing = easing;
animationState.initial = true;
link.makeGeometry = linearMakeGeometry(animationState, geometry.copy(), link);
// Changing the points will cause GoJS to call the modified makeGeometry
link.points = link.points.copy();
}
if (link.elements.count !== 1) {
link.elt(1).segmentFraction = 1 - easing(currentTime, 0, 1, duration)
}
}
);
// Function returns points to draw a cubic bezier
function sliceCubicBezier(currentTime, p1, p2, p3, p4, segment, duration, offsetX, offsetY) {
var t = currentTime / duration;
var u = 1 - t;
/*
This function takes the original points of the link's bezier curve and returns four different points that will draw a segment of the curve
The algorithm consists of four equations which use the start t to the end t, where t represents the point of the curve going from 0 to 1 which
in this case is related to the time, however the t0 is always 0 which simplifies the equations. The first point should always stay the same
because the segment of the curve drawn will always start at the fromNode
*/
newp2 = addPoints(scalarMult(p1, u), scalarMult(p2, t));
newp3 = scalarMult(p1, u * u).add(scalarMult(p2, 2 * t * u)).add(scalarMult(p3, t * t));
newp4 = scalarMult(p1, u * u * u).add(scalarMult(p2, 3 * t * u * u)).add(scalarMult(p3, 3 * t * t * u)).add(
scalarMult(p4, t * t * t));
segment.point1X = newp2.x + offsetX;
segment.point1Y = newp2.y + offsetY;
segment.point2X = newp3.x + offsetX;
segment.point2Y = newp3.y + offsetY;
segment.endX = newp4.x + offsetX;
segment.endY = newp4.y + offsetY;
}
// Uses same algorithm just for a quadratic bezier
function sliceQuadBezier(currentTime, p1, p2, p3, segment, duration, offsetX, offsetY) {
var t = currentTime / duration;
var u = 1 - t;
// Same concept as the cubic algorithm minus a point
newp2 = addPoints(scalarMult(p1, u), scalarMult(p2, t));
newp3 = scalarMult(p1, u * u).add(scalarMult(p2, 2 * t * u)).add(scalarMult(p3, t * t));
segment.point1X = newp2.x + offsetX;
segment.point1Y = newp2.y + offsetY;
segment.endX = newp3.x + offsetX;
segment.endY = newp3.y + + offsetY;
}
function scalarMult(point, factor) {
return new go.Point(point.x * factor, point.y * factor);
}
function addPoints(a, b) {
return new go.Point().add(a).add(b);
}
// Geometry for basic linear case
function linearMakeGeometry(animationState, geometry, link) {
var startValue = new go.Point(geometry.startX, geometry.startY);
var endValue = new go.Point(geometry.endX, geometry.endY);
function tempMakeGeometry() {
var currX = animationState.easing(animationState.currentTime, startValue.x, (endValue.x - startValue.x),
animationState.duration);
var currY = animationState.easing(animationState.currentTime, startValue.y, (endValue.y - startValue.y),
animationState.duration);
var tempGeo = link.elt(0).geometry.copy();
tempGeo.endX = currX;
tempGeo.endY = currY;
return tempGeo;
}
return tempMakeGeometry;
}
/*
This custom makeGeometry slowly builds the geometry of the link, animating a segment then adding it to the geometry until all of the segments
have been iterated through. Orthogonal links are made up of linear path segments and quadratic bezier corners which have to be treated
differently. The current point that the animation is on is then returned so the arrowhead can be drawn.
*/
function segmentMakeGeometry(animationState, geometry, link) {
var startValX = geometry.figures.first().startX + animationState.offsetX;
var startValY = geometry.figures.first().startY + animationState.offsetY;
var prevptX = 0;
var prevptY = 0;
var currptX = 0;
var currptY = 0;
function tempMakeGeometry() {
animationState.newGeometry = geometry.copy();
// Offset the position values so it is in the right spot relative to the document coordinates
animationState.newGeometry.figures.first().startX += animationState.offsetX;
animationState.newGeometry.figures.first().startY += animationState.offsetY;
if (animationState.currSegment > geometry.figures.first().segments.length - 1) {
return animationState.newGeometry;
}
var shouldPop = true;
var scaledTime = animationState.currentTime - animationState.elaDuration;
/*
Because multiple segments are being modified in order in this animation and each one has a separate duration, the time used in the
easing functions must be at or below the duration. Usually the animation would stop if the current time exceeded the duration but
because it is continuing to animate other segments after, the time has to be brought within the duration of the current segment being modified
*/
var usedTime = scaledTime;
if (scaledTime > animationState.duration) {
usedTime = animationState.duration;
}
// Segment can only be modified once before it becomes frozen, which means that a copy must be made and modified
var currSegment = geometry.figures.first().segments.elt(animationState.currSegment).copy();
// Corner / Quadratic Bezier case
if (currSegment.type === go.PathSegment.QuadraticBezier) {
// Only do once per segment
if (animationState.firstItr === true) {
// Set the initial points of the curve which are used to calculate the piece of the bezier at a given time within the duration
animationState.point1 = new go.Point(currSegment.point1X, currSegment.point1Y);
animationState.endValue = new go.Point(currSegment.endX, currSegment.endY);
animationState.duration = animationState.bezierDuration;
animationState.firstItr = false;
// Check to see if the scaled time is less than the duration on the first iteration
if (scaledTime > animationState.duration) {
usedTime = animationState.duration;
}
}
sliceQuadBezier(usedTime, animationState.startValue, animationState.point1, animationState.endValue,
currSegment, animationState.duration, animationState.offsetX, animationState.offsetY);
} else if (currSegment.type === go.PathSegment.Bezier) {
if (animationState.firstItr === true) {
animationState.points = [];
animationState.points.push(
new go.Point(geometry.figures.first().startX, geometry.figures.first().startY));
animationState.points.push(new go.Point(geometry.figures.first().segments.first().point1X, geometry.figures.first().segments.first().point1Y));
animationState.points.push(new go.Point(geometry.figures.first().segments.first().point2X, geometry.figures.first().segments.first().point2Y));
animationState.points.push(new go.Point(geometry.figures.first().segments.first().endX, geometry.figures.first().segments.first().endY));
animationState.duration = animationState.totalDuration;
animationState.firstItr = false;
if (scaledTime > animationState.duration) {
usedTime = animationState.duration;
}
}
var tempGeo = geometry.copy();
// Function which modifies the segment to make a portion of the bezier curve depending on the time relative to the duration
sliceCubicBezier(usedTime, animationState.points[0], animationState.points[1],
animationState.points[2],
animationState.points[3],
currSegment, animationState.duration, animationState.offsetX,
animationState.offsetY);
} else { // Line segment case
// Only do once per segment
if (animationState.firstItr === true) {
animationState.endValue = new go.Point(currSegment.endX, currSegment.endY);
// Calculate duration based on the total duration and the size of this segment compared to the entire link
animationState.duration =
animationState.totalDuration * Math.max(Math.abs(animationState.startValue.x - animationState.endValue.x),
Math.abs(animationState.startValue.y - animationState.endValue.y)) / geometry.flattenedTotalLength;
animationState.firstItr = false;
// Check to see if the scaled time is less than the duration on the first iteration
if (scaledTime > animationState.duration) {
usedTime = animationState.duration;
}
}
// Lines uses the given easing function to get their end values
var currX = animationState.easing(usedTime, animationState.startValue.x, animationState.endValue.x -
animationState.startValue.x, animationState.duration);
var currY = animationState.easing(usedTime, animationState.startValue.y, animationState.endValue.y -
animationState.startValue.y, animationState.duration);
if (animationState.duration === 0) {
currX = animationState.endValue.x;
currY = animationState.endValue.y;
}
currSegment.endX = currX + animationState.offsetX;
currSegment.endY = currY + animationState.offsetY;
}
animationState.changedSegments.push(currSegment);
// List will freeze after being changed once so a copy is made and used every tick
animationState.newGeometry.figures.first().segments = animationState.changedSegments.copy();
if (scaledTime > animationState.duration) {
var tempList = new go.List();
animationState.startValue = animationState.endValue;
animationState.elaDuration += scaledTime;
// Reset first Iteration so that the correct points will be set and used throughout that given part of the animation
animationState.firstItr = true;
animationState.currSegment++;
currptX = currSegment.endX - startValX;
currptY = currSegment.endY - startValY;
// Do not remove the segment the last time in order to build up the geometry
shouldPop = false;
}
currptX = currSegment.endX - startValX;
currptY = currSegment.endY - startValY;
animationState.currX = currptX;
animationState.currY = currptY;
// Only set the delta X and Ys every tick as the makeGeometry is called multiple times within a tick
if (!animationState.hasTicked) {
animationState.delX = currptX - prevptX;
animationState.delY = currptY - prevptY;
animationState.hasTicked = true;
}
prevptY = currptY;
prevptX = currptX;
// Remove last segment from the changedSegments list because it will be added the next time makeGeometry is called
if (shouldPop) {
animationState.changedSegments.pop();
}
return animationState.newGeometry;
}
return tempMakeGeometry;
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<!-- The DIV for the Diagram needs an explicit size or else we won't see anything.
This also adds a border to help see the edges of the viewport. -->
<div id="myDiagramDiv" style="border: solid 1px black; width:800px; height:800px"></div>
<button id="orth">Load Orthogonal Links</button>
<button id="bez">Load Bezier Links</button>
<button id="linear">Load Linear Links</button><br> Duration: <input type="range" min="100" max="3000" value="500"
class="slider" id="duration">
<span id="durationDisplay"></span><br> Corner: <input type="range" min="0" max="100" value="0" class="slider"
id="corner">
<span id="cornerDisplay"></span><br> Curvature: <input type="range" min="-50" max="50" value="40" class="slider"
id="curvature">
<span id="curvatureDisplay"></span><br>
<p>
This sample demonstrates defining custom animation effects with <a>AnimationManager,defineAnimationEffect</a>,
and chaining animations to recursively animate a tree.
</p>
</div>
</div>
<!-- * * * * * * * * * * * * * -->
<!-- End of GoJS sample code -->
</div>
</body>
<!-- This script is part of the gojs.net website, and is not needed to run the sample -->
<script src="../assets/js/goSamples.js"></script>
</html>