<!DOCTYPE html> <meta charset='utf-8'> <style> .links line { stroke: #343434; stroke-opacity: 1; } .exclamation { font-family: LibreFranklin; font-size: 16px; font-weight: bold; font-style: normal; font-stretch: normal; text-align: center; color: #d75148; } </style> <svg width='960' height='600'></svg> <script src='https://d3js.org/d3.v4.min.js'></script> <script> var svg = d3.select('svg'), width = +svg.attr('width'), height = +svg.attr('height'); var color = d3.scaleOrdinal(d3.schemeCategory20); var simulation = d3.forceSimulation() .force('charge', d3.forceManyBody().strength(() => -50).distanceMin(() => 30)) .force('link', d3.forceLink().distance(() => 200).id(function(d) { return d.id; })) .force('collide', d3.forceCollide().radius(function(d) { return 128 + 0.5; }).iterations(2)) .force('center', d3.forceCenter(width / 2, height / 2)) function rightRoundedRect(x, y, width, height, radius) { return 'M' + x + ',' + y // Move to (absolute) + 'h ' + (width - radius) // Horizontal line to (relative) + 'a ' + radius + ',' + radius + ' 0 0 1 ' + radius + ',' + radius // Relative arc + 'v ' + (height - 2 * radius) // Vertical line to (relative) + 'a ' + radius + ',' + radius + ' 0 0 1 ' + -radius + ',' + radius // Relative arch + 'h ' + (radius - width) // Horizontal lint to (relative) + 'z '; // path back to start } function leftRoundedRect(x, y, width, height, radius) { return 'M' + (x + width) + ',' + y // Move to (absolute) start at top-right + 'v ' + height // Vertical line to (relative) + 'h ' + (radius - width) // Horizontal line to (relative) + 'a ' + radius + ',' + radius + ' 0 0 1 ' + -radius + ',' + -radius // Relative arc + 'v ' + -(height - 2 * radius) // Vertical line to (relative) + 'a ' + radius + ',' + radius + ' 0 0 1 ' + radius + ',' + -radius // Relative arch + 'z '; // path back to start } d3.json('services.json', function(error, graph) { if (error) throw error; // Drawing the links between nodes var link = svg.append('g') .attr('class', 'links') .selectAll('line') .data(graph.links) .enter().append('line') .attr('stroke-width', '2px') // And svg group, to contain all of the attributes in @antonas' first prototype var node = svg.selectAll('.node') .data(graph.nodes) .enter() .append('g') .attr('class', 'node_group'); // Info Box node.append('path') .attr('class', 'node_info') .attr('d', leftRoundedRect('0', '0', 48, 48, 4)) .attr('stroke', '#bc3e35') .attr('stroke-width', '1px') .attr('fill', '#d6534a') // node.append('rect') // .attr('class', 'node_info') // .attr('stroke', '#bc3e35') // .attr('stroke-width', '1px') // .attr('fill', '#d6534a') // .attr('height', '48px') // .attr('width', '48px') // .attr('rx', '4') // .attr('ry', '4') // Box where label will live node.append('path') .attr('class', 'node') .attr('d', rightRoundedRect('48', '0', 112, 48, 4)) .attr('stroke', '#343434') .attr('stroke-width', '1px') .attr('fill', '#464646') // A circle that appears within the infobox. node.append('circle') .attr('class', 'alert') .attr('cx', '24') .attr('cy', '24') .attr('stroke-width', '0px') .attr('fill', '#fff') .attr('r', '9px') // An icon or label that exists within the circle, inside the infobox node.append('text') .attr('class', 'exclamation') .attr('x', '24') .attr('y', '30') .attr('text-anchor', 'middle') .attr('fill', '#d75148') .text('!') // Hover-over text for a node's label. node.append('text') .attr('class', 'info_text') .attr('x', '100') .attr('y', '30') .attr('text-anchor', 'middle') .attr('fill', '#fff') .text(d => d.id) node.call(d3.drag() .on('start', dragstarted) .on('drag', dragged) .on('end', dragended)); node.append('title') .text(function(d) { return d.id; }); simulation .nodes(graph.nodes) .on('tick', ticked); simulation.force('link') .links(graph.links); function ticked() { link .attr('x1', function(d) { return d.source.x + 80; }) .attr('y1', function(d) { return d.source.y + 24; }) .attr('x2', function(d) { return d.target.x + 80; }) .attr('y2', function(d) { return d.target.y + 24; }); node .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }); svg.selectAll('.node_group') .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; }); } }); function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } </script>