D3:根据窗宽调整大小条形图。

时间:2022-11-19 13:15:53

I have a D3 bar chart and I'm trying to resize its width according to the browser window size. Here is the chart script where the width variable is being set according to the SVG's parent div's width (#chart1), which has a percentage width set in the css:

我有一个D3条形图,我试着根据浏览器窗口大小调整它的宽度。这里是图表脚本,根据SVG的父div的宽度(#chart1)设置宽度变量,它在css中设置了百分比宽度:

var $chartWidth= $('#chart1').width();
var margin = {top:20, right:35, bottom:30, left:35};
var height = 180 - margin.top - margin.bottom;
var width = $chartWidth - margin.left - margin.right;
var parseDate = d3.time.format("%m/%Y").parse;

var yScaleBar = d3.scale.linear()
    .domain([0,100])
    .range([height, 0]);    

var xBarScale = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1);   

var yAxisBar = d3.svg.axis()
    .scale(yScaleBar)
    .ticks(4)
    .tickSize(-width, 0, 0)
    .orient("left");        

var xBarAxis = d3.svg.axis()
    .scale(xBarScale)
    .ticks(4)
    .orient("bottom");

var canvasBars = d3.select("#chart1").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 data = [
    {date:"01/2009",bar2:"50",bar:"10",q:"1Q 2009"},
    {date:"04/2009",bar2:"56",bar:"32",q:"2Q"},
    {date:"07/2009",bar2:"57",bar:"70",q:"3Q"},
    {date:"10/2009",bar2:"58",bar:"60",q:"4Q"},
    {date:"01/2010",bar2:"52",bar:"45",q:"1Q '10"}
];

  data.forEach(function(d) {
    d.date = parseDate(d.date);
    d.bar2 = +d.bar2;
    d.bar = +d.bar;
  });

xBarScale.domain(d3.range(data.length));

xBarAxis.tickValues([data[0].q,data[1].q,data[2].q,data[3].q,data[4].q]);  

canvasBars.append("g")
      .attr("class", "xaxis")
      .attr("transform", "translate(0," + height + ")")
      .call(xBarAxis);            


canvasBars.append("g")
      .attr("class", "yaxis")
      .call(yAxisBar); 


canvasBars.selectAll("rect")
                .data(data)
                .enter()
                .append("rect")
                .attr({
                "x": function(d) {return xBarScale(d.date);},  
                "y": function(d) {return yScaleBar(d.bar);},  
                "height": function(d) {return height -yScaleBar(d.bar);},
                "width": xBarScale.rangeBand(),
                "fill": "steelblue"
                }); 

When the window resizes, the width variable changes and calls resize();

当窗口调整大小时,宽度变量改变并调用resize();

$( window ).resize(function() {
  $chartWidth = $('#chart1').width();
  width = $chartWidth - margin.left - margin.right;
  resize();
});

Then my resize function should change the width attribute of the SVG, the width of the yAxisBar tick marks, and the xBarScale rangeRoundBands..... I then think I need to select all the bars (rects) and change their width attributes according the to the new xBarScale.rangeRoundBands. but I'm not sure how to correctly select these 3 things. So far, just the bar width's resize along with the window - but not the tick marks or the SVG. Here is what I've tried:

然后,我的resize函数应该改变SVG的宽度属性,yAxisBar标记的宽度,以及xBarScale rangeroundband。然后,我认为我需要选择所有的条(rects)并根据新的xbarscale.rangeroundband改变它们的宽度属性。但是我不知道如何正确地选择这三样东西。到目前为止,仅仅是条宽的大小和窗口的大小——但不是标记或SVG。以下是我的尝试:

function resize(){
    canvasBars.attr("width", width + margin.left + margin.right);
        yAxisBar.tickSize(-width, 0, 0);
        xBarScale.rangeRoundBands([0, width], .1);
        canvasBars.selectAll("rect").attr("width", xBarScale.rangeBand());
}

2 个解决方案

#1


1  

The most straight forward way of doing it would be to just set the width of your element to 100% in the css.

最直接的方法是将元素的宽度设置为100%在css中。

#2


1  

There is a much better way to do this with the Bootstrap 3 framework. I'm already implementing that for a website so I can show you how I do it for me and maybe you can adapt it to your specific needs.

有一个更好的方法来使用Bootstrap 3框架。我已经在为一个网站实现它了,所以我可以向你展示我是如何为我做的,也许你可以根据你的具体需要调整它。

HTML code

HTML代码

<div class="container>
<div class="row">

<div class='col-sm-6 col-md-4' id="month-view" style="height:345px;">
<div id ="responsivetext">Something to write</div>
</div>

</div>
</div>

I have set up a fixed height because of my needs, but you can leave the size auto as well. The "col-sm-6 col-md-4" makes the div responsive. You can learn more at http://getbootstrap.com/css/#grid-example-basic

我已经设定了一个固定的高度,因为我的需要,但是你也可以离开大小汽车。“col-sm-6 col-md-4”使div响应。您可以在http://getbootstrap.com/css/#grid-example-basic中了解更多信息。

We can access the graph with the help of the id month-view which can be seen below in the following code implemented with d3.js.

我们可以在id月视图的帮助下访问该图表,如下面的代码中可以看到d3.js实现的代码。

doing the magic with d3:

用d3做魔术:

    var visualize = function(data){

    // Initialize and select div, get the width and height that you want to use for the bar
    var width = document.getElementById('month-view').offsetWidth;

    var height = document.getElementById('month-view').offsetHeight - document.getElementById('responsivetext2').offsetHeight;



    var total_map = data.map(function (i){ return parseInt(i.total);});


    var heightScale = d3.scale.linear()
                   .domain([0, d3.max(total_map)])
                   .range([0, height-5]);


    var x = d3.scale.ordinal()
          .rangeRoundBands([0, width-30], .1);

    var canvas = d3.select("#month-view").append("svg")
        .attr("width", width - 13)
        .attr("height", height + 20)
        .append("g")
        .attr("transform","translate(5,-20)");

    var div = d3.select("body").append("div")
                      .attr("class", "tooltip")
                      .style("opacity", 0);

    // Add first bar (total)                  
    canvas.selectAll(".bar2")
        .data(data)
        .enter()
        .append('rect')
        .attr('width',  width/12 - 5)
        .attr('height', function (d){return heightScale(d.total)})
        .attr('x', function (d, i) {return i * (width/12 -3 )})
        .attr('y', function (d) {return height + 10 - heightScale(d.total)})
        .attr('class', 'bar2')
        .on("mouseover", function(d) {
          div.transition()
              .duration(200)
              .style("opacity", .95);
          div.html(monthNames(d.month) + "<br/>" + d.total.toFixed(0)+" kr total<br/> <span class='green'>"+ ( (d.total * d.eco)/100  ).toFixed(0) + " kr eko</span>")
              .style("left", (d3.event.pageX - 45) + "px")
              .style("top", (d3.event.pageY - 55) + "px");
          })
        .on("mouseout", function(d) {
            div.transition()
                .duration(500)
                .style("opacity", 0);
        });

    // Add second bar (money)
    canvas.selectAll(".bar")
        .data(data)
        .enter()
        .append('rect')
        .attr('width',  width/12 - 5)
        .attr('height', function (d){ return heightScale(( (d.total * d.eco)/100).toFixed(0))})
        .attr('x', function (d, i) {return i * (width/12 -3 )})
        .attr('y', function (d) {return height + 10 - heightScale(( (d.total * d.eco)/100).toFixed(0) )})
        .attr('class', 'bar')
        .on("mouseover", function(d) {
          div.transition()
              .duration(200)
              .style("opacity", .95);
          div.html(monthNames(d.month) + "<br/>"  + d.total.toFixed(0)+" kr total<br/> <span class='green'>"+ ( (d.total * d.eco)/100).toFixed(0) + " kr eko</span>")
              .style("left", (d3.event.pageX - 45) + "px")
              .style("top", (d3.event.pageY - 55) + "px");
          })
        .on("mouseout", function(d) {
            div.transition()
                .duration(500)
                .style("opacity", 0);
        });

    canvas.append("g")
      .attr("transform", "translate(0,0)");


    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom");

    x.domain(data.map(function(d) { return monthNames(d.month).substring(0, 3); }));


    }

Since you already are using d3, I won't spend the time to explain everything that I've used in this code, but it's a working code at least. What you need is the part:

既然您已经使用了d3,我不会花时间解释我在这段代码中使用的所有内容,但至少它是一个工作代码。你需要的是:

    var width = document.getElementById('month-view').offsetWidth;

    var height = document.getElementById('month-view').offsetHeight - document.getElementById('responsivetext2').offsetHeight;

The width is set by getting the width of the div with the id month-view.

宽度设置为使用id为月视图的div的宽度。

The height in my case should not include the entire area. I also have some text above the bar so I need to calculate that area as well. That's why I identified the area of the text with the id responsivetext. For calculating the allowed height of the bar, I subtracted the height of the text from the height of the div.

我这里的高度不应该包括整个区域。我也有一些文本在栏上,所以我需要计算那个区域。这就是我用id响应文本识别文本区域的原因。为了计算bar的允许高度,我从div的高度减去了文本的高度。

This allows you to have a bar that will adopt all the different screen/div sizes.

这允许您拥有一个可以采用所有不同屏幕/div大小的bar。

#1


1  

The most straight forward way of doing it would be to just set the width of your element to 100% in the css.

最直接的方法是将元素的宽度设置为100%在css中。

#2


1  

There is a much better way to do this with the Bootstrap 3 framework. I'm already implementing that for a website so I can show you how I do it for me and maybe you can adapt it to your specific needs.

有一个更好的方法来使用Bootstrap 3框架。我已经在为一个网站实现它了,所以我可以向你展示我是如何为我做的,也许你可以根据你的具体需要调整它。

HTML code

HTML代码

<div class="container>
<div class="row">

<div class='col-sm-6 col-md-4' id="month-view" style="height:345px;">
<div id ="responsivetext">Something to write</div>
</div>

</div>
</div>

I have set up a fixed height because of my needs, but you can leave the size auto as well. The "col-sm-6 col-md-4" makes the div responsive. You can learn more at http://getbootstrap.com/css/#grid-example-basic

我已经设定了一个固定的高度,因为我的需要,但是你也可以离开大小汽车。“col-sm-6 col-md-4”使div响应。您可以在http://getbootstrap.com/css/#grid-example-basic中了解更多信息。

We can access the graph with the help of the id month-view which can be seen below in the following code implemented with d3.js.

我们可以在id月视图的帮助下访问该图表,如下面的代码中可以看到d3.js实现的代码。

doing the magic with d3:

用d3做魔术:

    var visualize = function(data){

    // Initialize and select div, get the width and height that you want to use for the bar
    var width = document.getElementById('month-view').offsetWidth;

    var height = document.getElementById('month-view').offsetHeight - document.getElementById('responsivetext2').offsetHeight;



    var total_map = data.map(function (i){ return parseInt(i.total);});


    var heightScale = d3.scale.linear()
                   .domain([0, d3.max(total_map)])
                   .range([0, height-5]);


    var x = d3.scale.ordinal()
          .rangeRoundBands([0, width-30], .1);

    var canvas = d3.select("#month-view").append("svg")
        .attr("width", width - 13)
        .attr("height", height + 20)
        .append("g")
        .attr("transform","translate(5,-20)");

    var div = d3.select("body").append("div")
                      .attr("class", "tooltip")
                      .style("opacity", 0);

    // Add first bar (total)                  
    canvas.selectAll(".bar2")
        .data(data)
        .enter()
        .append('rect')
        .attr('width',  width/12 - 5)
        .attr('height', function (d){return heightScale(d.total)})
        .attr('x', function (d, i) {return i * (width/12 -3 )})
        .attr('y', function (d) {return height + 10 - heightScale(d.total)})
        .attr('class', 'bar2')
        .on("mouseover", function(d) {
          div.transition()
              .duration(200)
              .style("opacity", .95);
          div.html(monthNames(d.month) + "<br/>" + d.total.toFixed(0)+" kr total<br/> <span class='green'>"+ ( (d.total * d.eco)/100  ).toFixed(0) + " kr eko</span>")
              .style("left", (d3.event.pageX - 45) + "px")
              .style("top", (d3.event.pageY - 55) + "px");
          })
        .on("mouseout", function(d) {
            div.transition()
                .duration(500)
                .style("opacity", 0);
        });

    // Add second bar (money)
    canvas.selectAll(".bar")
        .data(data)
        .enter()
        .append('rect')
        .attr('width',  width/12 - 5)
        .attr('height', function (d){ return heightScale(( (d.total * d.eco)/100).toFixed(0))})
        .attr('x', function (d, i) {return i * (width/12 -3 )})
        .attr('y', function (d) {return height + 10 - heightScale(( (d.total * d.eco)/100).toFixed(0) )})
        .attr('class', 'bar')
        .on("mouseover", function(d) {
          div.transition()
              .duration(200)
              .style("opacity", .95);
          div.html(monthNames(d.month) + "<br/>"  + d.total.toFixed(0)+" kr total<br/> <span class='green'>"+ ( (d.total * d.eco)/100).toFixed(0) + " kr eko</span>")
              .style("left", (d3.event.pageX - 45) + "px")
              .style("top", (d3.event.pageY - 55) + "px");
          })
        .on("mouseout", function(d) {
            div.transition()
                .duration(500)
                .style("opacity", 0);
        });

    canvas.append("g")
      .attr("transform", "translate(0,0)");


    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom");

    x.domain(data.map(function(d) { return monthNames(d.month).substring(0, 3); }));


    }

Since you already are using d3, I won't spend the time to explain everything that I've used in this code, but it's a working code at least. What you need is the part:

既然您已经使用了d3,我不会花时间解释我在这段代码中使用的所有内容,但至少它是一个工作代码。你需要的是:

    var width = document.getElementById('month-view').offsetWidth;

    var height = document.getElementById('month-view').offsetHeight - document.getElementById('responsivetext2').offsetHeight;

The width is set by getting the width of the div with the id month-view.

宽度设置为使用id为月视图的div的宽度。

The height in my case should not include the entire area. I also have some text above the bar so I need to calculate that area as well. That's why I identified the area of the text with the id responsivetext. For calculating the allowed height of the bar, I subtracted the height of the text from the height of the div.

我这里的高度不应该包括整个区域。我也有一些文本在栏上,所以我需要计算那个区域。这就是我用id响应文本识别文本区域的原因。为了计算bar的允许高度,我从div的高度减去了文本的高度。

This allows you to have a bar that will adopt all the different screen/div sizes.

这允许您拥有一个可以采用所有不同屏幕/div大小的bar。