This is a useful example for converting a matplotlib barchart to D3 with an initial animation. Using this template, you can quickly generate a nice dynamic barchart using all of your favorite matplotlib styles, and add more advanced interactions without dealing with a lot of additional formatting.
See barchart.py
for code used to generate barchart.svg
. It generates a simple barchart with some basic formatting. Matplotlib's savefig
function will automatically output an svg format if the filepath has a .svg extension. The key is to assign a unique gid to each bar using set_gid
, so it is simple to find the bars when you read the svg using d3.xml
.
xxxxxxxxxx
<head>
<meta charset="utf-8">
<!-- CDN resource versions -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-ease.v1.min.js"></script>
<script src="https://d3js.org/d3-queue.v3.min.js"></script>
<script src="https://d3js.org/d3-selection.v1.min.js"></script>
<script src="https://d3js.org/d3-transition.v1.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<!-- chart SVGs-->
<div id="chart"></div>
<script type="text/javascript">
d3.queue()
.defer(d3.xml, "barchart.svg")
.await(ready);
function ready(error, xml) {
if (error) throw error;
// Load SVG into chart
d3.select("#chart").node().appendChild(xml.documentElement);
///////////////////////////////////////////////////////////////////////////
/////////////////////////// Helper Functions //////////////////////////////
///////////////////////////////////////////////////////////////////////////
// Temporarily disable user interractions to allow animations to complete
var disableUserInterractions = function (time) {
isTransitioning = true;
setTimeout(function(){
isTransitioning = false;
}, time);
}//disableUserInterractions
// Convert rectangles into paths
function getPathFromRect(x,y,w,h) {
return "M " + x + " " + y +
" L " + (x+w) + " " + y +
" L " + (x+w) + " " + (y-h) +
" L " + x + " " + (y-h) + " z"
}//getPathFromRect
// Convert paths into rectangles
function getRectFromPath(path) {
// TODO: Generalize this for more robust parsing
path = path.split(" ")
return {"x": parseFloat(path[0].split(" ")[1]),
"y": parseFloat(path[0].split(" ")[2]),
"w": (parseFloat(path[1].split(" ")[1]) - parseFloat(path[0].split(" ")[1])),
"h": (parseFloat(path[0].split(" ")[2]) - parseFloat(path[2].split(" ")[2]))
}
}//getRectFromPath
///////////////////////////////////////////////////////////////////////////
///////////////////////// Animation Elements //////////////////////////////
///////////////////////////////////////////////////////////////////////////
// Set initial transition state
var isTransitioning = false;
// Basic plot elements
var svg, plot, bars, bar_locs,
bar_label = "item_";
svg = d3.select("#chart").select("svg")
plot = svg.select("#figure_1")
var height = parseFloat(svg.style("height"));
var width = parseFloat(svg.style("width"))
bars = plot.selectAll("g").filter(function(d,i,j) {
return new RegExp(bar_label, 'g').test(j[i].id)
});
bar_locs = {}
bars.nodes().forEach(function(d,i,j) {
var bar = d3.select(j[i]).selectAll("path");
var bar_id = j[i].id;
var bar_rect = getRectFromPath(bar.attr("d"))
bar_locs[bar_id] = bar_rect
bar_rect["c"] = bar.style("fill")
})
svg.on("click", function(){
if (isTransitioning == false) {
init();
}
});
///////////////////////////////////////////////////////////////////////////
////////////////// Initialize Graphic and Animations //////////////////////
///////////////////////////////////////////////////////////////////////////
function init() {
//////////////////////////////////////////////////////
///////////////////// Actions ////////////////////////
//////////////////////////////////////////////////////
var DURATION = 1000;
var OPACITY = 1
disableUserInterractions(DURATION * 3);
bars.each(function(d,i,j) {
var loc = bar_locs[j[i].id]
d3.select(this).selectAll("path")
.attr("d", getPathFromRect(loc.x,loc.y,loc.w,0))
})
bars.transition()
.each(function(d,i,j) {
var loc = bar_locs[j[i].id]
d3.select(this).selectAll("path")
.transition().delay(i*100).duration(DURATION + (i*10))
.ease(d3.easeCubic)
.attr("d", getPathFromRect(loc.x,loc.y,loc.w,loc.h))
})
}//init
init()
};
</script>
</body>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-ease.v1.min.js
https://d3js.org/d3-queue.v3.min.js
https://d3js.org/d3-selection.v1.min.js
https://d3js.org/d3-transition.v1.min.js