|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> |
|
<title>Timeline Tests</title> |
|
|
|
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script> |
|
|
|
<style type="text/css"> |
|
.nodeHighlighted { |
|
fill: 'yellow' |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div id="tooltip"> |
|
<strong class="system"> </strong> |
|
<span class="label"> </span> |
|
</div> |
|
</body> |
|
<script type="text/javascript"> |
|
// d3.behavior.zoom().on("zoom", redraw) |
|
|
|
var tooltip = d3.select("#tooltip") |
|
|
|
var width = 960 |
|
var height = 300 |
|
var rectWidth = 20 |
|
var rectHeight = 10 |
|
|
|
var nodeR = 10 |
|
var nodeStrokeW = 1.5 |
|
|
|
var scale = 1 |
|
|
|
var data = d3.range(400).map(function(d, i) { |
|
return { |
|
x : d, |
|
y : 0, // doesn't matter, will be reset before it matters |
|
r : nodeR, |
|
system : 'csa', |
|
label : 'inquiry' |
|
} |
|
}) |
|
|
|
var dx = function(d) { return d.x } |
|
var dy = function(d) { return d.y } |
|
var dr = function(d) { return d.r } |
|
var colorX = function(d) { return color(d.x) } |
|
|
|
var extent = d3.extent(data, dx) |
|
|
|
// the range is set in such a way that portions of nodes are not drawn outside the g |
|
var xScale = d3.scale.linear() |
|
.domain(extent) |
|
.range([nodeStrokeW, width - 2*nodeR - nodeStrokeW]) |
|
|
|
var color = d3.scale.linear() |
|
.domain(extent) |
|
.range(["steelblue", "brown"]) |
|
.interpolate(d3.interpolateHsl) |
|
|
|
var norm = d3.random.normal(0, 4.0) |
|
|
|
var chart = d3.select("body").append("svg") |
|
.attr("width", 960) |
|
.attr("height", 300) |
|
.attr("pointer-events", "all") |
|
.append("g") |
|
.attr("transform", "translate(10,10)") |
|
.on("mousemove", function() { |
|
var x = d3.event.pageX - 10 // subtract translation |
|
followMe.transition() |
|
.duration(10) |
|
.attr("x1", x).attr("x2", x) |
|
}) |
|
.call(d3.behavior.zoom().x(xScale).scaleExtent([1, 10]).on("zoom", zoom)) |
|
.append('g'); |
|
|
|
// append a background rectangle to receive the pointer events |
|
// (otherwise zoom only works when the pointer is over a node) |
|
chart.append('rect') |
|
.attr('width', width) |
|
.attr('height', height) |
|
.attr('fill', 'white'); |
|
|
|
var axis = d3.svg.axis().scale(xScale) |
|
chart.append("g").attr("class", "xAxis").call(axis) |
|
|
|
var followMe = chart.append("line") |
|
.attr("x1", 0).attr("x2", 0) |
|
.attr("y1", 0).attr("y2", 400) |
|
.style("stroke", "red") |
|
|
|
function zoom() { |
|
chart.select(".xAxis").call(axis); |
|
|
|
if(scale != d3.event.scale) |
|
beeswarm() |
|
scale = d3.event.scale |
|
|
|
chart.selectAll("circle.node") |
|
.transition(10) |
|
.attr("cx", function(d) { return xScale(d.x) }) |
|
.attr("cy", dy) |
|
} |
|
|
|
var nodes = chart.selectAll("circle.node") |
|
.data(data) |
|
|
|
beeswarm() |
|
|
|
nodes.enter().append("circle") |
|
.attr("class", "node") |
|
.attr("cx", function(d) { return xScale(d.x) }) |
|
.attr("cy", dy) |
|
.attr("r", dr) |
|
.attr("fill", colorX) |
|
.style("stroke", "black") |
|
.style("stroke-width", 1.5) |
|
.on("mouseover", function(d) { |
|
tooltip.select(".system").html(d.system) |
|
return tooltip.select(".label").html(d.label) |
|
}) |
|
.on("mouseout", function(d) { |
|
tooltip.select(".system").html(" ") |
|
return tooltip.select(".label").html(" ") |
|
}) |
|
.on("click", function(d, i) { |
|
var inRange = function(dPrime, iPrime) { |
|
return Math.pow(i-iPrime, 2) <= 4 |
|
} |
|
|
|
d3.selectAll("circle.node").filter(inRange).attr("fill", "yellow") |
|
d3.selectAll("circle.node").filter(function(dPrime, iPrime) { return !inRange(dPrime, iPrime) }) |
|
.attr("fill", colorX) |
|
}) |
|
|
|
function beeswarm() { |
|
// reset vertical position |
|
data.map(function(d) { |
|
d.y = height / 2 |
|
}) |
|
for(var iter = 0; iter < 10; iter++) { |
|
var q = d3.geom.quadtree(data) |
|
for(var i = 0; i < data.length; i++) |
|
q.visit(collide(data[i])) |
|
} |
|
} |
|
|
|
function collide(node) { |
|
var r = node.r + 16, |
|
nx1 = xScale(node.x) - r, |
|
nx2 = xScale(node.x) + r, |
|
ny1 = node.y - r, |
|
ny2 = node.y + r; |
|
return function(quad, x1, y1, x2, y2) { |
|
if (quad.point && (quad.point !== node)) { |
|
var x = xScale(node.x) - xScale(quad.point.x), |
|
y = node.y - quad.point.y, |
|
l = Math.sqrt(x * x + y * y), |
|
r = node.r + quad.point.r; |
|
if (l < r) |
|
node.y += norm() |
|
} |
|
return xScale(x1) > nx2 |
|
|| xScale(x2) < nx1 |
|
|| y1 > ny2 |
|
|| y2 < ny1 |
|
} |
|
} |
|
</script> |
|
</html> |