Skip to content
  1. Examples

Zoom-dependent styles

In this example you can see an icon rule, that only loads the icons for images bigger than a certain threshold on the screen, and also checks that they are currently in the viewport. It updates it self when you zoom or pan, try it out.

ts
import Ogma from '@linkurious/ogma';

const ogma = new Ogma({
  container: 'graph-container'
});

// set of the nodes currently displayed and big
// enough to see their icons
const visibleIcons = new Set();
// minimum size of the nodes to see their icons
const minVisibleSize = 20;

// Specify the style.
const iconRule = ogma.styles.addNodeRule({
  // Specify the icon font, color and contents.
  image: {
    url: node =>
      visibleIcons.has(node.getId())
        ? `flags/${countryCodes[+node.getId() % countryCodes.length]}.svg`
        : undefined,
    minVisibleSize
  }
});

// update node visibility based on zoom level and view position
ogma.events.on('viewChanged', () => {
  const zoom = ogma.view.getZoom();
  // use the new .getElementsInView() API
  const nodesInView = ogma.view.getElementsInView().nodes;
  // avoid calling .getAttribute() on every node
  const radii = nodesInView.getAttribute('radius');
  const nodesBigEnough = nodesInView.filter(
    // d = radius * 2
    (_, i) => +radii[i] * 2 * zoom >= minVisibleSize
  );

  // Add the nodes that are big enough to the set of visible nodes.
  visibleIcons.clear();
  nodesBigEnough.getId().forEach(id => visibleIcons.add(id));

  // refresh the rule and load icons
  iconRule.refresh();
});

ogma.generate
  // intentionally big graph
  .random({ nodes: 1000, edges: 0 })
  // randomize sizes for the better example,
  // to see how bigger have their icons loaded earlier
  .then(graph => {
    graph.nodes
      .filter(() => Math.random() < 0.06)
      .forEach(node => (node.attributes!.radius = 52));
    return graph;
  })
  .then(graph => ogma.setGraph(graph))
  // make layout faster
  .then(() => ogma.layouts.force({ steps: 100, theta: 0.99, locate: true }));

ogma.styles.addNodeRule({
  // Set the node color as white
  color: n => (visibleIcons.has(n.getId()) ? undefined : '#ddd'),
  // Add a border to the node
  innerStroke: {
    color: '#161344',
    width: 3,
    minVisibleSize: 5
  }
});

const countryCodes = [
  'ad',
  'gh',
  'np',
  'ae',
  'gi',
  'nr',
  'af',
  'gl',
  'nu',
  'ag',
  'gm',
  'nz',
  'ai',
  'gn',
  'om',
  'al',
  'gp',
  'pa',
  'am',
  'gq',
  'pe',
  'ao',
  'gr',
  'pf',
  'aq',
  'gs',
  'pg',
  'ar',
  'gt',
  'ph',
  'as',
  'gu',
  'pk',
  'at',
  'gw',
  'pl',
  'au',
  'gy',
  'pm',
  'aw',
  'hk',
  'pn',
  'ax',
  'hm',
  'pr',
  'az',
  'hn',
  'ps',
  'ba',
  'hr',
  'pt',
  'bb',
  'ht',
  'pw',
  'bd',
  'hu',
  'py',
  'be',
  'id',
  'qa',
  'bf',
  'ie',
  're',
  'bg',
  'il',
  'ro',
  'bh',
  'im',
  'rs',
  'bi',
  'in',
  'ru',
  'bj',
  'io',
  'rw',
  'bl',
  'iq',
  'sa',
  'bm',
  'ir',
  'sb',
  'bn',
  'is',
  'sc',
  'bo',
  'it',
  'sd',
  'bq',
  'je',
  'se',
  'br',
  'jm',
  'sg',
  'bs',
  'jo',
  'sh',
  'bt',
  'jp',
  'si',
  'bv',
  'ke',
  'sj',
  'bw',
  'kg',
  'sk',
  'by',
  'kh',
  'sl',
  'bz',
  'ki',
  'sm',
  'ca',
  'km',
  'sn',
  'cc',
  'kn',
  'so',
  'cd',
  'kp',
  'sr',
  'cf',
  'kr',
  'ss',
  'cg',
  'kw',
  'st',
  'ch',
  'ky',
  'sv',
  'ci',
  'kz',
  'sx',
  'ck',
  'la',
  'sy',
  'cl',
  'lb',
  'sz',
  'cm',
  'lc',
  'tc',
  'cn',
  'li',
  'td',
  'co',
  'lk',
  'tf',
  'cr',
  'lr',
  'tg',
  'cu',
  'ls',
  'th',
  'cv',
  'lt',
  'tj',
  'cw',
  'lu',
  'tk',
  'cx',
  'lv',
  'tl',
  'cy',
  'ly',
  'tm',
  'cz',
  'ma',
  'tn',
  'de',
  'mc',
  'to',
  'dj',
  'md',
  'tr',
  'dk',
  'me',
  'tt',
  'dm',
  'mf',
  'tv',
  'do',
  'mg',
  'tw',
  'dz',
  'mh',
  'tz',
  'ec',
  'mk',
  'ua',
  'ee',
  'ml',
  'ug',
  'eg',
  'mm',
  'um',
  'eh',
  'mn',
  'un',
  'er',
  'mo',
  'us',
  'es',
  'mp',
  'uy',
  'et',
  'mq',
  'uz',
  'eu',
  'mr',
  'va',
  'fi',
  'ms',
  'vc',
  'fj',
  'mt',
  've',
  'fk',
  'mu',
  'vg',
  'fm',
  'mv',
  'vi',
  'fo',
  'mw',
  'vn',
  'fr',
  'mx',
  'vu',
  'ga',
  'my',
  'wf',
  'gb-eng',
  'mz',
  'ws',
  'gb-nir',
  'na',
  'ye',
  'gb-sct',
  'nc',
  'yt',
  'gb-wls',
  'ne',
  'za',
  'gb',
  'nf',
  'zm',
  'gd',
  'ng',
  'zw',
  'ge',
  'ni',
  'zz',
  'gf',
  'nl',
  'gg',
  'no'
];
html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <link
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/solid.min.css"
      rel="stylesheet"
    />

    <link type="text/css" rel="stylesheet" href="styles.css" />
  </head>

  <body>
    <div id="graph-container"></div>

    <!-- we force the loading of the font awesome -->
    <i class="fa fa-camera-retro fa-1x" style="color: rgba(0, 0, 0, 0)"></i>
    <script type="module" src="index.ts"></script>
  </body>
</html>
css
#graph-container {
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  position: absolute;
  margin: 0;
  overflow: hidden;
}