Label placement in d3 radial bar chart

后端 未结 1 754
没有蜡笔的小新
没有蜡笔的小新 2021-01-22 15:05

Here in my d3 radial chart, I am trying to get the label text just above the segment arcs instead of keeping outside the outer circle.

Fiddle

         


        
相关标签:
1条回答
  • 2021-01-22 15:20

    We can set the position of the <defs>'s paths using the same data you used to create the arcs.

    First, let's create an enter selection:

    var labels = svg.selectAll("foo")
        .data(data)
        .enter()
        .append("g")
        .classed("labels", true);
    

    Then, we append the paths using the barScale for each value (hardcoded padding of 4px here):

    labels.append("def")
        .append("path")
        .attr("id", (d, i) => "label-path" + i)
        .attr("d", d => "m0 " + -(barScale(d.value) + 4) +
            " a" + (barScale(d.value) + 4) + " " +
            (barScale(d.value) + 4) + " 0 1,1 -0.01 0");
    

    Please notice that we have to use unique IDs. Then, we change the IDs in the text-paths:

    .attr("xlink:href", (d, i) => "#label-path" + i)
    

    Here is your updated fiddle: https://jsfiddle.net/qt3e0rex/

    And the same code in the Stack snippet:

    var width = 360,
        height = 300,
        barHeight = height / 2 - 40;
    
    var formatNumber = d3.format("s");
    
    var color = d3.scale.ordinal()
        .range(["#F15D5D", "#FAD64B"]);
    
    var svg = d3.select('body').append("svg")
        .attr("width", width)
        .attr("height", height)
        .attr('class', 'radial')
        .append("g")
        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
    
    var data = [{
        "name": "ABC",
        "value": 4
    }, {
        "name": "XYZ",
        "value": 5
    }, {
        "name": "DEF",
        "value": 2
    }, {
        "name": "GHI",
        "value": 3
    }, {
        "name": "JKL",
        "value": 1
    }];
    
    data.sort(function(a, b) {
        return b.value - a.value;
    });
    
    var extent = [0, d3.max(data, d => d.value)];
    var barScale = d3.scale.linear()
        .domain(extent)
        .range([0, barHeight]);
    
    var keys = data.map(function(d, i) {
        return d.name;
    });
    var numBars = keys.length;
    
    // X scale
    var x = d3.scale.linear()
        .domain(extent)
        .range([0, -barHeight]);
    
    // X axis
    var xAxis = d3.svg.axis()
        .scale(x).orient("left")
        .ticks(3)
        .tickFormat(formatNumber);
    
    // Inner circles
    var circles = svg.selectAll("circle")
        .data(x.ticks(5))
        .enter().append("circle")
        .attr("r", function(d) {
            return barScale(d);
        })
        .style("fill", "none")
        //.style("stroke", "black")
        //.style("stroke-dasharray", "2,2")
        .style("stroke-width", ".5px");
    
    // Create arcs
    var arc = d3.svg.arc()
        .startAngle(function(d, i) {
            var a = (i * 2 * Math.PI) / numBars;
            var b = ((i + 1) * 2 * Math.PI) / numBars;
            var d = (b - a) / 4;
            var x = a + d;
            var y = b - d;
    
            return x; //(i * 2 * Math.PI) / numBars; 
        })
        .endAngle(function(d, i) {
            var a = (i * 2 * Math.PI) / numBars;
            var b = ((i + 1) * 2 * Math.PI) / numBars;
            var d = (b - a) / 4;
            var x = a + d;
            var y = b - d;
            return y; //((i + 1) * 2 * Math.PI) / numBars; 
        })
        .innerRadius(0);
    
    // Render colored arcs
    var segments = svg.selectAll("path")
        .data(data)
        .enter().append("path")
        .each(function(d) {
            d.outerRadius = 0;
        })
        .style("fill", function(d) {
            return color(d.name);
        })
        .attr("d", arc);
    
    // Animate segments
    segments.transition().ease("elastic").duration(1000).delay(function(d, i) {
            return (25 - i) * 50;
        })
        .attrTween("d", function(d, index) {
            var i = d3.interpolate(d.outerRadius, barScale(+d.value));
            return function(t) {
                d.outerRadius = i(t);
                return arc(d, index);
            };
        });
    
    // Outer circle
    svg.append("circle")
        .attr("r", barHeight)
        .classed("outer", true)
        .style("fill", "none")
        //.style("stroke", "black")
        .style("stroke-width", ".5px");
    
    // Apply x axis
    svg.append("g")
        .attr("class", "x axis")
        .call(xAxis);
    
    // Labels
    var labelRadius = barHeight * 1.025;
    
    var labels = svg.selectAll("foo")
        .data(data)
        .enter()
        .append("g")
        .classed("labels", true);
    
    labels.append("def")
        .append("path")
        .attr("id", (d, i) => "label-path" + i)
        .attr("d", d => "m0 " + -(barScale(d.value) + 4) + " a" + (barScale(d.value) + 4) + " " + (barScale(d.value) + 4) + " 0 1,1 -0.01 0");
    
    labels.append("text")
        .style("text-anchor", "middle")
        .style("font-weight", "bold")
        .style("fill", function(d, i) {
            return "#555";
        })
        .append("textPath")
        .attr("xlink:href", (d, i) => "#label-path" + i)
        .attr("startOffset", function(d, i) {
            return i * 100 / numBars + 50 / numBars + '%';
        })
        .text(function(d) {
            return d.name.toUpperCase();
        });
    body {
      background-color:#fff;
    }
    .axis path, .axis line {
    		fill: none;
    		shape-rendering: crispEdges;
    	}
    
    	.x.axis path {
    		display: none;
    	}
      
    	.tick text, line {
    			display:none;
    		}
        
      circle {
        stroke:#ccc;
      }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

    0 讨论(0)
提交回复
热议问题