Show values on top of bars in a barChart

前端 未结 5 1919
北恋
北恋 2020-12-03 15:47

I have a bar chart with ordinal scale for the x-axis. I want to display the y-values on the top of each bar or in the bottom of each bar. It would be also acceptable to disp

相关标签:
5条回答
  • 2020-12-03 16:06

    This function will reposition a row chart's labels to the end of each row. A similar technique could be used for a bar chart using the "y" attribute.

    • Labels animate along with the row rect, using chart's transitionDuration
    • Uses chart's valueAccessor function
    • Uses chart's xAxis scale to calculate label position

    rowChart.on('pretransition', function(chart) {
        var padding = 2;
            
        chart.selectAll('g.row > text') //find all row labels
            .attr('x', padding) //move labels to starting position
            .transition() //start transition
            .duration(chart.transitionDuration()) //use chart's transitionDuration so labels animate with rows
            .attr('x', function(d){ //set label final position
                var dataValue = chart.valueAccessor()(d); //use chart's value accessor as basis for row label position
                var scaledValue = chart.xAxis().scale()(dataValue); //convert numeric value to row width
                return scaledValue+padding; //return scaled value to set label position
            });
    });

    0 讨论(0)
  • 2020-12-03 16:16

    If you use a specialized valueAccessor with a chart, you can make the following substitution in dimirc's "D3-ish way" solution.

    Change

    .text(function(d){
        return d3.select(d).data()[0].data.value
    })
    

    To

    .text(function(d){
        return chart.valueAccessor()(d3.select(d).data()[0].data)
    });
    
    0 讨论(0)
  • 2020-12-03 16:22

    Check updated jsfiddle

    .renderlet(function(chart){
    
        var barsData = [];
        var bars = chart.selectAll('.bar').each(function(d) { barsData.push(d); });
    
        //Remove old values (if found)
        d3.select(bars[0][0].parentNode).select('#inline-labels').remove();
        //Create group for labels 
        var gLabels = d3.select(bars[0][0].parentNode).append('g').attr('id','inline-labels');
    
        for (var i = bars[0].length - 1; i >= 0; i--) {
    
            var b = bars[0][i];
            //Only create label if bar height is tall enough
            if (+b.getAttribute('height') < 18) continue;
            
            gLabels
                .append("text")
                .text(barsData[i].data.value)
                .attr('x', +b.getAttribute('x') + (b.getAttribute('width')/2) )
                .attr('y', +b.getAttribute('y') + 15)
                .attr('text-anchor', 'middle')
                .attr('fill', 'white');
        }
    
    })
    

    If you don't want the labels visible when the bars redraw (for example when bars change after user filters/clicks other chart) you can move the check of old values from de renderlet to the to a preRedraw listener.

    .on("preRedraw", function(chart){
    
        //Remove old values (if found)
        chart.select('#inline-labels').remove();
    
    })
    

    Alternative

    D3-ish way

    Demo jsfiddle

    .renderlet(function (chart) {
        
        //Check if labels exist
        var gLabels = chart.select(".labels");
        if (gLabels.empty()){
            gLabels = chart.select(".chart-body").append('g').classed('labels', true);
        }
        
        var gLabelsData = gLabels.selectAll("text").data(chart.selectAll(".bar")[0]);
        
        gLabelsData.exit().remove(); //Remove unused elements
        
        gLabelsData.enter().append("text") //Add new elements
        
        gLabelsData
        .attr('text-anchor', 'middle')
        .attr('fill', 'white')
        .text(function(d){
            return d3.select(d).data()[0].data.value
        })
        .attr('x', function(d){ 
            return +d.getAttribute('x') + (d.getAttribute('width')/2); 
        })
        .attr('y', function(d){ return +d.getAttribute('y') + 15; })
        .attr('style', function(d){
            if (+d.getAttribute('height') < 18) return "display:none";
        });
        
    })
    
    0 讨论(0)
  • 2020-12-03 16:23

    In dc.js version 3.* . You can just use .renderLabel(true). It will print the value at top

    0 讨论(0)
  • 2020-12-03 16:25

    Here's a bit of a hacky solution using the renderlet method. jsFiddle: http://jsfiddle.net/tpsc5f9f/4/

    JS

    var barChart = dc.barChart("#Chart")
        .width(480).height(300)
        .dimension(XDimension)
        .group(YDimension)
        .transitionDuration(500)
        .xUnits(dc.units.ordinal)
        .x(d3.scale.ordinal().domain(XDimension)) 
    
    dc.renderAll();
    
    barChart.renderlet(function(chart){
        moveGroupNames();
    });
    
    
    function moveGroupNames() {
       var $chart = $('#Chart'),
           bar  = $chart.find('.bar');
    
        bar.each(function (i, item) {
            var bar_top    = this.height.baseVal.value;
            var bar_left   = this.width.baseVal.value;
            var bar_offset_x = 30;
            var bar_offset_y = 33;
    
            var bar_val = $(this).find('title').html().split(':')[1];
    
            $chart.append('<div class="val" style="bottom:'+(bar_top+bar_offset_y)+'px;left:'+((bar_left*i)+(bar_offset_x))+'px;width:'+bar_left+'px">'+bar_val+'</div>');
    
        });
    }
    

    Added CSS

    body {
        margin-top:20px;
    }
    #Chart {
        position:relative;
    }
    
    #Chart .val {
        position:absolute;
        left:0;
        bottom:0;
        text-align: center;
    }
    
    0 讨论(0)
提交回复
热议问题