Built with blockbuilder.org
forked from shimizu's block: D3 v4 - force layout
license: gpl-3.0 |
Built with blockbuilder.org
forked from shimizu's block: D3 v4 - force layout
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"/> | |
<title>D3 v4 - force layout</title> | |
<style> | |
html, body, #graph { | |
width: 900px; | |
height: 500px; | |
} | |
svg { | |
background: lightblue; | |
width: 500px; | |
height: 500px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="graph"></div> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.1.1/d3.js"></script> | |
<script> | |
!(function(){ | |
"use strict" | |
var width,height | |
var chartWidth, chartHeight | |
var margin | |
var svg = d3.select("#graph").append("svg") | |
var chartLayer = svg.append("g").classed("chartLayer", true) | |
main() | |
function main() { | |
var range = 100 | |
var data = { | |
nodes:d3.range(0, range).map(function(d){ return {label: "l"+d ,r:~~d3.randomUniform(8, 28)()}}), | |
links:d3.range(0, range).map(function(){ return {source:~~d3.randomUniform(range)(), target:~~d3.randomUniform(range)()} }) | |
} | |
setSize(data) | |
drawChart(data) | |
} | |
function setSize(data) { | |
let bound = document.querySelector("svg") | |
.getBoundingClientRect(); | |
width = bound.width; | |
height = bound.height; | |
margin = {top:0, left:0, bottom:0, right:0 } | |
chartWidth = width - (margin.left+margin.right) | |
chartHeight = height - (margin.top+margin.bottom) | |
svg.attr("width", width).attr("height", height) | |
chartLayer | |
.attr("width", chartWidth) | |
.attr("height", chartHeight) | |
.attr("transform", "translate("+[margin.left, margin.top]+")") | |
} | |
function drawChart(data) { | |
function edgeForce(axis, origin, strength) { | |
var nodes; | |
function force(alpha) { | |
nodes.forEach(function(node) { | |
Math.max(Math.abs(origin - node[axis]), node.r) | |
var delta = strength / (origin - node[axis]) * alpha; | |
var repulsion = node.r * strength / 10000 | |
node[axis] -= delta; | |
}) | |
} | |
force.initialize = function(_) { | |
nodes = _; | |
} | |
return force; | |
} | |
var simulation = d3.forceSimulation() | |
//.force("link", d3.forceLink().id(function(d) { return d.index })) | |
.force("collide",d3.forceCollide( function(d){return d.r + 4 }).iterations(16) ) | |
//.force("charge", d3.forceManyBody()) | |
.force("center", d3.forceCenter(chartWidth / 2, chartHeight / 2)) | |
//.force("y", d3.forceY(0)) | |
//.force("x", d3.forceX(0)) | |
.force('edge-left', edgeForce('x', 0, 1000)) | |
.force('edge-right', edgeForce('x', chartWidth, 1000)) | |
.force('edge-top', edgeForce('y', 0, 1000)) | |
.force('edge-bottom', edgeForce('y', chartHeight, 1000)) | |
var link = svg.append("g") | |
.attr("class", "links") | |
.selectAll("line") | |
.data(data.links) | |
.enter() | |
.append("line") | |
.attr("stroke", "black") | |
var node = svg.append("g") | |
.attr("class", "nodes") | |
.selectAll("circle") | |
.data(data.nodes) | |
.enter().append("circle") | |
.attr("r", function(d){ return d.r }) | |
.call(d3.drag() | |
.on("start", dragstarted) | |
.on("drag", dragged) | |
.on("end", dragended)); | |
var ticked = function() { | |
link | |
.attr("x1", function(d) { return d.source.x; }) | |
.attr("y1", function(d) { return d.source.y; }) | |
.attr("x2", function(d) { return d.target.x; }) | |
.attr("y2", function(d) { return d.target.y; }); | |
node | |
.attr("cx", function(d) { return d.x; }) | |
.attr("cy", function(d) { return d.y; }); | |
} | |
simulation | |
.nodes(data.nodes) | |
.on("tick", ticked); | |
simulation.force("link") | |
.links(data.links); | |
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> | |
</body> | |
</html> |