This is a fork of Ziggy Jonsson's Bar Stack (flex lao) to add to this conversation.
The purpose of this gist was to recommend using whitespace to represent the axes of visualizations, as they signify the zero point in the opposite dimensions encoding. Additionally, this enhances the users comprehension because positive and negative areas are able to be assessed independently.
When doing visual encoding, we tend to ignore whitespace and let 0's handle themselves. To learn about other techniques around this concept, refer to: The Design of Nothing: Null, Zero, Blank by Andy Kirk (@visualisingdata).
Proposed Improvements:
forked from milroc's block: axes as whitespace
xxxxxxxxxx
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/d3@2.10.3/d3.v2.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3-legend/1.1.0/d3-legend.js"></script>
<title></title>
<style>
.axis text {
font: 10px sans-serif;
}
.axis path {
fill: none;
stroke: #FFF;
stroke-width:4;
shape-rendering: crispEdges;
}
.axis line {
display: none;
}
.color-legend text {
font-family: 'Open Sans', sans-serif;
font-size: 19pt;
}
</style>
</head>
<body>
<script type="text/javascript" >
function barStack(d) {
var l = d[0].length
while (l--) {
var posBase = 0, negBase = 0;
d.forEach(function(d) {
d=d[l]
d.size = Math.abs(d.y)
if (d.y<0) {
d.y0 = negBase
negBase-=d.size
} else
{
d.y0 = posBase = posBase + d.size
}
})
}
d.extent= d3.extent(d3.merge(d3.merge(d.map(function(e) { return e.map(function(f) { return [f.y0,f.y0-f.size]})}))))
return d
var legend = d.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d) { return "translate(0," + d.z + ")"; });
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.505856em")
.text(function(d) { return d; });
}
/* Here is an example */
var data = [[{x:"Africa",y:-0.756097561,z:"Abortion"},
{x:"Asia",y:1.473684211,z:"Abortion"},
{x:"Europe",y:6.135135135,z:"Abortion"},
{x:"North America",y:-0.846153846,z:"Abortion"},
{x:"Oceania",y:3,z:"Abortion"},
{x:"South America",y:-2.4,z:"Abortion"}],
[{x:"Africa",y:0.073170732,z:"Domestic violence"},
{x:"Asia",y:0.868421053,z:"Domestic violence"},
{x:"Europe",y:1.594594595,z:"Domestic violence"},
{x:"North America",y:3.307692308,z:"Domestic violence"},
{x:"Oceania",y:3.5000,z:"Domestic violence"},
{x:"South America",y:5.8,z:"Domestic violence"}],
[{x:"Africa",y:-0.731707317,z:"Harassment"},
{x:"Asia",y:-1.368421053,z:"Harassment"},
{x:"Europe",y:0.864864865,z:"Harassment"},
{x:"North America",y:-0.769230769,z:"Harassment"},
{x:"Oceania",y:1.5,z:"Harassment"},
{x:"South America",y:1.2,z:"Harassment"}],
[{x:"Africa",y:1.634146341,z:"Constitution"},
{x:"Asia",y:1.157894737,z:"Constitution"},
{x:"Europe",y:0.405405405,z:"Constitution"},
{x:"North America",y:1.461538462,z:"Constitution"},
{x:"Oceania",y:0,z:"Constitution"},
{x:"South America",y:1.6,z:"Constitution"}],
[{x:"Africa",y:2.41463414634146,z:"Other"},
{x:"Asia",y:1.3157894736842,z:"Other"},
{x:"Europe",y:3,z:"Other"},
{x:"North America",y:3,z:"Other"},
{x:"Oceania",y:3,z:"Other"},
{x:"South America",y:3,z:"Other"}],
[{x:"Africa",y:1.48780487804878,z:"Property"},
{x:"Asia",y:1.36842105263158,z:"Property"},
{x:"Europe",y:3,z:"Property"},
{x:"North America",y:2.84615384615385,z:"Property"},
{x:"Oceania",y:3,z:"Property"},
{x:"South America",y:2.6,z:"Property"}],
[{x:"Africa",y:1.09756097560976,z:"Work"},
{x:"Asia",y:-0.157894736842105,z:"Work"},
{x:"Europe",y:2.56756756756757,z:"Work"},
{x:"North America",y:1.15384615384615,z:"Work"},
{x:"Oceania",y:0.5,z:"Work"},
{x:"South America",y:3.2,z:"Work"}]
]
//var data = d3.csv("data.csv");
var h=500
,w=500
,margin=10
,color = d3.scale.category10()
,x = d3.scale.ordinal()
.domain(d3.range(1))
.rangeRoundBands([margin,w-margin], .1)
,y = d3.scale.linear()
.range([h-margin,0+margin])
,xAxis = d3.svg.axis().scale(x).orient("bottom")
,yAxis = d3.svg.axis().scale(y).orient("left")
barStack(data)
y.domain(data.extent)
svg = d3.select("body")
.append("svg")
.attr("height",h)
.attr("width",w)
svg.selectAll(".series").data(data)
.enter().append("g").classed("series",true).style("fill", function(d,i) { return color(i)})
.selectAll("rect").data(Object)
.enter().append("rect")
.attr("x",function(d,i) { return x(d.x)})
.attr("y",function(d) { return y(d.y0)})
svg.append("g").attr("class","axis x")
svg.append("g").attr("class","axis y")
/*Legend*/
var foregroundBarLayer = svg.append("g");
var colorLegendG = svg.append("g")
.attr("class", "color-legend")
.attr("transform", "translate(596, 0)");
var nested = d3.nest()
.key(function (d){ return d.z; })
.entries(data)
var stack = d3.layout.stack()
.y(function (d){ return d.y; })
.values(function (d){ return d.values; });
var colorScale = d3.scale.category10();
var layers = stack(nested.reverse()).reverse();
colorScale.domain(layers.map(function (layer){
return layer.key;
}));
var colorLegend = d3.legend.color()
.scale(colorScale)
.shapePadding(6.24)
.shapeWidth(20)
.shapeHeight(20)
.labelOffset(5);
colorLegendG.call(colorLegend);
colorLegendG.selectAll("text").attr("z", 4);
function listenForHover(selection, data){
selection
.on("mouseover", function (d){
hoveredColorValue = d;
render(data);
})
.on("mouseout", function (d){
hoveredColorValue = null;
render(data);
})
.style("cursor", "pointer");
}
listenForHover(colorLegendG.selectAll("rect"), data);
listenForHover(colorLegendG.selectAll("text"), data);
/*Legend.2*/
function render(data){
var nested = d3.nest()
.key(function (d){ return d.z; })
.entries(data);
var stack = d3.layout.stack()
.y(function (d){ return d.y; })
.values(function (d){ return d.values; });
var layers = stack(nested.reverse()).reverse();
colorScale.domain(layers.map(function (layer){
return layer.key;
}));
colorLegendG.call(colorLegend);
// Move the text down a bit.
colorLegendG.selectAll("text").attr("y", 4);
listenForHover(colorLegendG.selectAll("rect"), data);
listenForHover(colorLegendG.selectAll("text"), data);
}
d3.csv("data.csv", render);
/*Legend.2.E*/
var layout = 0,dur=0
redraw()
dur = 1500
function redraw() {
if (layout=!layout) {
/* Readjust the range to witdh and height */
x.rangeRoundBands([margin,w-margin], .1)
y.range([h-margin,0+margin])
/* Reposition and redraw axis */
svg.select(".x.axis")
.transition().duration(dur)
.attr("transform","translate (0 "+y(0)+")")
.call(xAxis.orient("bottom"))
svg.select(".y.axis")
.transition().duration(dur)
.attr("transform","translate ("+x(0)+" 0)")
.call(yAxis.orient("left"))
/* Reposition the elements */
svg.selectAll(".series rect")
.transition().duration(dur)
.attr("x",function(d,i) { return x(d.x)})
.attr("y",function(d) { return y(d.y0)})
.attr("height",function(d) { return y(0)-y(d.size)})
.attr("width",x.rangeBand())
} else {
/* Readjust the range to witdh and height */
x.rangeRoundBands([h-margin,0+margin], .1)
y.range([margin,w-margin])
/* Reposition and redraw axis */
svg.select(".x.axis")
.transition().duration(dur)
.attr("transform","translate ("+y(0)+" 0)")
.call(xAxis.orient("left"))
svg.select(".y.axis")
.transition().duration(dur)
.attr("transform","translate (0 "+x(0)+")")
.call(yAxis.orient("bottom"))
/* Reposition the elements */
svg.selectAll(".series rect")
.transition().duration(dur)
.attr("y",function(d,i) { return x(d.x)})
.attr("x",function(d) { return y(d.y0-d.size)})
.attr("width",function(d) { return y(d.size)-y(0)})
.attr("height",x.rangeBand())
}
d3.select("body").append("button")
.attr("type","button")
.text("Change Layout")
.style("position","absolute")
.style("left","5px")
.style("top","5px")
.on("click",redraw)
}
</script>
</body>
</html>
Modified http://mbostock.github.com/d3/d3.v2.js to a secure url
https://mbostock.github.com/d3/d3.v2.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.1.0/d3-legend.js