//
// Copyright © 2011-2025 Cambridge Intelligence Limited.
// All rights reserved.
//
// Sample Code
//
//! Animate properties to highlight interesting items.
import KeyLines from "keylines";
import data from "./animation-data.js";
let chart;
const palette = [
"rgb(166, 86, 40)",
"rgb(247, 129, 191)",
"rgb(55, 126, 184)",
"rgb(255, 127, 0)",
"rgb(77, 175, 74)",
"rgb(228, 26, 28)",
"rgb(152, 78, 163)",
"rgb(255, 255, 51)",
"rgb(153, 153, 153)",
"rgb(75, 75, 75)",
"rgb(200, 200, 200)",
];
const numberOfNodes = 11;
const interval = 1000;
const delta = 50;
let nextPing = 0;
// Return a random integer from 0 to n-1
function randInt(n) {
return Math.floor(Math.random() * n);
}
// Animate properties depending on which checkboxes are checked
async function doAnimation() {
const items = [];
// Defining Buttons
const animPos = document.getElementById("animpos").checked;
const animSize = document.getElementById("animsize").checked;
const nodeColour = document.getElementById("nodecolour").checked;
const animWidth = document.getElementById("animwidth").checked;
// Animate nodes
if (animPos || animSize || nodeColour) {
chart.each({ type: "node" }, (node) => {
const item = { id: node.id };
if (animPos) {
item.x = node.x + randInt(2 * delta) - delta;
item.y = node.y + randInt(2 * delta) - delta;
}
if (animSize) {
item.e = (randInt(20) + 5) / 10;
}
if (nodeColour) {
const newColour = palette[randInt(palette.length)];
if (node.fi) {
// set a random colour from the palette to the font icon node
item.fi = { c: newColour };
} else {
item.c = newColour;
}
}
items.push(item);
});
}
// Animate links
if (animWidth) {
chart.each({ type: "link" }, (link) => {
// We don't want the width of a link to be larger than the
// Smallest width of the nodes it's linking
const end1 = chart.getItem(link.id1);
const end2 = chart.getItem(link.id2);
const maxEnlargement = Math.min(end1.e, end2.e);
items.push({
id: link.id,
w: randInt(maxEnlargement * 20) + 1,
});
});
}
await chart.animateProperties(items, { time: interval });
// we call this function here so that it starts again once
// the animation is complete
doAnimation();
}
// Ping the next node in sequence
function doPing() {
const id = `n${nextPing}`;
const node = chart.getItem(id);
// We make the halo colour match the node colour
chart.ping(id, { c: node.fi ? node.fi.c : node.c });
// Increment nextPing, wrapping to zero when we reach numberOfNodes
nextPing = (nextPing + 1) % numberOfNodes;
}
function enableInteraction() {
doAnimation();
// Hook up the Ping button handler
const pingButton = document.getElementById("ping");
pingButton.addEventListener("click", () => {
doPing();
});
}
async function startKeyLines() {
const options = {
drag: {
links: false,
},
logo: "/images/Logo.png",
handMode: true,
iconFontFamily: "Font Awesome 5 Free",
};
chart = await KeyLines.create({
container: "klchart",
options,
});
chart.load(data);
chart.layout("organic", { animate: false, tightness: 2 });
enableInteraction();
}
function loadKeyLines() {
document.fonts.load('24px "Font Awesome 5 Free"').then(startKeyLines);
}
window.addEventListener("DOMContentLoaded", loadKeyLines);
<!DOCTYPE html>
<html lang="en" style="background-color: #2d383f;">
<head>
<meta charset="UTF-8">
<title>Animate Items</title>
<link rel="stylesheet" type="text/css" href="/css/keylines.css">
<link rel="stylesheet" type="text/css" href="/css/minimalsdk.css">
<link rel="stylesheet" type="text/css" href="/css/sdk-layout.css">
<link rel="stylesheet" type="text/css" href="/css/demo.css">
<link rel="stylesheet" type="text/css" href="/css/fontawesome5/fontawesome.css">
<link rel="stylesheet" type="text/css" href="/css/fontawesome5/solid.css">
<script src="/vendor/jquery.js" defer type="text/javascript"></script>
<script id="keylines" src="/public/keylines.umd.cjs" defer type="text/javascript"></script>
<script src="/animation.js" crossorigin="use-credentials" defer type="module"></script>
</head>
<body>
<div class="chart-wrapper demo-cols">
<div class="tab-content-panel flex1" id="lhs" data-tab-group="rhs">
<div class="toggle-content is-visible" id="lhsVisual" style="width:100%; height: 100%;">
<div class="klchart" id="klchart">
</div>
</div>
</div>
<div class="rhs citext closed" id="demorhscontent">
<div class="title-bar">
<svg viewBox="0 0 360 60" style="max-width: 360px; max-height: 60px; font-size: 24px; font-family: Raleway; flex: 1;">
<text x="0" y="38" style="width: 100%;">Animate Items</text>
</svg>
</div>
<div class="tab-content-panel" data-tab-group="rhs">
<div class="toggle-content is-visible tabcontent" id="controlsTab">
<p>Choose the properties to animate.</p>
<form autocomplete="off" onsubmit="return false" id="rhsForm">
<div class="cicontent">
<fieldset>
<label class="checkbox">
<input id="animsize" type="checkbox" checked="true">Node enlargement
</label>
<label class="checkbox">
<input id="animpos" type="checkbox">Node position
</label>
<label class="checkbox">
<input id="animwidth" type="checkbox">Link width
</label>
<label class="checkbox">
<input id="nodecolour" type="checkbox">Node colour
</label>
</fieldset>
<fieldset>
<div class="form-inline">
<input class="btn btn-spaced" type="button" value="Ping" id="ping">
</div>
</fieldset>
</div>
</form>
</div>
</div>
</div>
</div>
<div id="moreParent">
<div id="moreContainer">
</div>
</div>
<div id="lazyScripts">
</div>
</body>
</html>
Loading source
