首页 > 解决方案 > 路径和图形元素未在 D3 中显示

问题描述

我想根据我的数据集绘制一系列线。很难弄清楚出了什么问题,因为我没有收到任何错误消息。我希望比我更有经验的人熟悉一些常见的陷阱。

这是对两个示例的改编:使用 es6 类构建 d3 代码d3 多线图

以下是相关部分(我认为出现问题的地方):

  createScales(){
    this.keynames = d3.scaleOrdinal();

    this.keynames.domain(Object.keys(this.data[0]).filter(key => key!=='date'));

    this.keymap = this.keynames.domain().map(
      keyname => ({name: keyname, values: this.data.map(
        d => ({date: d.date, key: +d[keyname]})
      )})
    );

    const m = this.margin;

    const xExtent = d3.extent(this.keymap, d => d.date);

    const yExtent = [0,d3.max(this.keymap, d => Math.max(d.values, function(v){ return v.key }) )];

    this.xScale = d3.scaleTime()
                    .range([0, this.width-m.right])
                    .domain(xExtent).nice();

    this.yScale = d3.scaleLinear()
                    .range([this.height-(m.top+m.bottom), 0])
                    .domain(yExtent).nice();
  }

  addAxes(){
    const m = this.margin;

    const xAxis = d3.axisBottom()
                    .scale(this.xScale)
                    .ticks(8);

    const yAxis = d3.axisLeft()
                    .scale(this.yScale)
                    .ticks(4);

    this.plot.append("g")
             .attr("class", "x axis")
             .attr("transform", `translate(0, ${this.height-(m.top+m.bottom)})`)
             .call(xAxis.ticks(8)); //more than 8 ticks shown

    this.plot.append("g")
             .attr("class", "y axis")
             .call(yAxis.ticks(4)) //more than 4 ticks shown, rest of chain not working
             .append("text")
             .attr("transform", "rotate(-90)")
             .attr("y", 6)
             .attr("dy", ".71em") 
             .style("text-anchor", "end")
             .text("$USD");
  }

  addLine(){

    const line = d3.line()
                   .x(function(d){console.log(d); return this.xScale(d.date)}) //no log, no line
                   .y(function(d){return this.yScale(d.key)});

    this.plot.append('path')
             .datum(this.keymap)
             .classed('line', true)
             .attr('d', function(d){return line(d.values)}) //logs correct values, no line(data structure outlined below(1))
             .style('stroke', this.lineColor || 'red')
             .style('fill', 'none');

  }

(1)此控制台日志中显示的数据示例:

Array(4):
     0: {name: aapl, values: Array(691)}
          values: [0 .. 99]:
                  0: {date: Monday etc.., key: 38}
                  1: {date: Tuesday etc.., key: 39}
                  2: {date: Wednesday etc.., key: 38} ... etc

     1: {name: tsla, values: Array(691)}
     etc.

我看不出问题出在哪里.. 我的数据结构与示例中的完全相同,而且我之前的无类实现效果很好.. 未显示的轴元素只是蛋糕,但也许他们指出了一个更大的问题。谢谢你。

完整的代码和数据:文件1:plot.js

const chart = new Chart({element: document.querySelector('#graph')});

let getData = d3.csv('d1.csv', function(d){
                        function removeNaN(e,c){
                          if (e>0) {return e} else {return c}
                        }
                        return { date: d3.timeParse("%Y-%m-%d")(d.Date),
                                 aapl : d.AAPL, tsla : d.TSLA,
                                 aapl_sma: removeNaN(+d.SMA_AAPL,d.AAPL),
                                 tsla_sma: removeNaN(+d.SMA_TSLA,d.TSLA)
                        }
                      }).then(init);

function init(data){
  chart.setData(data);
}

文件 2:chart.js

class Chart{
  constructor(opts){
    this.data = opts.data;
    this.element = opts.element;

  }

  draw(){
    this.width = this.element.offsetWidth;
    this.height = this.width/2;
    this.padding = 50;
    this.margin = {
      top : 20,
      bottom : 20,
      left : 30,
      right : 50
    };

    this.element.innerHTML = '';
    const svg = d3.select(this.element).append('svg');
    svg.attr('width', this.width);
    svg.attr('height', this.height);

    this.plot = svg.append('g')
                   .attr('transform', `translate(${this.margin.left},${this.margin.top})`);

    this.createScales();
    this.addAxes();
    this.addLine();

  }

  createScales(){
    this.keynames = d3.scaleOrdinal();

    this.keynames.domain(Object.keys(this.data[0]).filter(key => key!=='date'));

    this.keymap = this.keynames.domain().map(
      keyname => ({name: keyname, values: this.data.map(
        d => ({date: d.date, key: +d[keyname]})
      )})
    );

    const m = this.margin;

    const xExtent = d3.extent(this.data, d => d.date);

    const yExtent = [0,d3.max(this.keymap, d => d3.max(d.values, function(v){ return v.key }) )];

    this.xScale = d3.scaleTime()
                    .range([0, this.width-m.right])
                    .domain(xExtent).nice();

    this.yScale = d3.scaleLinear()
                    .range([this.height-(m.top+m.bottom), 0])
                    .domain(yExtent).nice();

  }

  addAxes(){
    const m = this.margin;

    const xAxis = d3.axisBottom()
                    .scale(this.xScale);

    const yAxis = d3.axisLeft()
                    .scale(this.yScale);

    this.plot.append("g")
             .attr("class", "x axis")
             .attr("transform", `translate(0, ${this.height-(m.top+m.bottom)})`)
             .call(xAxis.ticks(8));

    this.plot.append("g")
             .attr("class", "y axis")
             .call(yAxis.ticks(4))
             .append("text")
             .attr("transform", "rotate(-90)")
             .attr("y", 6)
             .attr("dy", ".71em")
             .style("text-anchor", "end")
             .text("$USD");

  }

  addLine(){
    const line = d3.line()
                   .x(function(d){console.log(d); return this.xScale(d.date)})
                   .y(function(d){return this.yScale(d.key)});

    this.plot.append('path')
             .datum(this.keymap)
             .classed('line', true)
             .attr('d', function(d){console.log(d); return line(d.values)})
             .style('stroke', this.lineColor || 'red')
             .style('fill', 'none');
    console.log(this.plot)
  }

  setColor(newColor){
    this.plot.select('.line')
             .style('stroke', newColor);

    this.lineColor = newColor;

  }

  setData(data){
    this.data = data;

    this.draw();

  }
}

文件 3:index.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Graphs of the US Economy</title>
    <link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@200;400;900&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="style.css">
    <style>
    </style>
  </head>
  <body>
    <div id="container" class="container">
      <div id='graph'></div>
      <div id="sections">
        <div><h1>THIS IS A TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
        <div><h1>THIS IS A SECOND TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
        <div><h1>THIS IS A THIRD TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
        <div><h1>THIS IS A FOURTH TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
        <div><h1>THIS IS A FIFTH TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
        <div><h1>THIS IS A SIXTH TEST SECTION THAT I'M USING TO FIGURE OUT HOW MY CODE WORKS</h1></div>
      </div>
    </div>
    
    <script src="https://d3js.org/d3.v6.js"></script>
    <script src="graph-scroll.js"></script>
    <script src="chart.js"></script>
    <script src="plot.js"></script>
    <script>
    </script>
  </body>
</html>

文件 4:d1.csv,39 行:

Date,AAPL,SMA_AAPL,TSLA,SMA_TSLA
2018-12-31,38.33848571777344,,66.55999755859375,
2019-01-02,38.382225036621094,,62.02399826049805,
2019-01-03,34.55907440185547,,60.071998596191406,
2019-01-04,36.03437805175781,,63.53799819946289,
2019-01-07,35.95417022705078,,66.99199676513672,
2019-01-08,36.63956832885742,,67.06999969482422,
2019-01-09,37.26177215576172,,67.70600128173828,
2019-01-10,37.380863189697266,,68.99400329589844,
2019-01-11,37.013858795166016,,69.4520034790039,
2019-01-14,36.4572868347168,,66.87999725341797,
2019-01-15,37.20343780517578,,68.88600158691406,
2019-01-16,37.657936096191406,,69.20999908447266,
2019-01-17,37.88154602050781,,69.46199798583984,
2019-01-18,38.11487579345703,,60.45199966430664,
2019-01-22,37.259342193603516,,59.784000396728516,
2019-01-23,37.410030364990234,,57.518001556396484,
2019-01-24,37.113521575927734,,58.301998138427734,
2019-01-25,38.34333801269531,,59.40800094604492,
2019-01-28,37.988487243652344,,59.2760009765625,
2019-01-29,37.59474182128906,,59.492000579833984,
2019-01-30,40.16377258300781,,61.75400161743164,
2019-01-31,40.453006744384766,,61.40399932861328,
2019-02-01,40.472450256347656,,62.44200134277344,
2019-02-04,41.622066497802734,,62.577999114990234,
2019-02-05,42.33420181274414,,64.2699966430664,
2019-02-06,42.34878158569336,,63.444000244140625,
2019-02-07,41.546722412109375,,61.50199890136719,
2019-02-08,41.59553909301758,,61.15999984741211,
2019-02-11,41.35633087158203,,62.56800079345703,
2019-02-12,41.71269989013672,38.606483713785806,62.36199951171875,63.48539975484212
2019-02-13,41.539398193359375,38.71318079630534,61.63399887084961,63.32119979858398
2019-02-14,41.69073486328125,38.823464457194014,60.75400161743164,63.278866577148435
2019-02-15,41.59797286987305,39.05809440612793,61.57600021362305,63.32899996439616
2019-02-19,41.72246551513672,39.247697321573895,61.12799835205078,63.24866663614909
2019-02-20,41.990962982177734,39.44892374674479,60.512001037597656,63.032666778564455
2019-02-21,41.75419616699219,39.619411341349284,58.24599838256836,62.738533401489256
2019-02-22,42.22041702270508,39.78469950358073,58.94200134277344,62.44640007019043
2019-02-25,42.5279655456543,39.95626958211263,59.75400161743164,62.13840001424153

标签: javascriptd3.js

解决方案


主要问题出在addLine方法上。首先,尝试将 a 添加console.log(this)到您传递给的函数中.x()

const line = d3.line()
    .x(function(d){console.log('this:', this); return this.xScale(d.date)})
    .y(function(d){return this.yScale(d.key)});

您会在此函数中看到thisis undefined,因此您无法访问this.xScale. 这是由于this函数内部的行为。

考虑这个例子:

class Example {
      constructor(x) {
        this.x = x;
      }

      print() {
        console.log('in print method:', this);
        
        function normal() {
          console.log('in normal function:', this);
        }

        const arrow = () => console.log('in arrow function:', this);

        normal();
        arrow();
      }
  }

  const ex = new Example(4);
  ex.print();

this在普通函数中未定义,但在箭头函数中,this指的是对象。因此,我们可以调整线生成器以使用箭头函数:

const line = d3.line()
    .x(d => this.xScale(d.date))
    .y(d => this.yScale(d.key));

其次,您想为 中的每个元素绘制一条线this.keymap,因此您需要进行数据连接:

this.plot.append('g')
  .selectAll('path')
  .data(this.keymap)
  .join('path')
    .classed('line', true)
    .attr('d', function (d) { return line(d.values) })
    .style('stroke', this.lineColor || 'red')
    .style('fill', 'none');

这是一个完整的例子。我已经更改了数据传递到图表中的方式,以便我可以将它们全部放在一个片段中。

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <script src="https://d3js.org/d3.v7.js"></script>
</head>

<body>
  <div id="graph"></div>

  <script>
    class Chart {
      constructor(opts) {
        this.data = opts.data;
        this.element = opts.element;
      }

      draw() {
        this.width = this.element.offsetWidth;
        this.height = this.width / 2;
        this.padding = 50;
        this.margin = {
          top: 20,
          bottom: 20,
          left: 30,
          right: 50
        };

        this.element.innerHTML = '';
        const svg = d3.select(this.element).append('svg');
        svg.attr('width', this.width);
        svg.attr('height', this.height);

        this.plot = svg.append('g')
            .attr('transform', `translate(${this.margin.left},${this.margin.top})`);

        this.createScales();
        this.addAxes();
        this.addLine();
      }

      createScales() {
        this.keynames = d3.scaleOrdinal();

        this.keynames.domain(Object.keys(this.data[0]).filter(key => key !== 'date'));

        this.keymap = this.keynames.domain().map(
          keyname => ({
            name: keyname, values: this.data.map(
              d => ({ date: d.date, key: +d[keyname] })
            )
          })
        );

        const m = this.margin;

        const xExtent = d3.extent(this.data, d => d.date);

        const yExtent = [0, d3.max(this.keymap, d => d3.max(d.values, function (v) { return v.key }))];

        this.xScale = d3.scaleTime()
            .range([0, this.width - m.right])
            .domain(xExtent).nice();

        this.yScale = d3.scaleLinear()
            .range([this.height - (m.top + m.bottom), 0])
            .domain(yExtent).nice();
      }

      addAxes() {
        const m = this.margin;

        const xAxis = d3.axisBottom()
          .scale(this.xScale);

        const yAxis = d3.axisLeft()
          .scale(this.yScale);

        this.plot.append("g")
            .attr("class", "x axis")
            .attr("transform", `translate(0, ${this.height - (m.top + m.bottom)})`)
            .call(xAxis.ticks(8));

        this.plot.append("g")
            .attr("class", "y axis")
            .call(yAxis.ticks(4))
          .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 6)
            .attr("dy", ".71em")
            .attr("fill", "black")
            .style("text-anchor", "end")
            .text("$USD");
      }

      addLine() {
        const line = d3.line()
          .x(d => this.xScale(d.date))
          .y(d => this.yScale(d.key));

        this.plot.append('g')
          .selectAll('path')
          .data(this.keymap)
          .join('path')
            .classed('line', true)
            .attr('d', function (d) { return line(d.values) })
            .style('stroke', this.lineColor || 'red')
            .style('fill', 'none');
      }

      setColor(newColor) {
        this.plot.select('.line')
          .style('stroke', newColor);

        this.lineColor = newColor;
      }

      setData(data) {
        this.data = data;

        this.draw();
      }
    }

    const chart = new Chart({ element: document.querySelector('#graph') });

    const data = d3.csvParse(`Date,AAPL,SMA_AAPL,TSLA,SMA_TSLA
2018-12-31,38.33848571777344,,66.55999755859375,
2019-01-02,38.382225036621094,,62.02399826049805,
2019-01-03,34.55907440185547,,60.071998596191406,
2019-01-04,36.03437805175781,,63.53799819946289,
2019-01-07,35.95417022705078,,66.99199676513672,
2019-01-08,36.63956832885742,,67.06999969482422,
2019-01-09,37.26177215576172,,67.70600128173828,
2019-01-10,37.380863189697266,,68.99400329589844,
2019-01-11,37.013858795166016,,69.4520034790039,
2019-01-14,36.4572868347168,,66.87999725341797,
2019-01-15,37.20343780517578,,68.88600158691406,
2019-01-16,37.657936096191406,,69.20999908447266,
2019-01-17,37.88154602050781,,69.46199798583984,
2019-01-18,38.11487579345703,,60.45199966430664,
2019-01-22,37.259342193603516,,59.784000396728516,
2019-01-23,37.410030364990234,,57.518001556396484,
2019-01-24,37.113521575927734,,58.301998138427734,
2019-01-25,38.34333801269531,,59.40800094604492,
2019-01-28,37.988487243652344,,59.2760009765625,
2019-01-29,37.59474182128906,,59.492000579833984,
2019-01-30,40.16377258300781,,61.75400161743164,
2019-01-31,40.453006744384766,,61.40399932861328,
2019-02-01,40.472450256347656,,62.44200134277344,
2019-02-04,41.622066497802734,,62.577999114990234,
2019-02-05,42.33420181274414,,64.2699966430664,
2019-02-06,42.34878158569336,,63.444000244140625,
2019-02-07,41.546722412109375,,61.50199890136719,
2019-02-08,41.59553909301758,,61.15999984741211,
2019-02-11,41.35633087158203,,62.56800079345703,
2019-02-12,41.71269989013672,38.606483713785806,62.36199951171875,63.48539975484212
2019-02-13,41.539398193359375,38.71318079630534,61.63399887084961,63.32119979858398
2019-02-14,41.69073486328125,38.823464457194014,60.75400161743164,63.278866577148435
2019-02-15,41.59797286987305,39.05809440612793,61.57600021362305,63.32899996439616
2019-02-19,41.72246551513672,39.247697321573895,61.12799835205078,63.24866663614909
2019-02-20,41.990962982177734,39.44892374674479,60.512001037597656,63.032666778564455
2019-02-21,41.75419616699219,39.619411341349284,58.24599838256836,62.738533401489256
2019-02-22,42.22041702270508,39.78469950358073,58.94200134277344,62.44640007019043
2019-02-25,42.5279655456543,39.95626958211263,59.75400161743164,62.13840001424153`, function (d) {
      function removeNaN(e, c) {
        if (e > 0) { return e; } else { return c; }
      }
      return {
        date: d3.timeParse("%Y-%m-%d")(d.Date),
        aapl: +d.AAPL,
        tsla: +d.TSLA,
        aapl_sma: removeNaN(+d.SMA_AAPL, +d.AAPL),
        tsla_sma: removeNaN(+d.SMA_TSLA, +d.TSLA)
      };
    });

    chart.setData(data);
  </script>
</body>

</html>


推荐阅读