You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
334 lines
11 KiB
334 lines
11 KiB
3 years ago
|
/* ------------------------------------------------------------------------------
|
||
|
*
|
||
|
* # D3.js - hierarchical bar chart
|
||
|
*
|
||
|
* Demo d3.js hierarchical bar chart setup with .json data
|
||
|
*
|
||
|
* Version: 1.0
|
||
|
* Latest update: August 1, 2015
|
||
|
*
|
||
|
* ---------------------------------------------------------------------------- */
|
||
|
|
||
|
$(function () {
|
||
|
|
||
|
// Initialize chart
|
||
|
stackedMultiples('#d3-hierarchical-bars', 400);
|
||
|
|
||
|
// Chart setup
|
||
|
function stackedMultiples(element, height) {
|
||
|
|
||
|
|
||
|
// Basic setup
|
||
|
// ------------------------------
|
||
|
|
||
|
// Define main variables
|
||
|
var d3Container = d3.select(element),
|
||
|
margin = {top: 25, right: 40, bottom: 20, left: 130},
|
||
|
width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right,
|
||
|
height = height - margin.top - margin.bottom - 5,
|
||
|
barHeight = 30,
|
||
|
duration = 750,
|
||
|
delay = 25;
|
||
|
|
||
|
|
||
|
|
||
|
// Construct scales
|
||
|
// ------------------------------
|
||
|
|
||
|
// Horizontal
|
||
|
var x = d3.scale.linear()
|
||
|
.range([0, width]);
|
||
|
|
||
|
// Colors
|
||
|
var color = d3.scale.ordinal()
|
||
|
.range(["#26A69A", "#ccc"]);
|
||
|
|
||
|
|
||
|
|
||
|
// Create axes
|
||
|
// ------------------------------
|
||
|
|
||
|
// Horizontal
|
||
|
var xAxis = d3.svg.axis()
|
||
|
.scale(x)
|
||
|
.orient("top");
|
||
|
|
||
|
|
||
|
|
||
|
// Create chart
|
||
|
// ------------------------------
|
||
|
|
||
|
// Add SVG element
|
||
|
var container = d3Container.append("svg");
|
||
|
|
||
|
// Add SVG group
|
||
|
var svg = container
|
||
|
.attr("width", width + margin.left + margin.right)
|
||
|
.attr("height", height + margin.top + margin.bottom)
|
||
|
.append("g")
|
||
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||
|
|
||
|
|
||
|
// Construct chart layout
|
||
|
// ------------------------------
|
||
|
|
||
|
// Partition
|
||
|
var partition = d3.layout.partition()
|
||
|
.value(function(d) { return d.size; });
|
||
|
|
||
|
|
||
|
|
||
|
// Load data
|
||
|
// ------------------------------
|
||
|
|
||
|
d3.json("assets/demo_data/d3/bars/bars_hierarchical.json", function(error, root) {
|
||
|
partition.nodes(root);
|
||
|
x.domain([0, root.value]).nice();
|
||
|
down(root, 0);
|
||
|
});
|
||
|
|
||
|
|
||
|
//
|
||
|
// Append chart elements
|
||
|
//
|
||
|
|
||
|
// Add background bars
|
||
|
svg.append("rect")
|
||
|
.attr("class", "d3-bars-background")
|
||
|
.attr("width", width)
|
||
|
.attr("height", height)
|
||
|
.style("fill", "#fff")
|
||
|
.on("click", up);
|
||
|
|
||
|
|
||
|
// Append axes
|
||
|
// ------------------------------
|
||
|
|
||
|
// Horizontal
|
||
|
svg.append("g")
|
||
|
.attr("class", "d3-axis d3-axis-horizontal d3-axis-strong");
|
||
|
|
||
|
|
||
|
// Append bars
|
||
|
// ------------------------------
|
||
|
|
||
|
// Create hierarchical structure
|
||
|
function down(d, i) {
|
||
|
if (!d.children || this.__transition__) return;
|
||
|
var end = duration + d.children.length * delay;
|
||
|
|
||
|
// Mark any currently-displayed bars as exiting.
|
||
|
var exit = svg.selectAll(".enter")
|
||
|
.attr("class", "exit");
|
||
|
|
||
|
// Entering nodes immediately obscure the clicked-on bar, so hide it.
|
||
|
exit.selectAll("rect").filter(function(p) { return p === d; })
|
||
|
.style("fill-opacity", 1e-6);
|
||
|
|
||
|
// Enter the new bars for the clicked-on data.
|
||
|
// Per above, entering bars are immediately visible.
|
||
|
var enter = bar(d)
|
||
|
.attr("transform", stack(i))
|
||
|
.style("opacity", 1);
|
||
|
|
||
|
// Have the text fade-in, even though the bars are visible.
|
||
|
// Color the bars as parents; they will fade to children if appropriate.
|
||
|
enter.select("text").style("fill-opacity", 1e-6);
|
||
|
enter.select("rect").style("fill", color(true));
|
||
|
|
||
|
// Update the x-scale domain.
|
||
|
x.domain([0, d3.max(d.children, function(d) { return d.value; })]).nice();
|
||
|
|
||
|
// Update the x-axis.
|
||
|
svg.selectAll(".d3-axis-horizontal").transition()
|
||
|
.duration(duration)
|
||
|
.call(xAxis);
|
||
|
|
||
|
// Transition entering bars to their new position.
|
||
|
var enterTransition = enter.transition()
|
||
|
.duration(duration)
|
||
|
.delay(function(d, i) { return i * delay; })
|
||
|
.attr("transform", function(d, i) { return "translate(0," + barHeight * i * 1.2 + ")"; });
|
||
|
|
||
|
// Transition entering text.
|
||
|
enterTransition.select("text")
|
||
|
.style("fill-opacity", 1);
|
||
|
|
||
|
// Transition entering rects to the new x-scale.
|
||
|
enterTransition.select("rect")
|
||
|
.attr("width", function(d) { return x(d.value); })
|
||
|
.style("fill", function(d) { return color(!!d.children); });
|
||
|
|
||
|
// Transition exiting bars to fade out.
|
||
|
var exitTransition = exit.transition()
|
||
|
.duration(duration)
|
||
|
.style("opacity", 1e-6)
|
||
|
.remove();
|
||
|
|
||
|
// Transition exiting bars to the new x-scale.
|
||
|
exitTransition.selectAll("rect")
|
||
|
.attr("width", function(d) { return x(d.value); });
|
||
|
|
||
|
// Rebind the current node to the background.
|
||
|
svg.select(".d3-bars-background")
|
||
|
.datum(d)
|
||
|
.transition()
|
||
|
.duration(end);
|
||
|
|
||
|
d.index = i;
|
||
|
}
|
||
|
|
||
|
// Return to parent level
|
||
|
function up(d) {
|
||
|
if (!d.parent || this.__transition__) return;
|
||
|
var end = duration + d.children.length * delay;
|
||
|
|
||
|
// Mark any currently-displayed bars as exiting.
|
||
|
var exit = svg.selectAll(".enter")
|
||
|
.attr("class", "exit");
|
||
|
|
||
|
// Enter the new bars for the clicked-on data's parent.
|
||
|
var enter = bar(d.parent)
|
||
|
.attr("transform", function(d, i) { return "translate(0," + barHeight * i * 1.2 + ")"; })
|
||
|
.style("opacity", 1e-6);
|
||
|
|
||
|
// Color the bars as appropriate.
|
||
|
// Exiting nodes will obscure the parent bar, so hide it.
|
||
|
enter.select("rect")
|
||
|
.style("fill", function(d) { return color(!!d.children); })
|
||
|
.filter(function(p) { return p === d; })
|
||
|
.style("fill-opacity", 1e-6);
|
||
|
|
||
|
// Update the x-scale domain.
|
||
|
x.domain([0, d3.max(d.parent.children, function(d) { return d.value; })]).nice();
|
||
|
|
||
|
// Update the x-axis.
|
||
|
svg.selectAll(".d3-axis-horizontal").transition()
|
||
|
.duration(duration)
|
||
|
.call(xAxis);
|
||
|
|
||
|
// Transition entering bars to fade in over the full duration.
|
||
|
var enterTransition = enter.transition()
|
||
|
.duration(end)
|
||
|
.style("opacity", 1);
|
||
|
|
||
|
// Transition entering rects to the new x-scale.
|
||
|
// When the entering parent rect is done, make it visible!
|
||
|
enterTransition.select("rect")
|
||
|
.attr("width", function(d) { return x(d.value); })
|
||
|
.each("end", function(p) { if (p === d) d3.select(this).style("fill-opacity", null); });
|
||
|
|
||
|
// Transition exiting bars to the parent's position.
|
||
|
var exitTransition = exit.selectAll("g").transition()
|
||
|
.duration(duration)
|
||
|
.delay(function(d, i) { return i * delay; })
|
||
|
.attr("transform", stack(d.index));
|
||
|
|
||
|
// Transition exiting text to fade out.
|
||
|
exitTransition.select("text")
|
||
|
.style("fill-opacity", 1e-6);
|
||
|
|
||
|
// Transition exiting rects to the new scale and fade to parent color.
|
||
|
exitTransition.select("rect")
|
||
|
.attr("width", function(d) { return x(d.value); })
|
||
|
.style("fill", color(true));
|
||
|
|
||
|
// Remove exiting nodes when the last child has finished transitioning.
|
||
|
exit.transition()
|
||
|
.duration(end)
|
||
|
.remove();
|
||
|
|
||
|
// Rebind the current parent to the background.
|
||
|
svg.select(".d3-bars-background")
|
||
|
.datum(d.parent)
|
||
|
.transition()
|
||
|
.duration(end);
|
||
|
}
|
||
|
|
||
|
// Creates a set of bars for the given data node, at the specified index.
|
||
|
function bar(d) {
|
||
|
var bar = svg.insert("g", ".d3-axis-vertical")
|
||
|
.attr("class", "enter")
|
||
|
.attr("transform", "translate(0,5)")
|
||
|
.selectAll("g")
|
||
|
.data(d.children)
|
||
|
.enter()
|
||
|
.append("g")
|
||
|
.style("cursor", function(d) { return !d.children ? null : "pointer"; })
|
||
|
.on("click", down);
|
||
|
|
||
|
bar.append("text")
|
||
|
.attr("x", -6)
|
||
|
.attr("y", barHeight / 2)
|
||
|
.attr("dy", ".35em")
|
||
|
.style("text-anchor", "end")
|
||
|
.text(function(d) { return d.name; });
|
||
|
|
||
|
bar.append("rect")
|
||
|
.attr("width", function(d) { return x(d.value); })
|
||
|
.attr("height", barHeight);
|
||
|
|
||
|
return bar;
|
||
|
}
|
||
|
|
||
|
// A stateful closure for stacking bars horizontally.
|
||
|
function stack(i) {
|
||
|
var x0 = 0;
|
||
|
return function(d) {
|
||
|
var tx = "translate(" + x0 + "," + barHeight * i * 1.2 + ")";
|
||
|
x0 += x(d.value);
|
||
|
return tx;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Resize chart
|
||
|
// ------------------------------
|
||
|
|
||
|
// Call function on window resize
|
||
|
$(window).on('resize', resize);
|
||
|
|
||
|
// Call function on sidebar width change
|
||
|
$('.sidebar-control').on('click', resize);
|
||
|
|
||
|
// Resize function
|
||
|
//
|
||
|
// Since D3 doesn't support SVG resize by default,
|
||
|
// we need to manually specify parts of the graph that need to
|
||
|
// be updated on window resize
|
||
|
function resize() {
|
||
|
|
||
|
// Layout variables
|
||
|
width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right;
|
||
|
|
||
|
|
||
|
// Layout
|
||
|
// -------------------------
|
||
|
|
||
|
// Main svg width
|
||
|
container.attr("width", width + margin.left + margin.right);
|
||
|
|
||
|
// Width of appended group
|
||
|
svg.attr("width", width + margin.left + margin.right);
|
||
|
|
||
|
|
||
|
// Axes
|
||
|
// -------------------------
|
||
|
|
||
|
// Horizontal range
|
||
|
x.range([0, width]);
|
||
|
|
||
|
// Horizontal axis
|
||
|
svg.selectAll('.d3-axis-horizontal').call(xAxis);
|
||
|
|
||
|
|
||
|
// Chart elements
|
||
|
// -------------------------
|
||
|
|
||
|
// Bars
|
||
|
svg.selectAll('.enter rect').attr("width", function(d) { return x(d.value); });
|
||
|
}
|
||
|
}
|
||
|
});
|