Skip to content

Instantly share code, notes, and snippets.

@emeeks
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emeeks/0a4d7cd56e027023bf78 to your computer and use it in GitHub Desktop.
Save emeeks/0a4d7cd56e027023bf78 to your computer and use it in GitHub Desktop.
Mixed rendering force layout

This is an example of how to draw your force layout edges (links) using canvas and your nodes using SVG so that you can maintain event listeners on the nodes but draw much larger, more high performance graphs than if you use SVG for the edges.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Mixed Canvas-SVG Rendered Force Layout</title>
<meta charset="utf-8" />
</head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<body>
<canvas style="background: white;500px;width:500px;position:absolute;z-index:-1;" height=500 width=500></canvas>
<svg id="graphSVG" style="height:500px;width:500px;border:1px lightgray solid;" height=500 width=500></svg>
<div id="numbers" style="position:fixed;top:20px;left:20px;"></div>
<footer>
<script>
nodesSeed = d3.range(750);
numConnections = 5;
nodes = [];
edges = [];
for (x in nodesSeed) {
nodes.push({id: x, label: "node " + x})
}
//This is just making a bunch of random edges
var pass = Math.floor(Math.random() * 10) + 2;
var z = 0;
while (z < pass) {
for (x in nodes) {
var y = Math.floor(Math.random() * nodes.length);
var numEdges = y + 1;
while (y <= numEdges) {
if (y < nodes.length && y != x) {
edges.push({source: nodes[x], target: nodes[y]})
}
y++;
}
}
z++;
}
d3.select("#numbers").html("Nodes: "+nodes.length + " Edges: " +edges.length);
var svg = d3.select("svg");
force = d3.layout.force()
.charge(-100)
.linkDistance(100)
.size([500, 500])
.gravity(1)
.on("tick", redrawGraph)
.nodes(nodes)
.links(edges)
svg.selectAll("g.node").data(nodes).enter().append("g")
.attr("class", "node")
.style("cursor", "pointer")
.append("circle")
.attr("r", 6)
.style("fill",function() {
return "hsl(" + Math.random() * 360 + ",100%,50%)";
})
.call(force.drag())
.on("mouseover", highlight)
.on("click", clickNode);
function highlight() {
d3.select(this).style("fill", "red").style("stroke", "white").style("stroke-width", 4)
}
function clickNode(d,i) {
d3.select(this).transition().duration(1000).attr("r", 1)
d.fixed = true;
}
force.start();
function redrawGraph() {
var context = d3.select("canvas").node
var canvas = d3.select("canvas").node();
var context = canvas.getContext("2d");
context.clearRect (0,0,canvas.width,canvas.height);
context.lineWidth = 1;
context.strokeStyle = "rgba(0, 0, 0, 0.1)";
edges.forEach(function (link) {
context.beginPath();
context.moveTo(link.source.x,link.source.y)
context.lineTo(link.target.x,link.target.y)
context.stroke();
})
d3.selectAll("g.node")
.attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")"});
}
</script>
</footer>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment