【问题标题】:D3 line chart and adding data points real timeD3折线图和实时添加数据点
【发布时间】:2020-09-21 23:35:31
【问题描述】:

我是 d3 的初学者,正在尝试创建实时图表,以便在旅途中添加新值。我希望图表在添加新点时将旧点向左移动。下面是我的代码,但由于某种原因,浏览器冻结了代码(我在最后注释掉了导致冻结的 .on() 行)。

我做错了什么?

<!DOCTYPE html>

<head></head>

<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js">
    
  </script>
  <script>
<!DOCTYPE html>

<head></head>

<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js">
    
  </script>
  <script>
    var t = -1;
    var n = 40;
    var duration = 750;

    var data = [];

    console.log('hello');
    function next() {
      return {
        time: ++t,
        value: Math.random() * 10
      }
    }

    var margin = {
        top: 6,
        right: 0,
        bottom: 20,
        left: 40
      },
      width = 560 - margin.right,
      height = 120 - margin.top - margin.bottom;

    var xScale = d3.scaleTime()
      .domain([t - n + 1, t])
      .range([0, width]);

    var yScale = d3.scaleLinear()
      .domain([0, 10])
      .range([height, 0]);

    var line = d3.line()
      .x((d) => xScale(d.time))
      .y((d) => yScale(d.value));

    var svg = d3.select('body').append('p').append('svg');

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

    chartArea.append('defs').append('clipPath')
      .attr('id', 'clip2')
      .append('rect')
      .attr('x', 0)
      .attr('y', 0)
      .attr('width', width)
      .attr('height', height);

    chartArea.append('rect')
      .attr('class', 'bg')
      .attr('x', 0)
      .attr('y', 0)
      .attr('width', this.chartWidth)
      .attr('height', this.chartHeight);

    var xAxis = d3.axisBottom(xScale);
    var xAxisG = chartArea.append('g')
      .attr('class', 'x-axis')
      .attr('transform', `translate(0, ${height})`);

    xAxisG.call(xAxis);

    d3.selectAll('x-axis path').style('stroke', 'red')
      .style('stroke-width', 2);

    var yAxis = d3.axisLeft(yScale);
    var yAxisG = chartArea.append('g').attr('class', 'y-axis');
    yAxisG.call(yAxis);

    var grids = chartArea.append('g')
      .attr('class', 'grid')
      .call(d3.axisLeft(yScale).tickSize(-(width)).tickFormat((domain, number) => {
        return ""
      }));

    var pathsG = chartArea.append('g')
      .attr('id', 'paths')
      .attr('class', 'paths')
      .attr('clip-path', 'url(#clip2)');

    tick();

    function tick() {
      console.log('working');
      var newValue = {
        time: ++t,
        value: Math.random() * 10
      };

      data.push(newValue);

      xScale.domain([newValue.time - n + 2, newValue.time]);

      xAxisG.transition().duration(500).ease(d3.easeLinear).call(xAxis);
      console.log('is it?');


      var minerG = pathsG.selectAll('.minerLine').data([data]);
      var minerGEnter = minerG.enter()
        .append('g')
        .attr('class', 'minerLine')
        .merge(minerG);



      var minerSVG = minerGEnter.selectAll('path').data((d) => [d]);



      var minerSVGEnter = minerSVG.enter()
        .append('path')
        .attr('class', 'line')
        .merge(minerSVG)
        .transition()
        .duration(500)
        .ease(d3.easeLinear, 2)

        .attr('d', line(data))

        .on('end', () => {
          requestAnimationFrame(tick)
        })


    }
  </script>
</body>

</html>

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    这个问题是由tick在转换结束时立即同步调用引起的。执行 JavaScript 的 CPU 进程一直忙于更新数据和图表,无法在此选项卡上执行任何其他操作。

    解决此问题的一种方法是使用Window.requestAnimationFrame()

    .on('end', () => {
        requestAnimationFrame(tick)
    })
    

    下面更新的 sn-p 显示了此解决方案的实际效果。

    它不能解决问题中未提及的其他问题,例如图表中未显示任何数据。

    <!DOCTYPE html>
    
    <head></head>
    
    <body>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js">
        
      </script>
      <script>
        var t = -1;
        var n = 40;
        var duration = 750;
    
        var data = [];
    
        console.log('hello');
        function next() {
          return {
            time: ++t,
            value: Math.random() * 10
          }
        }
    
        var margin = {
            top: 6,
            right: 0,
            bottom: 20,
            left: 40
          },
          width = 560 - margin.right,
          height = 120 - margin.top - margin.bottom;
    
        var xScale = d3.scaleTime()
          .domain([t - n + 1, t])
          .range([0, width]);
    
        var yScale = d3.scaleLinear()
          .domain([0, 10])
          .range([height, 0]);
    
        var line = d3.line()
          .x((d) => xScale(d.time))
          .y((d) => yScale(d.value));
    
        var svg = d3.select('body').append('p').append('svg');
    
        var chartArea = svg.append('g')
          .attr('transform', `translate(${margin.left}, ${margin.top})`);
    
        chartArea.append('defs').append('clipPath')
          .attr('id', 'clip2')
          .append('rect')
          .attr('x', 0)
          .attr('y', 0)
          .attr('width', width)
          .attr('height', height);
    
        chartArea.append('rect')
          .attr('class', 'bg')
          .attr('x', 0)
          .attr('y', 0)
          .attr('width', this.chartWidth)
          .attr('height', this.chartHeight);
    
        var xAxis = d3.axisBottom(xScale);
        var xAxisG = chartArea.append('g')
          .attr('class', 'x-axis')
          .attr('transform', `translate(0, ${height})`);
    
        xAxisG.call(xAxis);
    
        d3.selectAll('x-axis path').style('stroke', 'red')
          .style('stroke-width', 2);
    
        var yAxis = d3.axisLeft(yScale);
        var yAxisG = chartArea.append('g').attr('class', 'y-axis');
        yAxisG.call(yAxis);
    
        var grids = chartArea.append('g')
          .attr('class', 'grid')
          .call(d3.axisLeft(yScale).tickSize(-(width)).tickFormat((domain, number) => {
            return ""
          }));
    
        var pathsG = chartArea.append('g')
          .attr('id', 'paths')
          .attr('class', 'paths')
          .attr('clip-path', 'url(#clip2)');
    
        tick();
    
        function tick() {
          console.log('working');
          var newValue = {
            time: ++t,
            value: Math.random() * 10
          };
    
          data.push(newValue);
    
          xScale.domain([newValue.time - n + 2]);
    
          xAxisG.transition().duration(500).ease().call(xAxis);
          console.log('is it?');
    
    
          var minerG = pathsG.selectAll('.minerLine').data([data]);
          var minerGEnter = minerG.enter()
            .append('g')
            .attr('class', 'minerLine')
            .merge(minerG);
    
    
    
          var minerSVG = minerGEnter.selectAll('path').data((d) => [d]);
    
    
    
          var minerSVGEnter = minerSVG.enter()
            .append('path')
            .attr('class', 'line')
            .merge(minerSVG)
            .transition()
            .duration(500)
            .ease(d3.easeLinear, 2)
    
            .attr('d', line(data))
    
            .on('end', () => {
              requestAnimationFrame(tick)
            })
    
    
        }
      </script>
    </body>
    
    </html>

    【讨论】:

    • 非常感谢您的帮助。我还有一件事,我在上面编辑了我的代码,现在 x 轴按照我的意愿移动。但是 d3 显示的时间刻度的格式是 0.005、0.010(由于某种原因,第一个数据点是上午 10 点)等等。有没有办法告诉 D3 将 x 轴数字显示为几秒?
    • 调用时可以格式化x轴。这是有关此的更多信息。 bl.ocks.org/mbostock/9764126。但是我没有看到将 .0010 转换为 AM/PM 格式的简单方法。
    • 谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-29
    • 2015-09-06
    • 1970-01-01
    • 2014-03-12
    • 1970-01-01
    • 2023-03-24
    相关资源
    最近更新 更多