UNPKG

force-graph

Version:

2D force-directed graph rendered on HTML5 canvas

96 lines (83 loc) 3.36 kB
<head> <style> body { margin: 0; } </style> <script src="//bundle.run/@yarnpkg/lockfile@1.1.0"></script> <script src="//unpkg.com/dat.gui"></script> <script src="//unpkg.com/d3-quadtree"></script> <script src="//unpkg.com/d3-force"></script> <script src="//unpkg.com/force-graph"></script> <!--<script src="../../dist/force-graph.js"></script>--> </head> <body> <div id="graph"></div> <script> // controls const controls = { 'DAG Orientation': 'lr'}; const gui = new dat.GUI(); gui.add(controls, 'DAG Orientation', ['lr', 'td', 'radialout', null]) .onChange(orientation => graph && graph.dagMode(orientation)); // graph config const graph = new ForceGraph(document.getElementById('graph')) .backgroundColor('#101020') .linkColor(() => 'rgba(255,255,255,0.2)') .dagMode('lr') .dagLevelDistance(300) .nodeId('package') .linkCurvature(d => 0.07 * // max curvature // curve outwards from source, using gradual straightening within a margin of a few px (['td', 'bu'].includes(graph.dagMode()) ? Math.max(-1, Math.min(1, (d.source.x - d.target.x) / 25)) : ['lr', 'rl'].includes(graph.dagMode()) ? Math.max(-1, Math.min(1, (d.target.y - d.source.y) / 25)) : ['radialout', 'radialin'].includes(graph.dagMode()) ? 0 : 1 ) ) .linkDirectionalParticles(2) .linkDirectionalParticleWidth(3) .nodeCanvasObject((node, ctx) => { const label = node.package; const fontSize = 15; ctx.font = `${fontSize}px Sans-Serif`; const textWidth = ctx.measureText(label).width; const bckgDimensions = [textWidth, fontSize].map(n => n + fontSize * 0.2); // some padding ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; ctx.fillRect(node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions); ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillStyle = 'lightsteelblue'; ctx.fillText(label, node.x, node.y); node.__bckgDimensions = bckgDimensions; // to re-use in nodePointerAreaPaint }) .nodePointerAreaPaint((node, color, ctx) => { ctx.fillStyle = color; const bckgDimensions = node.__bckgDimensions; bckgDimensions && ctx.fillRect(node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions); }) .d3Force('collide', d3.forceCollide(13)) .d3AlphaDecay(0.02) .d3VelocityDecay(0.3); fetch('//unpkg.com/d3@5.9.7/yarn.lock') .then(r => r.text()) .then(text => { const yarnlock = _yarnpkg_lockfile.parse(text); if (yarnlock.type !== 'success') throw new Error('invalid yarn.lock'); return yarnlock.object; }) .then(yarnlock => { const nodes = []; const links = []; Object.entries(yarnlock).forEach(([package, details]) => { nodes.push({ package, version: details.version }); if (details.dependencies) { Object.entries(details.dependencies).forEach(([dep, version]) => { links.push({source: package, target: `${dep}@${version}`}); }); } }); graph.graphData({ nodes, links }); }); </script> </body>