Search

Animate Link Widths

Time

Animate link widths to visualise the volume of data in the time bar.

Animate Link Widths
View live example →

This demo shows call traffic inside a small network over a 12 month period.

Link widths are a good way to represent different volumes of traffic between nodes. Seeing link widths change dynamically over time gives you a clear overview of events, and highlights unusual patterns that you can drill into further.

The time bar lets you cycle through the year and easily spot who phoned who, and how often. You can zoom into the time bar histogram to focus on calls that took place at a certain time on a specific date between nodes of interest.

Key functions used:

import KeyLines from "keylines";

import data from "./data.js";

let chart;
let timebar;

const MAX_LINK_WIDTH = 10;

const resetButton = document.getElementById("reset");
const startStopButton = document.getElementById("start-stop");

function scaleLinks() {
  const widthList = [];
  let maxWidth = -1;

  // get the current range and parse it to a timestamp
  const timeRange = timebar.range();
  const itemsInRange = timebar.getIds(timeRange.dt1, timeRange.dt2);
  itemsInRange.forEach((id) => {
    const item = chart.getItem(id);

    // calculate the number of times each connection occured in the current range
    if (item && item.type === "link") {
      let width = 0;
      item.dt.forEach((time) => {
        if (time >= timeRange.dt1 && time <= timeRange.dt2) {
          width += 1;
        }
      });
      maxWidth = Math.max(maxWidth, width);
      widthList.push({ id, w: width });
    }
  });

  // scale all widths relative to widest link
  widthList.forEach((item) => {
    item.w = (MAX_LINK_WIDTH * item.w) / maxWidth;
  });
  chart.animateProperties(widthList, { time: 100 });
}

function reset() {
  timebar.pause();
  timebar.range(new Date(2000, 0), new Date(2000, 1), { animate: false });
}

function startStop() {
  if (startStopButton.innerHTML === "Play") {
    timebar.play();
  } else {
    timebar.pause();
  }
}

async function klReady() {
  // load the data (defined in separate js file) into both the chart and time bar
  timebar.load(data);
  reset();
  chart.load(data);

  await chart.filter(timebar.inRange, { animate: false, type: "link" });
  scaleLinks();

  chart.layout("organic");

  // When the time bar range changes
  timebar.on("change", async () => {
    scaleLinks();
    // filter the chart to show only items in the new range
    await chart.filter(timebar.inRange, { animate: false, type: "link" });
    chart.layout("organic", { mode: "adaptive", time: 500, fixed: ["14", "15"] });
  });

  timebar.on("end", timebar.pause);
  timebar.on("play", () => {
    startStopButton.innerHTML = "Pause";
  });
  timebar.on("pause", () => {
    startStopButton.innerHTML = "Play";
  });
  resetButton.addEventListener("click", reset);
  startStopButton.addEventListener("click", startStop);
}

async function loadKeyLines() {
  await document.fonts.load('24px "Font Awesome 5 Free"');

  const options = {
    logo: "/images/Logo.png",
    handMode: true,
    overview: { icon: false, shown: false },
    iconFontFamily: "Font Awesome 5 Free",
  };

  [chart, timebar] = await KeyLines.create([
    { container: "klchart", options },
    { container: "kltimebar", type: "timebar" },
  ]);

  klReady();
}

window.addEventListener("DOMContentLoaded", loadKeyLines);
import KeyLines from "keylines";

export default {
  type: "LinkChart",
  items: [
    {
      type: "node",
      id: 1,
      t: "(020) xxxx 2781",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 14,
      id1: 1,
      a1: true,
      dt: [
        949120540713, 948854251006, 948370724603, 946722540092, 947457913508, 948587667519,
        947039610840, 951312458835, 952196349612, 955586932529, 958878251442, 962053790907,
        964226213512, 967133085073, 968735438922, 972042033625, 974425518500, 974783881627,
        974983227932, 974556970943, 974180170083, 974431294228, 975286367877, 976725625360,
      ],
      type: "link",
      id: "1-14",
    },
    {
      id2: 14,
      id1: 1,
      a2: true,
      dt: [958263499173, 977220151711, 971055649757, 971324729157, 960304110368],
      type: "link",
      id: "1-14-2",
    },
    {
      type: "node",
      id: 2,
      t: "(020) xxxx 5697",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 14,
      id1: 2,
      a1: true,
      dt: [
        946830798790, 950330393027, 952127005380, 954780761820, 959377757958, 961211311508,
        963677582593, 967611052898, 968392906195, 971406168424, 971521050776, 972156038035,
        971369588716, 972044236062, 971832965750, 971752493456, 974148717948, 973491509704,
        973688870185, 975139355792, 975360509904, 975038478227, 974848625408, 977853189901,
      ],
      type: "link",
      id: "2-14",
    },
    {
      id2: 14,
      id1: 2,
      a2: true,
      dt: [960275656342, 952557144649, 976846124722, 959071556936, 951181768093],
      type: "link",
      id: "2-14-2",
    },
    {
      type: "node",
      id: 3,
      t: "(020) xxxx 0435",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 14,
      id1: 3,
      a1: true,
      dt: [
        947097887182, 950063326910, 954575469917, 955109642881, 958284798440, 961783891422,
        962354867065, 961850176505, 961433762510, 961651398947, 961555068503, 962297684520,
        963791231209, 967723180279, 968366058924, 972688719610, 971899814770, 971438198680,
        971139068337, 970932653212, 971306940516, 970874920867, 975604368287, 977518304142,
      ],
      type: "link",
      id: "3-14",
    },
    {
      id2: 14,
      id1: 3,
      a2: true,
      dt: [947816921816, 976806439810, 949104589999, 952280201759, 961451309789],
      type: "link",
      id: "3-14-2",
    },
    {
      type: "node",
      id: 4,
      t: "(020) xxxx 7436",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 14,
      id1: 4,
      a1: true,
      dt: [
        947296872725, 949665463708, 953115981019, 956588595780, 956672752586, 955415496335,
        954949475278, 955866784937, 956057157958, 956667247892, 959649637161, 957787768957,
        958081348737, 958520201463, 958333199222, 958124361666, 957995989079, 961237018972,
        963411402939, 965922868236, 968518129379, 971757605569, 974991095145, 977912400283,
      ],
      type: "link",
      id: "4-14",
    },
    {
      id2: 14,
      id1: 4,
      a2: true,
      dt: [947246585259, 956120773473, 970973350090, 953965329250, 961896784133],
      type: "link",
      id: "4-14-2",
    },
    {
      type: "node",
      id: 5,
      t: "(020) xxxx 8166",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 14,
      id1: 5,
      a1: true,
      dt: [
        947636816499, 948036784973, 949261788009, 949086461685, 948875175611, 948198980385,
        948095511961, 949374998206, 952331454689, 952105820270, 953690088016, 953642506596,
        953938970095, 953204977525, 953922309487, 954996724402, 959297444056, 960216744545,
        964023335974, 963562403629, 964858227982, 963079617031, 963655809815, 963420230421,
        964289174244, 966243572303, 965774425654, 966491927107, 967525493608, 965713485223,
        965774839359, 965590954249, 969857653694, 969576501623, 970132682790, 969399897432,
        968247645720, 969473425093, 970126553144, 972899219866, 974111830684, 977787699278,
      ],
      type: "link",
      id: "5-14",
    },
    {
      id2: 14,
      id1: 5,
      a2: true,
      dt: [977964514447, 969015253002, 951005693001, 957225015612, 970400130371],
      type: "link",
      id: "5-14-2",
    },
    {
      type: "node",
      id: 6,
      t: "(020) xxxx 4387",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 14,
      id1: 6,
      a1: true,
      dt: [
        947744034783, 949837337834, 951030608560, 950878150636, 951127962900, 949907256078,
        951802319850, 950140474097, 952809825306, 956002115940, 958680642462, 960582045754,
        964440657134, 966748274877, 968205420606, 972218682697, 975401684185, 974923840500,
        974862501825, 973526190255, 975528874235, 975213101630, 974454802880, 977656124028,
        977673893191, 977416836407, 977391677873, 976800560911, 976253517725, 976910959605,
      ],
      type: "link",
      id: "6-14",
    },
    {
      id2: 14,
      id1: 6,
      a2: true,
      dt: [968491879984, 973821532901, 949698199251, 961313917602, 959890833509],
      type: "link",
      id: "6-14-2",
    },
    {
      type: "node",
      id: 7,
      t: "(020) xxxx 2720",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 14,
      id1: 7,
      a1: true,
      dt: [
        947288725973, 951560331834, 953999039852, 955177946388, 959365754864, 960464666151,
        963842650926, 966637956422, 969181136730, 968947655607, 968930626911, 968705895401,
        968124258748, 968282864008, 969164944156, 971170705051, 974930653132, 978076179900,
      ],
      type: "link",
      id: "7-14",
    },
    {
      id2: 14,
      id1: 7,
      a2: true,
      dt: [967125559989, 955347402281, 971104775072, 955195373540, 964693886781],
      type: "link",
      id: "7-14-2",
    },
    {
      type: "node",
      id: 8,
      t: "(020) xxxx 6380",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 14,
      id1: 8,
      a1: true,
      dt: [
        948536018925, 951657348969, 952064051220, 956263211415, 957963538850, 960807451536,
        964979720544, 966813274135, 968521052665, 971877620848, 973505055632, 977073144733,
      ],
      type: "link",
      id: "8-14",
    },
    {
      id2: 14,
      id1: 8,
      a2: true,
      dt: [963412039275, 958477923763, 970169248634, 976634060860, 965923478596],
      type: "link",
      id: "8-14-2",
    },
    {
      type: "node",
      id: 9,
      t: "(020) xxxx 6713",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 15,
      id1: 9,
      a1: true,
      dt: [
        948907940658, 950616060196, 953592067745, 954742719549, 958086431277, 961604099393,
        963675403792, 966365677624, 968458479961, 972351460258, 974317059347, 976918962282,
      ],
      type: "link",
      id: "9-15",
    },
    {
      id2: 15,
      id1: 9,
      a2: true,
      dt: [949809330586, 947434418292, 959414847956, 971640107036, 960035126581],
      type: "link",
      id: "9-15-2",
    },
    {
      type: "node",
      id: 10,
      t: "(020) xxxx 3605",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 15,
      id1: 10,
      a1: true,
      dt: [
        949112216329, 951948734486, 953932243789, 954723769506, 957654013146, 961170325541,
        963076382249, 967479744486, 969844775751, 972407605212, 973723702736, 978168739526,
      ],
      type: "link",
      id: "10-15",
    },
    {
      id2: 15,
      id1: 10,
      a2: true,
      dt: [977071775971, 965824352856, 964417780051, 969132521302, 969219934602],
      type: "link",
      id: "10-15-2",
    },
    {
      type: "node",
      id: 11,
      t: "(020) xxxx 6079",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 15,
      id1: 11,
      a1: true,
      dt: [
        947940802059, 951542226127, 952383572184, 953585952010, 952623333930, 953634380536,
        954131073083, 953095000565, 954252699095, 956288262145, 955692754818, 955470992113,
        955825690414, 956201772981, 956762389340, 956210685200, 959830160343, 960691057241,
        963600634958, 967731858874, 968361880257, 972350244938, 974307214341, 976598964334,
        977936941482, 977765310096, 977838779079, 976760791971, 977919326375, 978275634321,
      ],
      type: "link",
      id: "11-15",
    },
    {
      id2: 15,
      id1: 11,
      a2: true,
      dt: [952241372815, 967606388598, 959101190452, 972666780332, 953951539308],
      type: "link",
      id: "11-15-2",
    },
    {
      type: "node",
      id: 12,
      t: "(020) xxxx 7740",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 15,
      id1: 12,
      a1: true,
      dt: [
        949098675628, 950441665010, 952046758707, 954734230631, 957981133207, 961850409745,
        963316663448, 967481517424, 970217896883, 971838427320, 974364242687, 974294097647,
        975560841812, 974003154305, 973943275539, 973876854021, 974978858320, 976681958185,
      ],
      type: "link",
      id: "12-15",
    },
    {
      id2: 15,
      id1: 12,
      a2: true,
      dt: [958708728028, 954128951712, 950622125599, 954533243472, 949126054426],
      type: "link",
      id: "12-15-2",
    },
    {
      type: "node",
      id: 13,
      t: "(020) xxxx 9281",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: 15,
      id1: 13,
      a1: true,
      dt: [
        947934638447, 950504380121, 953368676836, 956418852126, 956335515136, 956358233612,
        956846466610, 957041719004, 957052601071, 955871780027, 959370044634, 960841474054,
        960770238063, 960265204600, 961224374154, 960632596647, 960494005431, 962196686411,
        963172229730, 965433634075, 969557782241, 970924197858, 975468888668, 977023641121,
      ],
      type: "link",
      id: "13-15",
    },
    {
      id2: 15,
      id1: 13,
      a2: true,
      dt: [965288763627, 959574955530, 972305281526, 961123374515, 968490106738],
      type: "link",
      id: "13-15-2",
    },
    {
      type: "node",
      id: 14,
      t: "(020) xxxx 8298",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: "15",
      id1: "14",
      a1: true,
      dt: [
        949083769627, 951846326556, 952925026393, 956645127225, 958198401637, 960803758925,
        965027974296, 967066814063, 970387451337, 972336342026, 974393840125, 976976392019,
      ],
      type: "link",
      id: "14-15",
    },
    {
      type: "node",
      id: 15,
      t: "(020) xxxx 1118",
      fi: {
        t: ".fas .fa-phone-square",
        c: "#25D366",
      },
    },
    {
      id2: "15",
      id1: "14",
      a1: true,
      dt: [
        948157482661, 949722002524, 954385091552, 955907378200, 959317503143, 961982917014,
        962938820918, 967685374623, 969871239288, 972362579909, 975401927868, 977773601740,
      ],
      type: "link",
      id: "14-15",
    },
  ],
};
<!doctype html>
<html lang="en" style="background-color: #2d383f">
  <head>
    <meta charset="utf-8" />
    <title>Animate Link Widths</title>
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/keylines.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/minimalsdk.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/sdk-layout.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/demo.css" />
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/fontawesome.css"
    />
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/solid.css"
    />
  </head>
  <body>
    <div id="klchart" class="klchart klchart-timebar"></div>
    <div id="kltimebar" class="kltimebar"></div>
    <script type="module" src="./code.js"></script>
  </body>
</html>

Terms of use

These terms do not alter or supersede any existing agreements between you (or your employer) and us.

By accessing or using any Content you agree to be bound by these Terms of Use. Please review these terms carefully before using the website.

The contents of this website, including but not limited to any text, code samples, API references, schemas, interactive tools, and other materials (collectively, the 'Content'), are made available for informational and internal evaluation purposes only. All intellectual property rights in the Content are reserved. No licence is granted to use the Content for any commercial purpose, or to copy, distribute, modify, reverse-engineer, or incorporate any part of the Content into any product or service, without our prior written consent.

This Content is provided “as is” and “as available,” without any representations, warranties, or guarantees of any kind, whether express or implied, including but not limited to implied warranties of merchantability, fitness for a particular purpose, non-infringement, or accuracy. To the fullest extent permitted by applicable law, we expressly exclude and disclaim all implied warranties, conditions, and other terms that might otherwise be implied.

We disclaim all liability for any loss or damage, whether direct, indirect, incidental, consequential, or otherwise, arising from any reliance placed on the Content or from your use of it, to the fullest extent permitted by applicable law. By continuing to access or use the Content, you acknowledge and agree to these terms.