【问题标题】:Multiple Responsive D3 Charts on same page reusing chart script同一页面上的多个响应式 D3 图表重用图表脚本
【发布时间】:2017-12-26 04:08:56
【问题描述】:

我正在尝试使用具有不同数据的相同脚本在同一页面上创建 3 个响应式条形图。

我的问题是在调整窗口大小时出现的。我不断遇到仅在要呈现的最后一个图表中更新的元素。

我很抱歉没有提供 jsfiddle,但我无法让 fiddle 呈现我的代码(fiddle - newbee)

下面的 sn-p 代码只生成轴'。我遇到的第一个问题是 d3.axisBottom(scale) - 作为我遇到的一个例子。

页面:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>AgeReport 1.3</title>

    <script src="jquery-1.11.2.js"></script>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script 
  src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js">
  </script>`

    <link rel=stylesheet type=text/css 
href="https://cdnjs.cloudflare.com/ajax/libs/twitter-
bootstrap/3.3.5/css/bootstrap.min.css">

   <style>
       body {
           background-color: #e6e6e6;
           font-family: sans-serif;
           font-size: 12px;
       }
        section {
            position: relative;
            padding: 24px;
            height: 100%;
        }
                   .section-body:first-child {
            margin-top: 24px;
        }
                   .col-md-3, .col-lg-3, .col-md-4, .col-lg-4, .col-md-6, 
 .col-lg-6 {
            padding: 0;
        }
       .card {
          position: relative;
          background-color: #F5F5F5;
          color: #212121;
          -webkit-border-radius: 2px;
          -moz-border-radius: 2px;
          border-radius: 2px;
          border: 1px solid #BDBDBD;
          box-shadow: none;
          height: 100%;
        }
        .card-head.card-head-xs {
          line-height: 32px;
          min-height: 36px;
        }
        .card-head {
          position: relative;
          line-height: 40px;
          min-height: 44px;
          vertical-align: middle;
          border-radius: 2px 2px 0 0;
        }
       section {
           background-repeat: no-repeat;
       }
       .row {
           margin-left: 0;
       }
       .col-md-4 {
           padding-left: 5px;
           padding-right: 5px;
       }
       .card-head {
           background-color: #bdbdbd;
       }
       .card-body {
           padding: 10px 10px 10px 10px;
       }

       /*-- Age Chart --*/

       #age-chart-row {
           position: relative;
           height: 345px;
       }
       #age-chart-row .card {
           border-width: 1px 1px 0 1px;
       }
       .chart-container {
           height: 288px;
           border: 1px solid #bcbcbc;
           background-color: white;
       }
       .age-chart-canvas {
           position: absolute;
           width: 100%;
            height: 100%;
       }
        .axis--y .tick line {
            stroke: lightgray;
        }

    </style>

    <script type="text/javascript">

        var ageDistributionData = {....}'

        $(document).ready(function() {

            var vData = ageDistributionData;
            var dData = ageDistributionData;
            var mData = ageDistributionData;

            var vChart = new AgeChart("#vChart", vData);
            var dChart = new AgeChart("#dChart", dData);
            var mChart = new AgeChart("#mChart", mData);

        });

    </script>
</head>

<body>
    <section>
        <div class="section-body">
            <div id="age-chart-row" class="row">
                <div class="col-md-4">
                    <div class="card">
                        <div class="card-head card-head-xs"></div>
                        <div class="card-body">
                            <div id="vChart" class="chart-container"></div>
                        </div>
                    </div>
                </div>
                <div class="col-md-4">
                    <div class="card">
                        <div class="card-head card-head-xs"></div>
                        <div class="card-body">
                            <div id="dChart" class="chart-container"></div>
                        </div>
                    </div>
                </div>
                <div class="col-md-4">
                    <div class="card">
                        <div class="card-head card-head-xs"></div>
                        <div class="card-body">
                            <div id="mChart" class="chart-container"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>
</body>
</html>

图表脚本:

function AgeChart(container, data) {

var containerW, containerH, chartW, chartH, svg, chartG,
    xScale, x, y, gAxis, xAxis, yAxis, xBottom, bars;
var margin = {top: 20, right: 10, bottom: 50, left: 60};

//-- axis labels -> will need translating --
var yLabelStr = "Anzahl",
    xLabelStr = "Alter";

// parse raw data
var agedData = parseAgedData(data),
    sizeMax = data.AgeDistribution.DataOwnerSizeMax,
    countMax = 0;
    for (var i = 0; i < agedData.length; i++) {
        if(agedData[i].doCount > countMax) {
            countMax = agedData[i].doCount;
        }
    };

// init scales
var xScale = d3.scaleBand()
    .paddingInner(0.16)
    .paddingOuter(0.1);
xScale.domain(agedData.map(function (d) {
    return d.label;
}));
x = d3.scaleBand()
    .paddingInner(0.16)
    .paddingOuter(0.1);
x.domain(agedData.map(function (d, i) {
    return i + 1;
}));
y = d3.scaleLinear();
y.domain([0, countMax]);

// init containers
svg = d3.select(container)
    .append("svg")
    .attr("id", container.substring(1) + "Svg")
    .classed("age-chart-canvas", true);
chartG = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// init axis lines
yAxis = chartG.append("g")
    .attr("class", "axis axis--y");
gAxis = chartG.append("g")
    .attr("class", "axis axis--x");
xAxis = gAxis.append("line")
    .style("stroke", "black")
    .attr("x1", 0)
    .attr("x2", 0)
    .classed("x-domain", true);

// render chart
render();

function render() {

    updateDimensions();

    // set tick lines
    yAxis.call(d3.axisLeft(y))
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", "0.71em")
        .attr("text-anchor", "end")
        .text(function(d) {
            return d;
        });
    d3.selectAll("g.axis--y g.tick line")
        .attr("x1", function(d) {
            return chartW;
        });
    gAxis.attr("transform", "translate(0," + chartH + ")")
        .call(new d3.axisBottom(xScale));
    gAxis.selectAll(".domain").remove();
    d3.selectAll(".x-domain")
        .attr("x2", function(d) {
            return chartW;
        });

    d3.selectAll("g.axis--x g.tick line")
        .attr("y2", function(d, i) {
            if(i%2 == 0)
                return 6;
            else
                return 16;
        })
        .style("fill", "#bdbdbd");
    d3.selectAll("g.axis--x g.tick text")
        .attr("y", function(d, i) {
            if(i%2 == 0)
                return 9;
            else
                return 20;
        });

};

function updateDimensions() {
    containerW = getDimension(container, "width");
    containerH = getDimension(container, "height");
    chartW = containerW - margin.left - margin.right;
    chartH = containerH - margin.top - margin.bottom;

    // set axis scales
    xScale.rangeRound([0, chartW]);
    x.rangeRound([0, chartW]);
    y.rangeRound([chartH, 0]);
};

d3.select(window).on("resize", render);

};

谢谢!

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    我也遇到了同样的问题。我知道你找到了解决方案,但我想我会提供我发现的内容,以防它帮助到此页面的其他人,因为这个问题比我发现的任何其他问题更准确地描述了我的问题。

    我的解决方案最终来自以下 Stack Overflow 帖子。那里的答案说明您需要向侦听器添加命名空间,否则添加新侦听器时将删除现有侦听器。

    How to have multiple d3 window resize events

    对于这个例子,我不得不改变这段代码:

        d3.select(window).on("resize", render);
    

        var resizeId = "resize." + scope.chartId
        d3.select(window).on(resizeId, render);
    

    其中 scope.chartId 是每个图表的唯一 ID,因此侦听器也是唯一的,并且在添加新图表时不会被删除。

    【讨论】:

      【解决方案2】:

      我似乎找到了解决办法。在熟悉了 Javascript 对象之后,我重新编写了代码来创建并返回一个封装了图表属性和方法的图表对象。然后我可以在多个图表对象中调用 render 方法来单独更新它们。

      新的图表脚本:

      function AgeChart(chartContainer, data) {
      
          var chartObj = {
      
          // public properties
          xAxisLabel: "Alter",
          yAxisLabel: "Anzahl",
          margin: {top: 20, right: 10, bottom: 50, left: 60},
          width: 0,
          height: 0,
          dummyData: 0,
          sizeMax: 0,
          countMax: 0,
      
          // private properties
          $chartGroup: 0,
          $container: "",
          $d: 0,
          $svg: 0,
          $x: d3.scaleBand()
              .paddingInner(0.16)
              .paddingOuter(0.1),
          $xscale: d3.scaleBand()
              .paddingInner(0.16)
              .paddingOuter(0.1),
          $y: d3.scaleLinear(),
          $xAxis: 0,
          $yAxis: 0,
      
          // property sets
          get data() { return this.$d; },
          set data(d) {
              this.$d = d;
              this.dummyData = parseAgedData(d);
              this.sizeMax = d.AgeDistribution.DataOwnerSizeMax;
              for (var i = 0; i < this.dummyData.length; i++) {
                  if(this.dummyData[i].doCount > this.countMax) {
                      this.countMax = chartObj.dummyData[i].doCount;
                  }
              };
              this.$xscale.domain(this.dummyData.map(function(d) {
                  return d.label;
              }));
              this.$x.domain(this.dummyData.map(function(d,i) {
                  return i + 1;
              }))
              this.$y.domain([0, this.countMax]);
          },
          get container() { return this.$container },
          set container(c) {
              this.$container = c;
              updateDimensions();
              this.$svg = d3.select(this.container)
                  .append("svg")
                  .attr("id", chartObj.container.substring(1) + "Svg")
                  .classed("age-chart-canvas", true);
              this.$chartGroup = this.$svg.append("g")
                  .attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");
              this.$yAxis = this.$chartGroup.append("g")
                  .attr("class", "axis axis--y");
              this.$xAxis = this.$chartGroup.append("g")
                  .attr("class", "axis axis--x")
                  .attr("transform", "translate(0," + this.height + ")");
          },
      
          // methods
          render: function() {
      
              updateDimensions();
      
              this.$xscale.rangeRound([0, this.width]);
              this.$x.rangeRound([0, this.width]);
              this.$y.rangeRound([this.height, 0]);
              this.$yAxis.call(d3.axisLeft(this.$y))
                  .append("text")
                  .attr("transform", "rotate(-90)")
                  .attr("y", 6)
                  .attr("dy", "0.71em")
                  .attr("text-anchor", "end")
                  .text(function(d) {
                      return d;
                  });
              var chartW = this.width;
              d3.selectAll("g.axis--y g.tick line")
                  .attr("x1", function(d) {
                      return chartW;
                  });
              this.$xAxis.call(d3.axisBottom(this.$xscale));
              d3.selectAll("g.axis--x g.tick line")
                  .attr("y2", function(d, i) {
                      if(i%2 == 0)
                          return 6;
                      else
                          return 16;
                  })
                  .style("fill", "#bdbdbd");
              d3.selectAll("g.axis--x g.tick text")
                  .attr("y", function(d, i) {
                      if(i%2 == 0)
                          return 9;
                      else
                          return 20;
                  });
      
             }
          };
      
          chartObj.data = data;
          chartObj.container = chartContainer;
          chartObj.render();
      
          function updateDimensions() {
      
              var containerW = getDimension(chartObj.container, "width"),
              containerH = getDimension(chartObj.container, "height");
              chartObj.width = containerW - chartObj.margin.left -   
              chartObj.margin.right;
              chartObj.height = containerH - chartObj.margin.top -   
              chartObj.margin.bottom;
      
         };
      
          return chartObj;
      
      };
      

      然后在我的页面上,当窗口调整大小并且所有图表都独立更新时,我会调用 render()...

          <script type="text/javascript">
      
              $(document).ready(function() {
      
                  var vData = ageDistributionData;
                  var dData = ageDistributionData;
                  var mData = ageDistributionData;
      
                  var vChart = AgeChart("#vChart", vData);
                  var dChart = new AgeChart("#dChart", dData);
                  var mChart = new AgeChart("#mChart", mData);
      
                  window.addEventListener("resize", function() {
                      vChart.render();
                      dChart.render();
                      mChart.render();
                  })
      
              });
      
          </script>
      

      欢迎任何提示或建议。

      【讨论】:

      • 我已经更改了代码,以便在 AgeChart 函数中的 chartObj 之外声明“私有属性”(在上面的代码中以“$”开头)。换句话说,使用闭包。这才是真正的关键——使用闭包并返回一个可重用的对象。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-15
      • 1970-01-01
      • 2013-07-11
      • 1970-01-01
      • 2015-11-07
      相关资源
      最近更新 更多