Skip to content

Instantly share code, notes, and snippets.

@biovisualize
Last active March 4, 2016 22:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save biovisualize/1363743 to your computer and use it in GitHub Desktop.
Save biovisualize/1363743 to your computer and use it in GitHub Desktop.
D3 chart plugin template

A template example to write simple chart plugins on top of D3. It is a base for discussion, so your are much welcome to modify it and comment it. The requirements are discussed on the wiki.

This implementation provides: div or svg parent, variants (bars or stacked bars), position, relative position (after or under another chart of the same svg parent), margins, gap between bars (in %), transition duration, separated data and labels (to discuss), uses axis component (to complete), chart size taken from the parent and will resize with it, all items accessible from CSS for styling, most options optional, automatic redraw on setting change and zero argument returns the value.

In this example, the first bar chart data updates periodically and a mouse click changes chart type.

if (typeof d3.bio != "object") d3.bio = {};
d3.bio.barchart = function (){
var barchart = {},
data= [],
label= [],
parent,
group,
w, h,
x, y,
chartW, chartH,
duration = 1000,
margin = [20, 20, 20, 20],
gap = 30
variant = "standard";
barchart.parent = function(p){
if (!arguments.length) return parent.node();
parent = d3.select(p);
w = parent.node().clientWidth;
h = parent.node().clientHeight;
x = 0;
y = 0;
if(d3.ns.prefix.xhtml == parent.node().namespaceURI){
parent = parent.append("svg:svg")
.attr("viewBox", "0 0 "+w+" "+h)
.attr("preserveAspectRatio", "none");
}
group = parent.append("svg:g")
.attr("class", "group")
.attr("id", "group"+parent.node().childElementCount);
group.append("svg:rect")
.attr("class", "panel")
.call(resize);
return barchart;
};
barchart.margin = function(m){
if (!arguments.length) return margin;
margin = m;
group.select("rect.panel")
.call(resize);
return redraw();
};
barchart.size = function(s){
if (!arguments.length) return [w, h];
w = s[0];
h = s[1];
group.select("rect.panel")
.call(resize);
return redraw();
};
barchart.position = function(p, other){
if (!arguments.length) return [x, y];
if(typeof p == "string"){
var otherPos = other.position();
var otherSize = other.size();
if(p == "after"){
x = otherPos[0]+otherSize[0];
y = otherPos[1]+otherSize[1]-h;
}else if(p == "under"){
x = otherPos[0];
y = otherPos[1]+otherSize[1];
}
}else{
x = p[0];
y = p[1];
}
group.select("rect.panel")
.call(resize);
return redraw();
};
barchart.transition = function(d){
if (!arguments.length) return duration;
duration = d;
return redraw();
};
barchart.data = function(d){
if (!arguments.length) return data;
data = d;
return redraw();
};
barchart.gap = function(g){
if (!arguments.length) return gap;
gap = g;
return redraw();
};
barchart.variant = function(v){
if (!arguments.length) return variant;
variant = v;
return redraw();
};
function resize(){
chartW = w - margin[1] - margin[3];
chartH = h - margin[0] - margin[2];
this.attr("width", chartW)
.attr("height", chartH)
.attr("x", x+margin[3])
.attr("y", y+margin[0]);
return barchart;
}
function redraw(){
if(variant == "stacked") barchart.toStackedBarchart();
else barchart.toBarchart();
return barchart;
};
barchart.toPCP = function(){
var yScale = d3.scale.linear().domain([0, d3.max(data)+h/100]).range([chartH, 0]);
var markX = x+margin[3];
var markY = function(d, i){return y+yScale(d)+margin[0];};
var markW = 5;
var markH = 5;
drawSVG(markX, markY, markW, markH);
return barchart;
};
barchart.toBarchart = function(){
var yScale = d3.scale.linear().domain([0, d3.max(data)+h/100]).range([chartH, 0]);
var xScale = d3.scale.linear().domain([0, data.length]).range([0, chartW]);
var gapW = chartW/data.length*(gap/100);
var markX = function(d, i){return x+xScale(i)+margin[3]+gapW/2;};
var markY = function(d, i){return y+yScale(d)+margin[0];};
var markW = chartW/data.length-gapW;
var markH = function(d, i){return chartH-yScale(d);};
drawSVG(markX, markY, markW, markH);
return barchart;
};
barchart.toStackedBarchart = function(){
var yScale = d3.scale.linear().domain([0, d3.sum(data)+h/100]).range([chartH, 0]);
var stackH = [];
var stackTopH = 0;
for(var i=0; i<data.length; i++){
stackTopH += chartH-yScale(data[i]);
stackH.push(stackTopH)
}
var gapW = chartW*(gap/100);
var markX = x+margin[3]+gapW/2;
var markY = function(d, i){return y+h-stackH[i]-margin[2];};
var markW = chartW-gapW;
var markH = function(d, i){return chartH-yScale(d);};
drawSVG(markX, markY, markW, markH);
return barchart;
};
function drawSVG(markX, markY, markW, markH){
var marks = group.selectAll("rect.mark")
.data(data);
marks.enter().append("svg:rect")
.attr("class", "mark")
.attr("x", chartW)
.attr("y", markY)
.attr("width", 0)
.attr("height", markH)
.attr("opacity", 1);
marks.transition()
.attr("x", markX)
.attr("y", markY)
.attr("width", markW)
.attr("height", markH);
marks.exit()
.transition()
.duration(duration/5)
.attr("opacity", 0)
.remove();
}
return barchart;
};
<head>
<title></title>
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
<style type="text/css">
body{
margin: 0;
}
#viz1{
width: 400px;
height: 400px;
position: absolute;
left: 0;
border: solid silver 1px;
}
.panel{
fill: aliceblue;
}
#group1 .mark{
fill: skyblue;
stroke: steelblue;
}
#group2 .mark{
fill: orange;
stroke: orangered;
}
#group3 .mark{
fill: yellowgreen;
stroke: olivedrab;
}
#group4 .mark{
fill: pink;
stroke: orchid;
}
.domain{
display: none;
}
.axis line {
stroke: silver;
}
</style>
</head>
<body>
<div id="viz1"></div>
<script type="text/javascript" src="chart_template.js"></script>
<script type="text/javascript">
var barchart1 = d3.bio.barchart()
.parent("#viz1")
.margin([10, 10, 20, 10])
.gap(30)
.size([200, 200])
.data([1, 2, 5]);
var barchart2 = d3.bio.barchart()
.parent(barchart1.parent())
.margin([10, 10, 20, 10])
.gap(30)
.size(barchart1.size())
.position("after", barchart1)
.data([10, 4, 6]);
var barchart3 = d3.bio.barchart()
.parent(barchart1.parent())
.margin([10, 10, 20, 10])
.gap(30)
.size(barchart1.size())
.position("under", barchart1)
.data([15, 25, 12]);
var barchart4 = d3.bio.barchart()
.parent(barchart1.parent())
.margin([10, 10, 20, 10])
.gap(30)
.size(barchart3.size())
.position("after", barchart3)
.data([1, 40, 15]);
var toggle = true;
d3.select("body").on("mousedown", function(){
if(event.button == 2) return false;
toggle = !toggle;
if(toggle){
barchart1.variant("standard")
barchart2.variant("standard")
barchart3.variant("standard")
barchart4.variant("standard")
}else{
barchart1.variant("stacked")
barchart2.variant("stacked")
barchart3.variant("stacked")
barchart4.variant("stacked")
}
});
setInterval(function() {
var data = [];
var label = [];
var len = ~~(Math.random()*10)+4;
for(var i=0; i<len; i++) data.push(~~(Math.random()*100));
for(i=0; i<data.length; i++) label.push("bar"+(i+1));
barchart1.data(data)
//.label(label)
;
}, 1500);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment