|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title></title> |
|
<style> |
|
.axis { |
|
font: 12px sans-serif; |
|
} |
|
|
|
.axis path, |
|
.axis line { |
|
fill: none; |
|
stroke: #000; |
|
stroke-width: 1px; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> |
|
<script> |
|
var margin = { top: 10, left: 170, bottom: 200, right: 300 }, |
|
width = 960 - margin.left - margin.right, |
|
height = 600 - margin.top - margin.bottom; |
|
|
|
var scale = { |
|
x: d3.scale.linear().range([0, width]).nice(), |
|
y: d3.scale.linear().range([height, 0]).nice(), |
|
z: d3.scale.linear().range([0, width/2]).nice(), |
|
fill: d3.scale.sqrt().range(["#fff", "#000", "#fff"]) |
|
.interpolate(d3.interpolateLab), |
|
opacity: d3.scale.sqrt().range([0.1, 1, 0.1]), |
|
r: d3.scale.sqrt().range([1, 5, 1]) |
|
}; |
|
|
|
var axis = { |
|
x: d3.svg.axis().scale(scale.x).orient("bottom").ticks(5), |
|
y: d3.svg.axis().scale(scale.y).orient("right").ticks(5), |
|
z: d3.svg.axis().scale(scale.z).orient("bottom").ticks(5) |
|
}; |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
.append("g") |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
var eventRect = d3.select("svg").append("rect") |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
.style("opacity", 0); |
|
|
|
var data = createData(); |
|
|
|
var zExtent = d3.extent(data, function(d) { return d.z; }), |
|
zDifferenceExtent = [ |
|
zExtent[0] - zExtent[1], |
|
0, |
|
zExtent[1] - zExtent[0] |
|
], |
|
currentZ = 0; |
|
|
|
scale.x.domain(d3.extent(data, function(d) { return d.x; })); |
|
scale.y.domain(d3.extent(data, function(d) { return d.y; })); |
|
scale.z.domain(zExtent); |
|
scale.fill.domain(zDifferenceExtent); |
|
scale.opacity.domain(zDifferenceExtent); |
|
scale.r.domain(zDifferenceExtent); |
|
|
|
svg.append("g").attr("class", "x axis") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(axis.x); |
|
svg.append("g").attr("class", "y axis") |
|
.attr("transform", "translate(" + width + ",0)") |
|
.call(axis.y); |
|
svg.append("g").attr("class", "z axis") |
|
.attr("transform", |
|
"translate(" + (width - scale.z(currentZ)) + "," + (height + scale.z(currentZ)) + ") " + |
|
"skewY(-45)" |
|
) |
|
.call(axis.z) |
|
.selectAll("text") |
|
.attr("transform", "skewY(45)"); // un-skew text to make legible |
|
|
|
svg.call(renderPoints, data, currentZ); |
|
eventRect.on("wheel", function() { |
|
event.preventDefault(); |
|
var scrolled = d3.event.deltaY > 0 ? "down" : "up"; |
|
currentZ += 0.1*(scrolled === "up" ? 1 : -1); |
|
currentZ = currentZ > zExtent[1] ? zExtent[1] : |
|
currentZ < zExtent[0] ? zExtent[0] : |
|
currentZ; |
|
|
|
svg.call(renderPoints, data, currentZ); |
|
svg.select(".z.axis") |
|
.attr("transform", |
|
"translate(" + (width - scale.z(currentZ)) + "," + (height + scale.z(currentZ)) + ") " + |
|
"skewY(-45)" |
|
) |
|
}); |
|
|
|
function renderPoints(selection, data, focusedZ) { |
|
var points = selection.selectAll(".point").data(data); |
|
|
|
points.enter().append("circle") |
|
.attr("class", "point"); |
|
|
|
points |
|
.attr("cx", function(d) { return scale.x(d.x); }) |
|
.attr("cy", function(d) { return scale.y(d.y); }) |
|
.attr("r", function(d) { return scale.r(focusedZ - d.z); }) |
|
.style("fill", function(d) { return scale.fill(focusedZ - d.z); }) |
|
.style("opacity", function(d) { return scale.opacity(focusedZ - d.z); }); |
|
|
|
points.exit() |
|
.remove(); |
|
} |
|
|
|
function createData() { |
|
return d3.range(200) |
|
.map(function() { |
|
var x = d3.random.normal(10)(), |
|
y = d3.random.normal(10)(), |
|
z = Math.cos(x) + Math.sin(y); |
|
return {x:x, y:y, z:z}; |
|
}); |
|
} |
|
</script> |
|
</body> |
|
</html> |