konrad
konrad

Reputation: 3706

text wrapping in d3.js not centered on chart bar

Can someone please help me here with centering the wrapped text value on the bar. Right now the first line is centered but all new appended lines are below it. Is there a way to re-center it after they were wrapped?

http://jsbin.com/qajejahiqi/edit?js,output

Thanks!

Upvotes: 0

Views: 142

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102194

Using a ternary operator, change the y position of your labels according to the length of the text:

.attr("y", function(d){
    return d.month.length > 50 ? y(d.month) + 8 : y(d.month) + (y.rangeBand() / 2);
})

I don't like to use magic numbers, this is just an example for you to see how to create your actual function, in which you can change the magic numbers I'm using here for computed positions.

The logic of this ternary operator is quite simple. First, we get the length of the text:

d.month.length > 50

You can change 50 for any computed value, according to your left margin and the font size. If the text is bigger than that, you set the y position:

y(d.month) + 8

Again, 8 is just a magic number here, you have to create a function to calculate the adequate number according to the width of the bars, size of the texts etc...

Also, if you have different numbers of lines, you can concatenate several ternary operators:

condition1 ? actionA : condition2 ? actionB : condition3? actionC : actionD;

If condition1 is true, actionA is done. If it's false, condition2 is evaluated. If it's true, actionB is done, if it's false, condition3 is evaluated, and so on...

Check the demo:

var temperatures = [
  {temp: 32, month: 'January is really a long month.', color: 0},
  {temp: 38, month: 'February is really a cold month.', color: 1},
  {temp: 47, month: 'March, i dont know what to think', color: 1},
  {temp: 59, month: 'April. What if I wanted to test a really long string here that will span.', color: 2},
  {temp: 70, month: 'May', color: 2},
  {temp: 80, month: 'June', color: 2},
  {temp: 90, month: 'July', color: 2},
  {temp: 83, month: 'Auguest', color: 0},
  {temp: 76, month: 'September', color: 3},
  {temp: 64, month: 'October', color: 4},
  {temp: 49, month: 'November', color: 4},
  {temp: 37, month: 'December', color: 4}
];

var margin = {top: 5, right: 60, bottom: 50, left: 150},
    width = 700 - margin.left - margin.right,
    height = 600 - margin.top - margin.bottom;

var y = d3.scale.ordinal()
    .domain(temperatures.map(function (d) { return d.month; }))
    .rangeBands([0, height], 0.1, 0.35);

var x = d3.scale.linear()
    .domain([0, d3.max(temperatures, function(d){return d.temp;})])
    .range([0, width]);

var color = d3.scale.category20c();

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .innerTickSize(-(height-5))

var barChart = 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 + ")");

barChart.selectAll("#bar")
    .data(temperatures)
    .enter().append("rect")
    .attr("id", "bar")
    .attr("x", 0)
    .attr("width", function(d){return x(d.temp);})
    .attr("y", function (d) { return y(d.month); })
    .attr("fill", function(d){ return color(d.color); })
    .attr("height", y.rangeBand())
    .on("mouseover", function(){
        d3.select(this).attr("fill", "red");
    })
    .on("mouseout", function(){
        d3.select(this).attr("fill", function(d) { return color(d.color); });             
    });

barChart.append("g")
    .attr("class", "x axisHorizontal")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
    .append("text")
    .style("text-anchor", "end")
    .attr("x", width - 3)
    .attr("y", -5)
    .text("Label")

barChart.append("g")
    .selectAll("axisLabels")
    .data(temperatures)
    .enter()
    .append("text")
    .attr("x", 0)
    .attr("y", function(d){return d.month.length > 50 ? y(d.month) + 8 : y(d.month) + (y.rangeBand() / 2);})
    .attr("text-anchor", "end")
    .attr("dy", ".35em")
    .attr("dx", -5)
    .text(function(d){return d.month;})
    .call(wrap, margin.left);

barChart.append("g")
    .selectAll("valueLabels")
    .data(temperatures)
    .enter()
    .append("text")
    .attr("x", function(d){return x(d.temp);})
    .attr("y", function(d){return y(d.month) + (y.rangeBand() / 2);})
    .attr("dx", 5)
    .attr("dy", ".35em")
    .text(function(d){return d.temp;})

function wrap(text, width) {
  text.each(function() {
    var text = d3.select(this),
        words = text.text().split(/\s+/).reverse(),
        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("dx", -5).attr("dy", dy + "em");
    while (word = words.pop()) {
      line.push(word);
      tspan.text(line.join(" "));
      if (tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [word];
        tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dx", -5).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
      }
    }
  });
}
body { 
    font: 10px Arial;
}
.axis path {
    fill: none;
    stroke: grey;
    shape-rendering: crispEdges;
}
.axis text {
    font-family: Arial;
    font-size: 10px;
}
.axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
}
svg{
    display: block;
    margin: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 1

Related Questions