create-gojs-kit
Version:
A CLI for downloading GoJS samples, extensions, and docs
345 lines (305 loc) • 14.9 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="A continuously operating ForceDirectedLayout lets the user push and pull nodes around." />
<meta itemprop="description" content="A continuously operating ForceDirectedLayout lets the user push and pull nodes around." />
<meta property="og:description" content="A continuously operating ForceDirectedLayout lets the user push and pull nodes around." />
<meta name="twitter:description" content="A continuously operating ForceDirectedLayout lets the user push and pull nodes around." />
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="../assets/css/style.css">
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
<meta itemprop="name" content="Interactive Force Directed Layout During Dragging" />
<meta property="og:title" content="Interactive Force Directed Layout During Dragging" />
<meta name="twitter:title" content="Interactive Force Directed Layout During Dragging" />
<meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/interactiveforce.png" />
<meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/interactiveforce.png" />
<meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/interactiveforce.png" />
<meta property="og:url" content="https://gojs.net/latest/samples/interactiveForce.html" />
<meta property="twitter:url" content="https://gojs.net/latest/samples/interactiveForce.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:type" content="website" />
<meta property="twitter:domain" content="gojs.net" />
<title>
Interactive Force Directed Layout During Dragging | GoJS Diagramming Library
</title>
</head>
<body>
<!-- This top nav is not part of the sample code -->
<nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200">
<div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4">
<a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo"
href="../">
GoJS
</a>
<div class="relative">
<button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation">
<svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div id="topnavList" class="hidden md:block">
<div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold
md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit">
<a href="../learn/">Learn</a>
<a href="../samples/">Samples</a>
<a href="../intro/">Intro</a>
<a href="../api/">API</a>
<a href="../download.html">Download</a>
<a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a>
<a id="tc" href="https://nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a>
<a id="tb" href="https://nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a>
</div>
</div>
</div>
</div>
</nav>
<script>
window.addEventListener("DOMContentLoaded", function () {
// topnav
var topButton = document.getElementById("topnavButton");
var topnavList = document.getElementById("topnavList");
if (topButton && topnavList) {
topButton.addEventListener("click", function (e) {
topnavList
.classList
.toggle("hidden");
e.stopPropagation();
});
document.addEventListener("click", function (e) {
// if the clicked element isn't the list, close the list
if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) {
topButton.click();
}
});
// set active <a> element
var url = window
.location
.href
.toLowerCase();
var aTags = topnavList.getElementsByTagName('a');
for (var i = 0; i < aTags.length; i++) {
var lowerhref = aTags[i]
.href
.toLowerCase();
if (lowerhref.endsWith('.html'))
lowerhref = lowerhref.slice(0, -5);
if (url.startsWith(lowerhref)) {
aTags[i]
.classList
.add('active');
break;
}
}
}
});
</script>
<div class="flex flex-col prose">
<div class="w-full max-w-screen-xl mx-auto">
<!-- * * * * * * * * * * * * * -->
<!-- Start of GoJS sample code -->
<script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script>
<div id="allSampleContent" class="p-4 w-full">
<script id="code">
// This variation on ForceDirectedLayout does not move any selected Nodes
// but does move all other nodes (vertexes).
class ContinuousForceDirectedLayout extends go.ForceDirectedLayout {
constructor(init) {
super();
if (init) Object.assign(this, init);
}
isFixed(v) {
return v.node.isSelected;
}
// optimization: reuse the ForceDirectedNetwork rather than re-create it each time
doLayout(coll) {
if (!this._isObserving) {
this._isObserving = true;
// cacheing the network means we need to recreate it if nodes or links have been added or removed or relinked,
// so we need to track structural model changes to discard the saved network.
this.diagram.addModelChangedListener(e => {
// modelChanges include a few cases that we don't actually care about, such as
// "nodeCategory" or "linkToPortId", but we'll go ahead and recreate the network anyway.
// Also clear the network when replacing the model.
if (e.modelChange !== '' || (e.change === go.ChangeType.Transaction && e.propertyName === 'StartingFirstTransaction')) {
this.network = null;
}
});
}
var net = this.network;
if (net === null) {
// the first time, just create the network as normal
this.network = net = this.makeNetwork(coll);
} else {
// but on reuse we need to update the LayoutVertex.bounds for selected nodes
this.diagram.nodes.each(n => {
var v = net.findVertex(n);
if (v !== null) v.bounds = n.actualBounds;
});
}
// now perform the normal layout
super.doLayout(coll);
// doLayout normally discards the LayoutNetwork by setting Layout.network to null;
// here we remember it for next time
this.network = net;
// in the future, don't allow nodes to move as far
//this.initialTemperature = x => 10;
}
}
// end ContinuousForceDirectedLayout
function init() {
myDiagram = new go.Diagram('myDiagramDiv', {
initialAutoScale: go.AutoScale.Uniform, // an initial automatic zoom-to-fit
contentAlignment: go.Spot.Center, // align document to the center of the viewport
layout: new ContinuousForceDirectedLayout({ // automatically spread nodes apart while dragging
defaultSpringLength: 150
}),
// do an extra layout at the end of a move
SelectionMoved: e => e.diagram.layout.invalidateLayout()
});
// dragging a node invalidates the Diagram.layout, causing a layout during the drag
myDiagram.toolManager.draggingTool.doMouseMove = function () {
// method override must be function, not =>
go.DraggingTool.prototype.doMouseMove.call(this);
if (this.isActive) this.diagram.layout.doLayout(this.diagram);
};
// define each Node's appearance
myDiagram.nodeTemplate =
new go.Node('Auto') // the whole node panel
// define the node's outer shape, which will surround the TextBlock
.add(
new go.Shape('Circle', {
fill: 'CornflowerBlue',
stroke: 'black',
spot1: new go.Spot(0, 0, 5, 5),
spot2: new go.Spot(1, 1, -5, -5)
}),
new go.TextBlock({
font: 'bold 10pt helvetica, bold arial, sans-serif',
textAlign: 'center',
maxSize: new go.Size(100, NaN)
})
.bind('text')
);
// the rest of this app is the same as samples/conceptMap.html
// replace the default Link template in the linkTemplateMap
myDiagram.linkTemplate =
new go.Link() // the whole link panel
.add(
new go.Shape({ stroke: 'black' }), // the link shape
new go.Shape({ toArrow: 'standard', stroke: null }), // the arrowhead
new go.Panel('Auto')
.add(
new go.Shape({ // the label background, which becomes transparent around the edges
fill: new go.Brush('Radial', { 0: 'rgb(240, 240, 240)', 0.3: 'rgb(240, 240, 240)', 1: 'rgba(240, 240, 240, 0)' }),
stroke: null
}),
new go.TextBlock({ // the label text
textAlign: 'center',
font: '10pt helvetica, arial, sans-serif',
stroke: '#555555',
margin: 4
})
.bind('text')
)
);
// create the model for the concept map
var nodeDataArray = [
{ key: 1, text: 'Concept Maps' },
{ key: 2, text: 'Organized Knowledge' },
{ key: 3, text: 'Context Dependent' },
{ key: 4, text: 'Concepts' },
{ key: 5, text: 'Propositions' },
{ key: 6, text: 'Associated Feelings or Affect' },
{ key: 7, text: 'Perceived Regularities' },
{ key: 8, text: 'Labeled' },
{ key: 9, text: 'Hierarchically Structured' },
{ key: 10, text: 'Effective Teaching' },
{ key: 11, text: 'Crosslinks' },
{ key: 12, text: 'Effective Learning' },
{ key: 13, text: 'Events (Happenings)' },
{ key: 14, text: 'Objects (Things)' },
{ key: 15, text: 'Symbols' },
{ key: 16, text: 'Words' },
{ key: 17, text: 'Creativity' },
{ key: 18, text: 'Interrelationships' },
{ key: 19, text: 'Infants' },
{ key: 20, text: 'Different Map Segments' }
];
var linkDataArray = [
{ from: 1, to: 2, text: 'represent' },
{ from: 2, to: 3, text: 'is' },
{ from: 2, to: 4, text: 'is' },
{ from: 2, to: 5, text: 'is' },
{ from: 2, to: 6, text: 'includes' },
{ from: 2, to: 10, text: 'necessary\nfor' },
{ from: 2, to: 12, text: 'necessary\nfor' },
{ from: 4, to: 5, text: 'combine\nto form' },
{ from: 4, to: 6, text: 'include' },
{ from: 4, to: 7, text: 'are' },
{ from: 4, to: 8, text: 'are' },
{ from: 4, to: 9, text: 'are' },
{ from: 5, to: 9, text: 'are' },
{ from: 5, to: 11, text: 'may be' },
{ from: 7, to: 13, text: 'in' },
{ from: 7, to: 14, text: 'in' },
{ from: 7, to: 19, text: 'begin\nwith' },
{ from: 8, to: 15, text: 'with' },
{ from: 8, to: 16, text: 'with' },
{ from: 9, to: 17, text: 'aids' },
{ from: 11, to: 18, text: 'show' },
{ from: 12, to: 19, text: 'begins\nwith' },
{ from: 17, to: 18, text: 'needed\nto see' },
{ from: 18, to: 20, text: 'between' }
];
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<div id="myDiagramDiv" style="background-color: whitesmoke; border: solid 1px black; width: 100%; height: 700px"></div>
<p>As you drag a node around, the custom <a>ForceDirectedLayout</a> operates continuously, causing other nodes to be pushed aside or pulled along.</p>
<p>The graph is exactly the same as the <a href="conceptMap.html">Concept Map</a> sample. But the node template is different, and the layout is different.</p>
</div>
</div>
<!-- * * * * * * * * * * * * * -->
<!-- End of GoJS sample code -->
</div>
<div id="allTagDescriptions" class="p-4 w-full max-w-screen-xl mx-auto">
<hr/>
<h3 class="text-xl">GoJS Features in this sample</h3>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Force Directed Layout</h4>
<p>
This predefined layout treats the graph as if it were a system of physical bodies with forces acting on and between them.
The layout iteratively moves nodes and links to minimize the total sum of forces on each node. The resulting layout will normally not contain
overlapping Nodes, excluding cases where the graph is densely interconnected.
More information can be found in the <a href="../intro/layouts.html#ForceDirectedLayout">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#force-directed">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Custom Layouts</h4>
<p>
GoJS allows for the creation of custom layouts to meet specific needs.
</p>
<p>
There are also many layouts that are extensions -- not predefined in the <code>go.js</code> or <code>go-debug.js</code> library,
but available as source code in one of the three extension directories, with some documentation and corresponding samples.
More information can be found in the <a href="../intro/layouts.html#CustomLayouts">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#customlayout">Related samples</a>
</p>
<hr>
</div>
</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>