【问题标题】:D3: Resize bar chart according to window widthD3:根据窗口宽度调整条形图大小
【发布时间】:2014-06-24 23:09:33
【问题描述】:

我有一个 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"
                }); 

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

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

然后我的 resize 函数应该更改 SVG 的宽度属性、yAxisBar 刻度线的宽度和 xBarScale rangeRoundBands..... 然后我想我需要选择所有条形(矩形)并更改它们的宽度根据新的 xBarScale.rangeRoundBands 的属性。但我不确定如何正确选择这三件事。到目前为止,只有条形宽度随窗口一起调整大小 - 而不是刻度线或 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());
}

【问题讨论】:

    标签: javascript d3.js responsive-design


    【解决方案1】:

    在 D3 中有一种更简单的方法来执行此操作 您可以使用 SVG 元素上的 viewBox 和 preserveAspectRatio 属性组合来调整图表大小。

      const svg = d3
      .select('div#chart-container')
      .append('svg')
      .style(
        'padding',
        marginRatio.top +
          ' ' +
          marginRatio.right +
          ' ' +
          marginRatio.bottom +
          ' ' +
          marginRatio.left +
          ' '
      )
      .attr('preserveAspectRatio', 'xMinYMin meet')
      .attr(
        'viewBox',
        '0 0 ' +
          (width + margin.left + margin.right) +
          ' ' +
          (height + margin.top + margin.bottom)
      )
    

    参考这里:https://eddyerburgh.me/create-responsive-bar-chart-d3-js

    【讨论】:

      【解决方案2】:

      使用 Bootstrap 3 框架有更好的方法来做到这一点。我已经在一个网站上实现了它,所以我可以向您展示我是如何为我做的,也许您可​​以根据您的特定需求对其进行调整。

      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>
      

      由于我的需要,我已经设置了一个固定的高度,但您也可以保留尺寸auto。 “col-sm-6 col-md-4”使 div 响应。您可以通过http://getbootstrap.com/css/#grid-example-basic了解更多信息

      我们可以借助 id month-view 访问图表,这可以在下面用 d3.js 实现的代码中看到。

      用 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); }));
      
      
          }
      

      由于您已经在使用 d3,我不会花时间解释我在此代码中使用的所有内容,但至少它是一个工作代码。您需要的是零件:

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

      通过获取id为month-view的div的宽度来设置宽度。

      在我的情况下,高度不应包括整个区域。我在栏上方也有一些文字,所以我也需要计算该区域。这就是我用 responsivetext 标识文本区域的原因。为了计算栏的允许高度,我从 div 的高度减去文本的高度

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

      【讨论】:

        【解决方案3】:

        最直接的做法是在 CSS 中将元素的 width 设置为 100%。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-09-29
          • 2019-07-31
          • 2012-06-19
          • 2012-12-15
          • 2012-08-07
          • 2012-04-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多