Search code examples
d3.jslabeldonut-chart

Label of donut chart is too long


My problem is labels of donut chart too long. It'll be cut lost when it get over width or height of svg.

i don't know how i must cut it to 2 or more line. I try to add tag div outside tag text but it's wrong. Who can give me a solution. This is my code:

var tooltip = d3.select('#chart')
                .append('div')
                .attr('class', 'tooltips');

        tooltip.append('div')
                .attr('class', 'label');

        var data = [
            {country: "UNITED KINGDOMhhhhhhhhhhhhhhhhhhhhhhhhh hhhhhhhhhhhhhhhhh", val: 86.68},
            {country: "HONG KONGggggggggggggggggggggg g g g gg g g g g gg  gg g g ", val: 9.23},
            {country: "OTHERS", val: 4.09}
        ];

        var w = 600,
                h = 600,
                r = Math.min(w, h) / 2 - 100,
                labelr = r + 30, // radius for label anchor
                color = d3.scale.category20(),
                donut = d3.layout.pie(),
                arc = d3.svg.arc().innerRadius(r * .6).outerRadius(r);

        var vis = d3.select("#chart")
                .append("svg:svg")
                .data([data])
                .attr("width", w + 150)
                .attr("height", h);

        var arcs = vis.selectAll("g.arc")
                .data(donut.value(function(d) { return d.val }))
                .enter().append("svg:g")
                .attr("class", "arc")
                .attr("transform", "translate(" + (r + 200) + "," + (r+100) + ")");

        var arcOver = d3.svg.arc()
                .innerRadius(r * .57)
                .outerRadius(r + 5);

        arcs.append("path")
                .attr("fill", function(d, i) { return color(i); })
                .attr("d", arc)
                .on("mouseover",function(d){
                    d3.select(this).transition()
                      .duration(50)
                      .attr("d", arcOver);
                    tooltip.select('.label').html(d.value + "%");
                    tooltip.style('display', 'block');
                })
                .on('mouseout', function() {
                    d3.select(this).transition()
                            .duration(50)
                            .attr("d", arc);
                    tooltip.style('display', 'none');
                })
                .on('mousemove', function(d) {
                    tooltip.style('top', (d3.event.pageY - 80) + 'px')
                            .style('left', (d3.event.pageX + 10) + 'px');
                });

        arcs.append("text")
                .attr("transform", function(d) {
                    var c = arc.centroid(d),
                            x = c[0],
                            y = c[1],
                    // pythagorean theorem for hypotenuse
                            h = Math.sqrt(x*x + y*y);
                    return "translate(" + (x/h * labelr) +  ',' +
                            (y/h * labelr) +  ")";
                })
                .attr("dy", ".35em")
                .attr("text-anchor", function(d) {
                    // are we past the center?
                    return (d.endAngle + d.startAngle)/2 > Math.PI ?
                            "end" : "start";
                })
                .text(function(d) { return d.data.country; });

Thanks!!!


Solution

  • var tooltip = d3.select('#chart')
                    .append('div')
                    .attr('class', 'tooltips');
    
            tooltip.append('div')
                    .attr('class', 'label');
    
            var data = [
                {country: "UNITED KINGDOMhhhhhhhhhhhhhhhhhhhhhhhhh hhhhhhhhhhhhhhhhh", val: 86.68},
                {country: "HONG KONGggggggggggggggggggggg g g g gg g g g g gg  gg g g ", val: 9.23},
                {country: "OTHERS", val: 4.09}
            ];
    
            var w = 600,
                    h = 600,
                    r = Math.min(w, h) / 2 - 100,
                    labelr = r + 30, // radius for label anchor
                    color = d3.scale.category20(),
                    donut = d3.layout.pie(),
                    arc = d3.svg.arc().innerRadius(r * .6).outerRadius(r);
    
            var vis = d3.select("#chart")
                    .append("svg:svg")
                    .data([data])
                    .attr("width", w + 150)
                    .attr("height", h);
    
            var arcs = vis.selectAll("g.arc")
                    .data(donut.value(function(d) { return d.val }))
                    .enter().append("svg:g")
                    .attr("class", "arc")
                    .attr("transform", "translate(" + (r + 200) + "," + (r+100) + ")");
    
            var arcOver = d3.svg.arc()
                    .innerRadius(r * .57)
                    .outerRadius(r + 5);
    
            arcs.append("path")
                    .attr("fill", function(d, i) { return color(i); })
                    .attr("d", arc)
                    .on("mouseover",function(d){
                        d3.select(this).transition()
                          .duration(50)
                          .attr("d", arcOver);
                        tooltip.select('.label').html(d.value + "%");
                        tooltip.style('display', 'block');
                    })
                    .on('mouseout', function() {
                        d3.select(this).transition()
                                .duration(50)
                                .attr("d", arc);
                        tooltip.style('display', 'none');
                    })
                    .on('mousemove', function(d) {
                        tooltip.style('top', (d3.event.pageY - 80) + 'px')
                                .style('left', (d3.event.pageX + 10) + 'px');
                    });
    
           var text =  arcs.append("text")
                    .attr("transform", function(d) {
                        var c = arc.centroid(d),
                                x = c[0],
                                y = c[1],
                        // pythagorean theorem for hypotenuse
                                h = Math.sqrt(x*x + y*y);
                        return "translate(" + (x/h * labelr) +  ',' +
                                (y/h * labelr) +  ")";
                    })
                    .attr("dy", ".35em")
                    .attr("text-anchor", function(d) {
                        // are we past the center?
                        return (d.endAngle + d.startAngle)/2 > Math.PI ?
                                "end" : "start";
                    });/*
                    .text(function(d) {
              return d.data.country; 
            });*/
    text.each(function(d){
        var text = d3.select(this),
            words = d.data.country.split(/\s+/).reverse(),
            word,
            line = [],
            lineNumber = 0,
            lineHeight = 0.22, // 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");
        while (word = words.pop()) {
          line.push(word);
          tspan.text(line.join(" "));
          if (tspan.node().getComputedTextLength() > 10) {
            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);
          }
        }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <div id='chart'></div>

    After doing a great lot of work out. I got this. Hope this will fulfill your need/requirement.

    All I did is, Adding tspan elements to text element.Observe below code. text is a var see above code. which holds all text elements which we want to add to the every g

    text.each(function(d){
            var text = d3.select(this),//selecting current text element
                words = d.data.country.split(/\s+/).reverse(),//splitting the country name by using space, if you want you can change.
                word,//to store one one word
                line = [],
                lineNumber = 0,
                lineHeight = 0.22, // ems, you can increase for more gaps vise versa
                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");
            while (word = words.pop()) {
              line.push(word);
              tspan.text(line.join(" "));
              if (tspan.node().getComputedTextLength() > 10) {//here I'm checking the length of the text
                line.pop();
                tspan.text(line.join(" "));
                line = [word];
                tspan = text.append("tspan")
                .attr("x", 0)
                .attr("y", y)
                .attr("dy", ++lineNumber * lineHeight + dy + "em")//setting the gap between the label line gaps.
                .text(word);
              }
            }
        })