Skip to content

Instantly share code, notes, and snippets.

@flacoman91
Forked from cool-Blue/README.md
Last active August 21, 2018 15:24
Show Gist options
  • Save flacoman91/cdaf61d1bfb97a8cdb465d631c5a0a2e to your computer and use it in GitHub Desktop.
Save flacoman91/cdaf61d1bfb97a8cdb465d631c5a0a2e to your computer and use it in GitHub Desktop.
Variable width bar chart with matching, data driven, variable width wordwrap

This example, using satirical data from The Onion, demonstrates how to wrap long axis labels to fit on multiple lines.

  1. Modified wrap to acept text wrap width as a function of data and index.
  2. Made bar widths proportional to values.

Note: The second point is not recommended practice but serves to ilustrate the variable wrap feature!

Updated to use D3v5

We can make this file beautiful and searchable if this error is corrected: No tabs found in this TSV file in line 0.
name,width,value
Family in feud with Zuckerbergs,.5,.99
Cdfdsfa,.40,.45
Committed 671 birthdays to memory,.30,.10
Hdsd safad,.20,.24
Dddd,.5,.75
pdd,.4,.4
Hd,.4,.30
Di long title,.4,.23
N,.4,.17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Wrapping Long Labels</title>
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.title {
font: bold 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js"></script>
<script>
var margin = {top: 80, right: 180, bottom: 80, left: 180},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var padding = 0.1, outerPadding = 0.3,
x = d3.scaleOrdinal();
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x);
var yAxis = d3.axisLeft()
.scale(y)
.ticks(8, "%");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("data.tsv", type).then(function (data) {
var a = alpha(data, v), //scale factor between value and bar width
mid = Midi(data, v, a), //mid-point displacement of bar i
w = Wi(data, v, a); //width of bar i
x.range(data.map(mid)); //force irregular intervals based on value
x.domain(data.map(function (d) {
return d.name;
}));
y.domain([0, d3.max(data, ww)]);
svg.append("text")
.attr("class", "title")
.attr("x", x(data[0].name))
.attr("y", -26)
.attr("shape-rendering", "crispEdges")
.text("Why Are We Leaving Facebook?");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll(".tick text")
.call(wrap, function (d, i) {
return w(i);
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function (d, i) {
return x(d.name) - a * d.width/2; //center the bar on the tick
})
//using x.range sets x.rangeBands to zero, compute width as a function of value
.attr("width", function (d) {
return a * d.width; //`a` already accounts for both types of padding
})
.attr("y", function (d) {
return y(d.value);
})
.attr("height", function (d) {
return height - y(d.value);
});
});
function wrap(text, width) {
text.each(function (d, i) {
var text = d3.select(this),
words = text.html(d).text().split(/\s+/).reverse(), //reset html to clear any tspans added before
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em"),
//[edit]add dWidth
dWidth = typeof width === "function" ? width(d, i) : width;
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > dWidth/*[edit]*/) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
function type(d) {
d.width = +d.width;
d.value = +d.value;
return d;
}
function v(d) {
return +d.width;
}
function ww(d) {
return +d.value;
}
function alpha(values, value) {
var n = values.length, total = d3.sum(values, value);
return (width - (n - 1) * padding * width / n - 2 * outerPadding * width / n) / total
}
function Wi(values, value, alpha) {
return function (i) {
return value(values[i]) * alpha
}
}
function Midi(values, value, alpha) {
var w = Wi(values, value, alpha), n = values.length;
return function (_, i) {
var op = outerPadding * width / n, p = padding * width / n;
return op + d3.sum(values.slice(0, i), value) * alpha + i * p + w(i) / 2;
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment