create-gojs-kit
Version:
A CLI for downloading GoJS samples, extensions, and docs
340 lines (301 loc) • 14.5 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 donut chart in each node." />
<meta itemprop="description" content="A donut chart in each node." />
<meta property="og:description" content="A donut chart in each node." />
<meta name="twitter:description" content="A donut chart in each node." />
<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="Donut Charts Implemented as Shapes in Nodes" />
<meta property="og:title" content="Donut Charts Implemented as Shapes in Nodes" />
<meta name="twitter:title" content="Donut Charts Implemented as Shapes in Nodes" />
<meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/donutcharts.png" />
<meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/donutcharts.png" />
<meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/donutcharts.png" />
<meta property="og:url" content="https://gojs.net/latest/samples/donutCharts.html" />
<meta property="twitter:url" content="https://gojs.net/latest/samples/donutCharts.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:type" content="website" />
<meta property="twitter:domain" content="gojs.net" />
<title>
Donut Charts Implemented as Shapes in Nodes | 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">
function init() {
myDiagram = new go.Diagram('myDiagramDiv');
const Inner = 40; // inner radius
const Thickness = 30; // Inner + Thickness is outer radius
myDiagram.nodeTemplate =
new go.Node('Spot')
.add(
new go.Panel({
itemTemplate:
new go.Panel('Spot', {
toolTip:
go.GraphObject.build('ToolTip')
.add(
new go.TextBlock()
.bind('text', '', d => `${d.text}\n#: ${d.value}\n${(d.sweep / 3.6).toFixed(1)}%`)
)
})
.add(
new go.Shape({ fill: '#00000020', stroke: 'white' }) // this always occupies the full circle
.bind('geometry', '', makeAnnularWedge)
.bind('fill', 'color'),
new go.TextBlock({
width: Thickness,
textAlign: 'center',
font: '8pt sans-serif'
})
.bind('alignment', '', computeTextAlignment)
//.bind("angle", "", ensureUpright), // does the text need to be rotated?
.bind('text', '', d => d.value + '\n' + Math.round(d.sweep / 3.6) + '%')
)
})
.bind('itemArray', 'values', normalizeData),
new go.TextBlock({
maxSize: new go.Size(2 * Inner, 2 * Inner),
font: 'bold 10pt sans-serif'
})
.bind('text')
);
function normalizeData(arr) {
const details = [];
let total = 0;
arr.forEach(item => {
if (typeof item === 'object') {
details.push(Object.assign({}, item));
total += Math.max(item.value, 0.001);
} else {
details.push({ value: item });
total += Math.max(Number(item), 0.001);
}
});
let angle = 0;
details.forEach(item => {
item.angle = angle;
const sw = (item.value / total) * 360;
item.sweep = sw;
angle += sw;
item.radius = Inner;
item.thick = Thickness;
});
return details;
}
function makeAnnularWedge(data) {
const angle = typeof data.angle === 'number' ? data.angle : 0; // start angle
const sweep = typeof data.sweep === 'number' ? data.sweep : 90;
const radius = typeof data.radius === 'number' ? data.radius : Inner; // inner radius
const thick = typeof data.thick === 'number' ? data.thick : Thickness;
// the Geometry will be centered about (0,0)
const outer = radius + thick; // the outer radius
const inner = radius;
const p = new go.Point(outer, 0).rotate(angle);
const q = new go.Point(inner, 0).rotate(angle + sweep);
const rad = Inner + Thickness;
const geo = new go.Geometry()
.add(new go.PathFigure(-rad, -rad)) // always make sure the Geometry includes the top left corner
.add(new go.PathFigure(rad, rad)) // and the bottom right corner of the whole circular area
.add(
new go.PathFigure(p.x, p.y) // start at outer corner, go clockwise
.add(new go.PathSegment(go.SegmentType.Arc, angle, sweep, 0, 0, outer, outer))
.add(new go.PathSegment(go.SegmentType.Line, q.x, q.y)) // to opposite inner corner, then anticlockwise
.add(new go.PathSegment(go.SegmentType.Arc, angle + sweep, -sweep, 0, 0, inner, inner).close())
);
return geo;
}
function computeTextAlignment(data) {
const angle = typeof data.angle === 'number' ? data.angle : 0; // start angle
const sweep = typeof data.sweep === 'number' ? data.sweep : 90;
const radius = typeof data.radius === 'number' ? data.radius : Inner; // inner radius
const thick = typeof data.thick === 'number' ? data.thick : Thickness;
const p = new go.Point(radius + thick / 2, 0).rotate(angle + sweep / 2);
return new go.Spot(0.5, 0.5, p.x, p.y);
}
// only used if rotating the text labels
function ensureUpright(data) {
const angle = typeof data.angle === 'number' ? data.angle : 0;
const sweep = typeof data.sweep === 'number' ? data.sweep : 90;
const a = angle + sweep / 2;
if (a > 90 && a < 270) return a + 180;
return a;
}
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, text: 'Alpha', values: [10, 20, 30] },
{ key: 2, text: 'Beta', values: [{ value: 1, color: 'goldenrod' }] },
{
key: 3,
text: 'Gamma',
values: [
{ value: 3, color: '#EFFFFD' },
{ value: 9, color: '#B8FFF9' },
{ value: 12, color: '#85F4FF' },
{ value: 7, color: '#42C2FF' }
]
},
{
key: 4,
text: 'Delta',
values: [
{ value: 30, color: 'pink' },
{ value: 90, color: 'lightgreen' }
]
}
],
[
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 3, to: 4 },
{ from: 4, to: 1 }
]
);
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<div
id="myDiagramDiv"
style="border: solid 1px black; width: 100%; height: 600px"></div>
<p>
Each node contains a Position Panel containing a variable number of elements
that get Geometry values such that each shows a data value as an annular bar
in a circle. One can also specify the colors of the bars. The
<a>Panel.itemTemplate</a> also includes a <a>TextBlock</a> that shows its
actual value as well as the percentage that it represents of the whole Array
of data values.
</p>
<p>
For more sophisticated charts within nodes, see the
<a href="canvases.html">Canvas Charts</a> sample.
</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>Item Arrays</h4>
<p>
It is sometimes useful to display a variable number of elements in a node by data binding to a JavaScript Array.
In GoJS, this is simply achieved by binding (or setting) <a href="../api/symbols/Panel.html#itemArray" target="api">Panel.itemArray</a>.
The <a href="../api/symbols/Panel.html" target="api">Panel</a> will create an element in the panel for each value in the Array.
More information can be found in the <a href="../intro/itemArrays.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#itemarrays">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>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
</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>