Shows use of .transition()
with .transform()
Last active
December 15, 2015 00:59
-
-
Save bollwyvl/5176743 to your computer and use it in GitHub Desktop.
Collapsible Area Plot Matrix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<style> | |
html, body{ | |
overflow: hidden; | |
padding: 0; | |
margin: 0; | |
} | |
path.line{ | |
stroke-width: 2; | |
fill: transparent; | |
} | |
path.area{ | |
fill-opacity: 0.2; | |
} | |
#controls{ | |
position:fixed; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="controls"> | |
<button id="collapse_cols">Collapse Columns</button> | |
<button id="collapse_rows">Collapse Rows</button> | |
<button id="makedata">New Data</button> | |
</div> | |
<!-- Firefox doesn't assume the SVG is as big as its contents --> | |
<svg width="100%" height="100%"></svg> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script type="text/javascript" charset="utf-8" src="script.js"></script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// not necessary for a gist, but usually good practice | |
;(function(d3){ | |
"use strict"; | |
// some housecleaning to make sure we're not willing things into existence | |
var window = this, | |
document = window.document, | |
documentElement = document.documentElement; | |
// the root SVG object | |
var svg = d3.select("svg"), | |
// the width/height of the document | |
width, height, | |
// a global for whether rows and/or columns are collapsed | |
collapsed = { cols: 0, rows: 0 }, | |
// x/y scales: (r)ow (c)ell. pretty dynamic, see `update_scales`... | |
y_r = d3.scale.linear(), | |
x_c = d3.scale.linear(), | |
// x/y for path... abusing that domain/range default is [0,1] | |
y_p = d3.scale.linear(), | |
x_p = d3.scale.linear(), | |
// ...and some helpers for path things | |
x_p_idx = function(datum, idx) { return x_p(idx); }, | |
y_p_datum = function(datum) { return y_p(datum); }, | |
// a scale of colors... | |
color = d3.scale.category10(), | |
// ...and a helper to make using it easier | |
color_by_id = function(datum, idx){ return color(idx); }, | |
// a generator for the SVG path `d` attribute that squares off the bottom | |
area = d3.svg.area() | |
.interpolate("basis") | |
.x(x_p_idx) | |
.y0(1) | |
.y1(y_p_datum), | |
// a generator for the SVG path `d` attribute | |
line = d3.svg.line() | |
.interpolate("basis") | |
.x(x_p_idx) | |
.y(y_p_datum), | |
// oh right, we need some data... | |
data = make_data(); | |
// main render function (handles init and update). | |
function render(){ | |
// data/screen size may have changed | |
update_scales(); | |
// an outer SVG `g`... not strictly necessary | |
var outer = svg.selectAll("g.outer") | |
.data([1]), | |
// all these `_init` things are only called when new, un-DOM'd data is | |
// found | |
outer_init = outer.enter() | |
.append("g") | |
.attr("class", "outer"), | |
// each of the rows of plots | |
rows = outer.selectAll("g.row") | |
.data(data), | |
row_init = rows.enter() | |
.append("g").attr("class", "row") | |
// use of d3 call, as we reuse this same stuff in the transition | |
// below... | |
.call(tx_row), | |
// each of the cells (columns within rows) | |
cells = rows.selectAll("g.cell") | |
// a little weird: likely, the sub data would be in an attribute: | |
// e.g. row.vals | |
.data(function(row){ return row; }), | |
cell_init = cells.enter() | |
.append("g").attr("class", "cell") | |
.call(tx_cell), | |
// note we don't `selectAll`, as we just want one path per cell... now | |
areas = cells.select("path.area"), | |
area_init = cell_init.append("path") | |
.attr("class", "area") | |
.style("fill", color_by_id) | |
.attr("d", area), | |
lines = cells.select("path.line"), | |
line_init = cell_init.append("path") | |
.attr("class", "line") | |
.style("stroke", color_by_id) | |
.attr("d", line) | |
// thanks ahaarnos: the old effect was kind of artistic, like | |
// calligraphy, but weird | |
.attr("vector-effect", "non-scaling-stroke"); | |
// it's possible that the "shape" of the data changed: this removes hanging | |
// DOM elements | |
rows.exit().remove(); | |
cells.exit().remove(); | |
// all the animation stuff | |
areas | |
.transition() | |
.ease("back") | |
// not strictly necessary unless data actually changes | |
.attr("d", area); | |
lines | |
.transition() | |
.ease("back") | |
// not strictly necessary unless data actually changes | |
.attr("d", line); | |
rows | |
.transition() | |
.call(tx_row); | |
cells | |
.transition() | |
.call(tx_cell); | |
} | |
// make some fake data. not great. | |
function make_data(){ | |
var rows = Math.ceil(Math.random() * 10), | |
cols = Math.ceil(Math.random() * 10), | |
points = Math.ceil(Math.random() * 100); | |
// probably a prettier way to do this... gets the point across | |
return d3.range(rows).map(function(){ | |
return d3.range(cols).map(function(){ | |
return d3.range(points).map(function(){ | |
return Math.random(); | |
}); | |
}); | |
}); | |
} | |
// change the parts of the scales that are dynamic | |
function update_scales(){ | |
width = documentElement.clientWidth; | |
height = documentElement.clientHeight; | |
y_r.domain([0, data.length]) | |
.range([0, height]); | |
x_c.domain([0, data[0].length]) | |
.range([0, width]); | |
x_p.domain([0, data[0][0].length - 1]); | |
} | |
// vanity replacement for tons of string parse to create SVG transform attrs | |
function tx(mode){ | |
return function(x, y){ | |
return mode + "(" + x + "," + y + ") "; | |
}; | |
} | |
tx.t = tx("translate"); | |
tx.s = tx("scale"); | |
// transform rows | |
function tx_row(rows){ | |
rows.attr("transform", function(datum, idx){ | |
if(collapsed.rows){ return tx.t(0, 0) + tx.s(1, height); } | |
return tx.t(0, y_r(idx)) + tx.s(1, y_r(1)); | |
}); | |
} | |
// transform the columns (cells) within rows | |
function tx_cell(cells){ | |
cells.attr("transform", function(datum, idx){ | |
if(collapsed.cols){ return tx.t(0, 0) + tx.s(width, 1); } | |
return tx.t(x_c(idx), 0) + tx.s(x_c(1), 1); | |
}); | |
} | |
// event handlers. | |
d3.selectAll("button").on("click", function(){ | |
var id = d3.select(this).attr("id").split("_"); | |
switch(id[0]){ | |
case "collapse": collapsed[id[1]] = !collapsed[id[1]]; break; | |
case "makedata": data = make_data(); break; | |
} | |
render(); | |
}); | |
d3.select(window).on("resize", render); | |
// actually render everything. | |
render(); | |
}).call(this, d3); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment