D3 установка минимальной высоты для столбчатой диаграммы

Я пытаюсь создать столбчатую диаграмму с помощью d3.js но у меня проблема.
Проблема заключается в том, что когда есть бар со значением 1 в 10 000 высоты оси Y, бар имеет высоту 0px, и он не появляется на графике.

Пример данных, вызывающих проблему:
[{green: 1, yellow: 9400, white: 999999}]

«Зеленая» полоса будет такой маленькой, как будто ее не существует.

Вот соответствующий код:

var x = d3.scale.ordinal()
        .rangeRoundBands([0, width], 1),
    y = d3.scale.linear()
        .rangeRound([height + 2, margin.top]);

var d3Elm = d3.select(element[0]);

var dataStackLayout = d3.layout.stack()(data);

x.domain(dataStackLayout[0].map(function(d){
    return d3StackedBarCtrl.labelEllipsis(d.displayName, 10);
}));

y.domain([0,
    d3.max(dataStackLayout[dataStackLayout.length - 1],
        function (d) {
            return d.y0 + d.y;
        })
    ]).nice();

var layer = svg.selectAll('.stack')
    .data(dataStackLayout)
    .enter().append('g')
    .attr('class', 'stack');

layer.selectAll('rect')
    .data(function(d){
        return d;
    })
    .enter().append('rect')
    .attr('x', function(d){
        return x(d.displayName);
    })
    .attr('y', function(d){
        return y(d.y + d.y0);
    })
    .attr('width', 24)
    .attr('height', function(d){
        return y(d.y0) - y(d.y + d.y0);
    })
    .attr('class', 'rect');

Как я могу установить минимальную высоту для баров и заставить другие бары уменьшить их высоту за счет расширенных баров?!

Спасибо.

1 ответ

  1. Прежде всего, то, что вы делаете, лежит с данными . Если зеленый на несколько порядков меньше белого, пусть будет, и объясните это своим читателям где-нибудь на графике. Но я понимаю, что вы просто хотите сделать соответствующий прямоугольник видимым.

    Это можно сделать двумя способами: изменить матрицу сложенных данных или изменить сами прямоугольники. Поскольку вы просто хотите сделать бар едва заметным, я бы пошел со вторым вариантом, который не включает в себя изменение данных (aka Lie !), создание правила, устанавливающего минимальную высоту для бара. Поскольку этот бар будет немного перекрывать следующий, нам не нужно будет менять значения других баров (вопреки вашему запросу); кроме того, он имеет дело с пикселями, а не с данными.

    Правило может быть чем-то простым, как это:

    .attr("height", function(d) { 
        return y(d.y0) - y(d.y0 + d.y) < 2 ? 2 : y(d.y0) - y(d.y0 + d.y); 
    })
    

    Который говорит: «проверьте вычисленную высоту. Меньше 2 пикселей? Если да, установите высоту для 2 пикселей. Если нет, установите высоту для вычисленного».

    Проверьте это демо (этот код не мой, я просто нашел его в этой скрипке: http://jsfiddle.net/xavipolo/q5q6331p/ ). Я сделал «апельсины» имеют небольшое значение в 2007 году. Тем не менее, соответствующий бар имеет минимальную высоту 4 пикселей:

    // Setup svg using Bostock's margin convention
    
    var margin = {top: 20, right: 160, bottom: 35, left: 30};
    
    var width = 960 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;
    
    var svg = 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 + ")");
    
    
    /* Data in strings like it would be if imported from a csv */
    
    var data = [
      { year: "2006", redDelicious: "100", mcintosh: "150", oranges: "90", pears: "60" },
      { year: "2007", redDelicious: "120", mcintosh: "180", oranges: "1", pears: "40" },
      { year: "2008", redDelicious: "050", mcintosh: "200", oranges: "80", pears: "20" },
      { year: "2009", redDelicious: "010", mcintosh: "150", oranges: "50", pears: "40" },
      { year: "2010", redDelicious: "020", mcintosh: "100", oranges: "40", pears: "20" },
      { year: "2011", redDelicious: "030", mcintosh: "120", oranges: "60", pears: "30" },
      { year: "2012", redDelicious: "040", mcintosh: "150", oranges: "80", pears: "10" },
      { year: "2013", redDelicious: "060", mcintosh: "110", oranges: "90", pears: "40" },
      { year: "2014", redDelicious: "100", mcintosh: "130", oranges: "90", pears: "50" },
      { year: "2015", redDelicious: "160", mcintosh: "190", oranges: "60", pears: "90" },
      { year: "2016", redDelicious: "190", mcintosh: "170", oranges: "50", pears: "70" },
    ];
    
    var parse = d3.time.format("%Y").parse;
    
    
    // Transpose the data into layers
    var dataset = d3.layout.stack()(["redDelicious", "mcintosh", "oranges", "pears"].map(function(fruit) {
      return data.map(function(d) {
        return {x: parse(d.year), y: +d[fruit]};
      });
    }));
    
    
    // Set x, y and colors
    var x = d3.scale.ordinal()
      .domain(dataset[0].map(function(d) { return d.x; }))
      .rangeRoundBands([10, width-10], 0.02);
    
    var y = d3.scale.linear()
      .domain([0, d3.max(dataset, function(d) {  return d3.max(d, function(d) { return d.y0 + d.y; });  })])
      .range([height, 0]);
    
    var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"];
    
    
    // Define and draw axes
    var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left")
      .ticks(5)
      .tickSize(-width, 0, 0)
      .tickFormat( function(d) { return d } );
    
    var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom")
      .tickFormat(d3.time.format("%Y"));
    
    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis);
    
    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);
    
    
    // Create groups for each series, rects for each segment 
    var groups = svg.selectAll("g.cost")
      .data(dataset)
      .enter().append("g")
      .attr("class", "cost")
      .style("fill", function(d, i) { return colors[i]; });
    
    var rect = groups.selectAll("rect")
      .data(function(d) { return d; })
      .enter()
      .append("rect")
      .attr("x", function(d) { return x(d.x); })
      .attr("y", function(d) { return y(d.y0 + d.y); })
      .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y) < 4 ? 4 : y(d.y0) - y(d.y0 + d.y); })
      .attr("width", x.rangeBand())
      .on("mouseover", function() { tooltip.style("display", null); })
      .on("mouseout", function() { tooltip.style("display", "none"); })
      .on("mousemove", function(d) {
        var xPosition = d3.mouse(this)[0] - 15;
        var yPosition = d3.mouse(this)[1] - 25;
        tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
        tooltip.select("text").text(d.y);
      });
    
    
    // Draw legend
    var legend = svg.selectAll(".legend")
      .data(colors)
      .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d, i) { return "translate(30," + i * 19 + ")"; });
     
    legend.append("rect")
      .attr("x", width - 18)
      .attr("width", 18)
      .attr("height", 18)
      .style("fill", function(d, i) {return colors.slice().reverse()[i];});
     
    legend.append("text")
      .attr("x", width + 5)
      .attr("y", 9)
      .attr("dy", ".35em")
      .style("text-anchor", "start")
      .text(function(d, i) { 
        switch (i) {
          case 0: return "Anjou pears";
          case 1: return "Naval oranges";
          case 2: return "McIntosh apples";
          case 3: return "Red Delicious apples";
        }
      });
    
    
    // Prep the tooltip bits, initial display is hidden
    var tooltip = svg.append("g")
      .attr("class", "tooltip")
      .style("display", "none");
        
    tooltip.append("rect")
      .attr("width", 30)
      .attr("height", 20)
      .attr("fill", "white")
      .style("opacity", 0.5);
    
    tooltip.append("text")
      .attr("x", 15)
      .attr("dy", "1.2em")
      .style("text-anchor", "middle")
      .attr("font-size", "12px")
      .attr("font-weight", "bold");
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>