动态更改d3.js直方图的箱和高度

时间:2016-05-30 22:34:26

标签: javascript jquery-ui d3.js dynamic histogram

我希望能够使用范围滑块动态更改容器/条形/矩形的数量。下面的代码几乎就在那里,但有一些我无法解决的问题。

  1. 当我使用范围滑块时,它似乎跳过了一些值。
  2. 一旦滑动到较低的数字,条形/矩形的高度就超出了高度。
  3. The jsfiddle is here.

    的CSS:

    body {
        font: 10px sans-serif;
    }
    .bar rect {
        fill: steelblue;
        shape-rendering: crispEdges;
    }
    .bar text {
        fill: #fff;
    }
    
    .axis path,
    .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
    }
    #slider-range-max {
        width: 100%;
    }
    

    HTML:

    <div id="histogram_container">
        <div id="histogram"></div>
        <div align="center">
            <p>
                <label for="bins">Number of bins:</label>
                <input type="text" id="bins" readonly style="border:0; color:#f6931f; font-weight:bold;">
            </p>
            <div id="slider-range-max"></div>
        </div>
    </div>
    

    JS:

    var grades_data = [],
        grades = [];
    
    var score_max = 100,
        score_min = 0;
    
    var age_max = 18,
        age_min = 5;
    
    var data_generator = (function() {
        var gen = d3.random.normal(score_max / 2, 15);
        return function() {
            return Math.floor(Math.max(score_min, Math.min(gen(), score_max)));
        }
    }());
    
    for (var i = 0; i < 1001; i++) {
        var age = Math.floor(Math.random() * (age_max - age_min + 1)) + age_min;
        var grade = data_generator();
    
        grades_data.push({
            age,
            grade
        });
        grades.push(grade);
    }
    
    var margin = {
            top: 20,
            right: 20,
            bottom: 30,
            left: 40
        },
        width = 600 - margin.left - margin.right,
        height = 600 - margin.top - margin.bottom;
    
    var format_count = d3.format(",.0f");
    
    var x_gh_scale = d3.scale.linear().range([0, width])
        .domain([0, 100]),
        x_gh_axis = d3.svg.axis().scale(x_gh_scale).orient("bottom");
    
    var data = d3.layout.histogram()
        .bins(x_gh_scale.ticks(20))
        (grades);
    
    var y_gh_scale = d3.scale.linear().range([height, 0])
        .domain([0, d3.max(data, function(d) {
            return d.y;
        })]);
    
    var grade_histo_svg = d3.select("#histogram").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 bar = grade_histo_svg.selectAll(".bar")
        .data(data)
        .enter().append("g")
        .attr("class", "bar")
        .attr("transform", function(d) {
            return "translate(" + x_gh_scale(d.x) + "," + y_gh_scale(d.y) + ")";
        });
    
    bar.append("rect")
        .attr("x", 1)
        .attr("width", x_gh_scale(data[0].dx) - 1)
        .attr("height", function(d) {
            return height - y_gh_scale(d.y);
        });
    
    bar.append("text")
        .attr("dy", ".75em")
        .attr("y", 6)
        .attr("x", x_gh_scale(data[0].dx) / 2)
        .attr("text-anchor", "middle")
        .text(function(d) {
            return format_count(d.y);
        });
    
    grade_histo_svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(x_gh_axis);
    
    $(function() {
        $("#slider-range-max").slider({
            range: "max",
            min: 1,
            max: 20,
            value: 20,
            slide: function(event, ui) {
                $("#bins").val(ui.value);
    
                var data = d3.layout.histogram()
                    .bins(x_gh_scale.ticks(ui.value))
                    (grades);
    
                var bar = grade_histo_svg.selectAll(".bar")
                    .remove()
                    .data(data)
                    .enter().append("g")
                    .attr("class", "bar")
                    .attr("transform", function(d) {
                        return "translate(" + x_gh_scale(d.x) + "," + y_gh_scale(d.y) + ")";
                    });
    
                bar.append("rect")
                    .attr("x", 1)
                    .attr("width", x_gh_scale(data[0].dx) - 1)
                    .attr("height", function(d) {
                        return height - y_gh_scale(d.y);
                    });
    
                bar.append("text")
                    .attr("dy", ".75em")
                    .attr("y", 6)
                    .attr("x", x_gh_scale(data[0].dx) / 2)
                    .attr("text-anchor", "middle")
                    .text(function(d) {
                        return format_count(d.y);
                    });
            }
        });
        $("#bins").val($("#slider-range-max").slider("value"));
    });
    

1 个答案:

答案 0 :(得分:2)

在你的代码中结合奇怪的东西。

首先,

 var data = d3.layout.histogram()
   .bins(x_gh_scale.ticks(ui.value)) //<-- what is your intention here?
   (grades);

.ticks返回刻度线所在的数组。您似乎将其用作thresholds,但这会产生不受欢迎的result

  

指定的计数只是一个提示;规模可能会更多或更少   值取决于输入域。

如果您想要N个箱子,请将其称为:

var data = d3.layout.histogram()
   .bins(ui.value)
   (grades);

其次,您需要在重新装箱后调整y缩放域,以防止您的视频流出页面。这似乎可以解决问题:

y_gh_scale.domain([0, d3.max(data, function(d){
  return d.length
})]);

第三,您不能按照自己的方式链接.remove() call。它返回已删除的元素,您只想重新输入新的:

 grade_histo_svg.selectAll(".bar")
   .remove()

 var bar = grade_histo_svg.selectAll(".bar")
  .data(data)
  .enter().append("g")
  .attr("class", "bar")
  .attr("transform", function(d) {
    return "translate(" + x_gh_scale(d.x) + "," + y_gh_scale(d.y) + ")";
  });

此处有updated fiddle,已应用此更改。