Skip to content

Instantly share code, notes, and snippets.

@JMStewart
Last active August 29, 2015 14:07
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 JMStewart/43ec6ba5df865181a66d to your computer and use it in GitHub Desktop.
Save JMStewart/43ec6ba5df865181a66d to your computer and use it in GitHub Desktop.
Canvas + D3 Force 2

This is an alternative method to use d3 with canvas. This uses d3's data binding with DOM nodes with a custom namespace, as demonstrated here. This method gives significant performance improvements over using svg, while still allowing us to use data binding to control update selections as well as transitions.

Compare to Canvas + D3 Force.

<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
</head>
<body>
<script>
var size = 1000;
var height = 500;
var width = 960;
var charge = -0.3;
var data = d3.range(size).map(function(){
return {r: Math.floor(Math.random() * 8 + 2)};
});
var start = new Date();
var time = 0;
var ticks = 0;
var force = d3.layout.force()
.size([width, height])
.charge(function(d){
return d.r * d.r * charge;
});
var nodes;
var circles;
var newCircles;
var dyingCircles;
var canvas = d3.select('body')
.append('canvas')
.attr({
height: height,
width: width
});
d3.ns.prefix.custom = "http://github.com/mbostock/d3/examples/dom";
var container = d3.select(document.createElementNS('custom', 'g'));
function drawWithData(data){
force.nodes(data)
.start();
nodes = force.nodes();
circles = container.selectAll('circle')
.data(nodes);
newCircles = circles.enter()
.append('custom:circle')
.attr('r', 0);
circles.transition()
.duration(1000)
.attr('r', function(d){
return d.r;
});
dyingCircles = circles.exit();
dyingCircles.transition()
.attr('r', 0)
.remove();
}
drawWithData(data);
var context = canvas.node().getContext("2d");
force.on('tick', function(){
var renderStart = new Date();
context.clearRect(0, 0, width, height);
context.fillStyle = "steelblue";
context.beginPath();
circles.each(function(d) {
context.moveTo(d.x, d.y);
context.arc(d.x, d.y, d3.select(this).attr('r'), 0, 2 * Math.PI);
});
dyingCircles.each(function(d) {
context.moveTo(d.x, d.y);
context.arc(d.x, d.y, d3.select(this).attr('r'), 0, 2 * Math.PI);
});
context.fill();
time += (new Date() - renderStart);
ticks++;
});
force.on('end', function(){
var totalTime = new Date() - start;
console.log('Total Time:', totalTime);
console.log('Render Time:', time);
console.log('Ticks:', ticks);
console.log('Average Time:', totalTime / ticks);
});
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment