简体   繁体   中英

Convert vertical stacked bar to horizontal stacked bar D3.js v4

I found a vertical stack bar graph sample on google - http://bl.ocks.org/juan-cb/43f10523858abf6053ae I want to convert it in horizontal stacked bar graph. I have done the changes but something is wrong. Graph is not correct.I think all the bars are overlapped.

Please help me to resolve this. Code is copied.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
    body {
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        width: 960px;
        height: 500px;
        position: relative;
    }
    svg {
        width: 100%;
        height: 100%;
        position: center;
    }
    text{
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    }
    .toolTip {
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        position: absolute;
        display: none;
        width: auto;
        height: auto;
        background: none repeat scroll 0 0 white;
        border: 0 none;
        border-radius: 8px 8px 8px 8px;
        box-shadow: -3px 3px 15px #888888;
        color: black;
        font: 12px sans-serif;
        padding: 5px;
        text-align: center;
    }
    .legend {
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        font-size: 60%;
    }
    text {
        font: 10px sans-serif;
    }
    .axis text {
        font: 10px sans-serif;
    }
    .axis path{
        fill: none;
        stroke: #000;
    }
    .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
    }
    .x.axis path {
        display: none;
    }
</style>
<body>
<div  class="barGraph" id='stacked-bar'></div>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script>
<script>
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("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("dy", ++lineNumber * lineHeight + dy + "em").text(word);
            }
        }
    });
}
init();
function init(){
   var dataset =  [{
"goodRating": 27,
"avgRating": 21,
"badRating": 16,
"rooms": "0.01"
},
  {
"goodRating": 26,
"avgRating": 22,
"badRating": 31,
"rooms": "0.02"
},
  {
"goodRating": 100,
"avgRating": 0,
"badRating": 0,
"rooms": "1"
}];
   var groupSpacing = 6;
    var margin = {top: 10, right: 10, bottom: 60, left: 100},
        width = 1000 - margin.left - margin.right,
        height = 400 - margin.top - margin.bottom;

      var y = d3.scaleBand()
            .range([height, 0]);
    var x = d3.scaleLinear()
            .range([0, width], .1,.3);

  //  var colorRange = d3.scale.category20();
    var color = d3.scaleOrdinal(d3.schemeCategory20);
    var     xAxis = d3.axisBottom(x).tickFormat(dataset.rooms),
            yAxis =  d3.axisLeft(y);
    var svg = d3.select("#stacked-bar").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 + ")");

    var divTooltip = d3.select("body").append("div").attr("class", "toolTip");
    color.domain(d3.keys(dataset[0]).filter(function(key) { return key !== "rooms"; }));
    dataset.forEach(function(d) {
        var y0 = 0;
        var y1 = 0;
        d.values = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
        d.total = d.values[d.values.length - 1].y1;
    });
    y.domain(dataset.map(function(d) { return d.rooms; }));
    x.domain([0, d3.max(dataset, function(d) { return d.total; })]);
    svg.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")")
            .call(xAxis)
            .selectAll("text")  
              .style("text-anchor", "end")
              .attr("dx", "-.8em")
              .attr("dy", ".5em")
              .attr("transform", "rotate(-65)");

    svg.append("g")
            .attr("class", "y axis")
            .call(yAxis)
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 9)
            .attr("dy", ".71em")
            .style("text-anchor", "end")
            .text("Satisfaction %");
    var bar = svg.selectAll(".rooms")
            .data(dataset)
            .enter().append("g")
            .attr("class", "g")
            .attr("transform", function(d) { return "translate(" + y(d.rooms) + ",0)"; });
    svg.selectAll(".x.axis .tick text")
            .call(wrap, y.bandwidth());

    var bar_enter = bar.selectAll("rect")
    .data(function(d) { return d.values; })
    .enter();

    bar_enter.append("rect")
        .attr("height", y.bandwidth())
        .attr("x", function(d) { return x(d.y1); })
        .attr("width", function(d) { return x(d.y1) - x(d.y0) })
        .style("fill", function(d) { return color(d.name); });

    bar_enter.append("text")
        .text(function(d) { return d3.format(".2s")(d.y1-d.y0)+"%"; })
        .attr("x", function(d) { return x(d.y1)+(x(d.y0) - x(d.y1))/2; })
        .attr("y", y.bandwidth()/3)
        .style("fill", '#ffffff');

    bar.on("mousemove", function(d){
                divTooltip.style("left", d3.event.pageX+10+"px");
                divTooltip.style("top", d3.event.pageY-25+"px");
                divTooltip.style("display", "inline-block");
                var elements = document.querySelectorAll(':hover');
                l = elements.length
                l = l-1
                element = elements[l].__data__
                value = element.y1 - element.y0
                divTooltip.html("Room No : "+(d.rooms)+"<br>"+element.name+" : "+value+"%");
            });
    bar.on("mouseout", function(d){
                divTooltip.style("display", "none");
            });

}   
</script>
</body>

Regards, Pinki Sharma

A few minor things were missing/incorrect:

  1. The group ( <g> ) containing the bars was being transformed incorrectly (I'm guessing you missed this while changing from vertical stacked to horizontal) ie the following line

     .attr("transform", function(d) { return "translate(" + y(d.rooms) + ",0)"; }); 

    translates the bar groups from the left and the y position would be 0 and hence the overlap. I've changed that to this:

     .attr("transform", function(d) { return "translate(0, " + y(d.rooms) + ")"; }); 
  2. The rects' x value is changed from x(d.y1) to x(d.y0) (might be a typo)

     bar_enter.append("rect") .attr("height", y.bandwidth()) .attr("x", function(d) { return x(d.y0); }) 
  3. Axis padding was missing for the scaleBand(). I've added that (check docs for more info)

     var y = d3.scaleBand() .rangeRound([height, 0]).padding(0.1); 
  4. Reset the margins to adapt to the SVG dimensions:

     var margin = {top: 10, right: 60, bottom: 60, left: 50}, 

Combining all of the above, here's a fork of your codepen:

HORIZONTAL STACKED BAR CHART DEMO

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM