UNPKG

create-gojs-kit

Version:

A CLI for downloading GoJS samples, extensions, and docs

333 lines (293 loc) 14.2 kB
<!DOCTYPE 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 custom Link geometry that draws arrowheads at the end of each segment of orthogonally routed links." /> <meta itemprop="description" content="A custom Link geometry that draws arrowheads at the end of each segment of orthogonally routed links." /> <meta property="og:description" content="A custom Link geometry that draws arrowheads at the end of each segment of orthogonally routed links." /> <meta name="twitter:description" content="A custom Link geometry that draws arrowheads at the end of each segment of orthogonally routed links." /> <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="Multiple Arrowheads Along Link Path" /> <meta property="og:title" content="Multiple Arrowheads Along Link Path" /> <meta name="twitter:title" content="Multiple Arrowheads Along Link Path" /> <meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/multiarrow.png" /> <meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/multiarrow.png" /> <meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/multiarrow.png" /> <meta property="og:url" content="https://gojs.net/latest/samples/multiArrow.html" /> <meta property="twitter:url" content="https://gojs.net/latest/samples/multiArrow.html" /> <meta name="twitter:card" content="summary_large_image" /> <meta property="og:type" content="website" /> <meta property="twitter:domain" content="gojs.net" /> <title> Multiple Arrowheads Along Link Path | 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"> // Produce a Geometry that includes an arrowhead at the end of each segment. // This only works with orthogonal non-Bezier routing. class MultiArrowLink extends go.Link { constructor(init) { super(); this.routing = go.Routing.Orthogonal; if (init) Object.assign(this, init); } // produce a Geometry from the Link's route makeGeometry() { // get the Geometry created by the standard behavior const geo = super.makeGeometry(); if (geo.type !== go.GeometryType.Path || geo.figures.length === 0) return geo; const mainfig = geo.figures.elt(0); // assume there's just one PathFigure const mainsegs = mainfig.segments; const arrowLen = 8; // length for each arrowhead const arrowWid = 3; // actually half-width of each arrowhead let fx = mainfig.startX; let fy = mainfig.startY; for (let i = 0; i < mainsegs.length; i++) { const a = mainsegs.elt(i); // assume each arrowhead is a simple triangle const ax = a.endX; const ay = a.endY; let bx = ax; let by = ay; let cx = ax; let cy = ay; if (fx < ax - arrowLen) { bx -= arrowLen; by += arrowWid; cx -= arrowLen; cy -= arrowWid; } else if (fx > ax + arrowLen) { bx += arrowLen; by += arrowWid; cx += arrowLen; cy -= arrowWid; } else if (fy < ay - arrowLen) { bx -= arrowWid; by -= arrowLen; cx += arrowWid; cy -= arrowLen; } else if (fy > ay + arrowLen) { bx -= arrowWid; by += arrowLen; cx += arrowWid; cy += arrowLen; } geo.add( new go.PathFigure(ax, ay, true) .add(new go.PathSegment(go.SegmentType.Line, bx, by)) .add(new go.PathSegment(go.SegmentType.Line, cx, cy).close()) ); fx = ax; fy = ay; } return geo; } } // end of MultiArrowLink class function init() { myDiagram = new go.Diagram('myDiagramDiv', { layout: new go.ForceDirectedLayout(), 'undoManager.isEnabled': true }); myDiagram.nodeTemplate = new go.Node('Auto', { locationSpot: go.Spot.Center }) .add( new go.Shape({ figure: 'RoundedRectangle', parameter1: 10, fill: 'orange', // default fill color portId: '', fromLinkable: true, fromSpot: go.Spot.AllSides, toLinkable: true, toSpot: go.Spot.AllSides, cursor: 'pointer' }) .bind('fill', 'color'), new go.TextBlock({ margin: 10, font: 'bold 12pt sans-serif' }) .bind('text') ); myDiagram.linkTemplate = new MultiArrowLink({ // subclass of Link, defined above relinkableFrom: true, relinkableTo: true, reshapable: true, resegmentable: true }) .add( new go.Shape({ isPanelMain: true }) .bind('fill', 'color') // no arrowhead is defined here -- they are hard-coded in MultiArrowLink.makeGeometry ); // create a few nodes and links myDiagram.model = new go.GraphLinksModel( [ { key: 1, text: 'one', color: 'lightyellow' }, { key: 2, text: 'two', color: 'brown' }, { key: 3, text: 'three', color: 'green' }, { key: 4, text: 'four', color: 'slateblue' }, { key: 5, text: 'five', color: 'aquamarine' }, { key: 6, text: 'six', color: 'lightgreen' }, { key: 7, text: 'seven' } ], [ { from: 5, to: 6, color: 'orange' }, { from: 1, to: 2, color: 'red' }, { from: 1, to: 3, color: 'blue' }, { from: 1, to: 4, color: 'goldenrod' }, { from: 2, to: 5, color: 'fuchsia' }, { from: 3, to: 5, color: 'green' }, { from: 4, to: 5, color: 'black' }, { from: 6, to: 7 } ] ); } window.addEventListener('DOMContentLoaded', init); </script> <div id="sample"> <div id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 700px; min-width: 200px"></div> <p>This sample demonstrates customization of the <a>Link</a> <a>Shape</a>'s <a>Geometry</a>.</p> <p> The MultiArrowLink class in this sample inherits from Link and overrides the <a>Link.makeGeometry</a> method to add arrowheads at the end of each interior segment, assuming that the route is orthogonal. </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>Links</h4> <p> The <a href="../api/symbols/Link.html" target="api">Link</a> class is used to implement a visual relationship between nodes. Links are normally created by the presence of link data objects in the <a href="../api/symbols/GraphLinksModel.html#linkDataArray" target="api">GraphLinksModel.linkDataArray</a> or by a parent key reference as the value of the <a href="../api/symbols/TreeModel.html#nodeParentKeyProperty" target="api">TreeModel.nodeParentKeyProperty</a> of a node data object in a <a href="../api/symbols/TreeModel.html" target="api">TreeModel</a>. More information can be found in the <a href="../intro/links.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#links">Related samples</a> </p> <hr> <!-- 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>Geometry Path Strings</h4> <p> The <b>GoJS</b> <a href="../api/symbols/Geometry.html" target="api">Geometry</a> class controls the "shape" of a <a href="../api/symbols/Shape.html" target="api">Shape</a>, whereas the <a href="../api/symbols/Shape.html#fill" target="api">Shape.fill</a> and <a href="../api/symbols/Shape.html#stroke" target="api">Shape.stroke</a> and other shape properties control the colors and appearance of the shape. For common shape figures, there are predefined geometries that can be used by setting <a href="../api/symbols/Shape.html#figure" target="api">Shape.figure</a>. However one can also define custom geometries. </p> <p> One can construct any Geometry by allocating and initializing a <a href="../api/symbols/Geometry.html" target="api">Geometry</a> of at least one <a href="../api/symbols/PathFigure.html" target="api">PathFigure</a> holding some <a href="../api/symbols/PathSegment.html" target="api">PathSegment</a>s. But you may find that using the string representation of a Geometry is easier to write and save in a database. Use the static method <a href="../api/symbols/Geometry.html#parse" target="api">Geometry.parse</a> or the <a href="../api/symbols/Shape.html#geometryString" target="api">Shape.geometryString</a> property to transform a geometry path string into a <a href="../api/symbols/Geometry.html" target="api">Geometry</a> object. </p> <p> More information can be found in the <a href="../intro/geometry.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#geometries">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>