【问题标题】:How to update data in d3 chart with an onchange slider event?如何使用 onchange 滑块事件更新 d3 图表中的数据?
【发布时间】:2021-02-28 11:31:42
【问题描述】:

附件是我在 JS Fiddle 中的最小工作示例。我只能显示初始数据,但是当我更改滑块时,数据不会更新。我在这里的代码哪里出错了?为糟糕的结构道歉 - 我仍然是 D3 的初学者。

https://jsfiddle.net/pv02z8em/1/

   chartGroup
    .selectAll('.line-series')
    .data(data, d=> d.x)
    .join(
      enter => {
        enter.append('path')
        .attr('class', d => `line-series x_${d.x}`)
        .attr("d", drawLine(data))
        .style('stroke', 'dodgerblue')
        .style('stroke-width', 2)
        .style('fill', 'none')
        .style('opacity', 1)
      },
      update => { update.transition().duration(500) },
      exit => { exit.remove() }
    )
}

【问题讨论】:

    标签: javascript svg d3.js


    【解决方案1】:

    您在该代码中有几个问题:

    1. 您没有通过 ID 选择滑块。应该是d3.select('#slider-x-range');

    2. 在监听器中,你没有调用buildLine(data)

    3. 删除buildLine 中与路径本身无关的所有内容,否则每次用户移动滑块时都会创建不同的 SVG;

    4. 您的join 结构当前正在附加几个 路径,一个在另一个之上。这肯定不是你想要的。可能只是:

      let path = chartGroup.selectAll('.line-series')
          .data([data]);
      
      path = path.enter()
          .append('path')
          .attr('class', "line-series")
          .attr("d", d => drawLine(d))
          .style('stroke', 'dodgerblue')
          .style('stroke-width', 2)
          .style('fill', 'none')
          .style('opacity', 1)
          .merge(path)
      
      path.transition().duration(500).attr("d", d => drawLine(d))
      

    这是您的代码,其中包含这些和其他细微更改:

    <!DOCTYPE html>
    <html lang="en">
    
      <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="css/normalize.css">
        <link rel="stylesheet" href="css/skeleton.css">
        <link rel="stylesheet" href="css/skeleton_override.css">
    
        <style>
          svg {
            display: inline-block;
            position: relative;
            vertical-align: top;
            overflow: hidden;
    
          }
    
          .x-axis,
          .y-axis {
            font: 16px sans-serif;
          }
    
          .axis-label {
            font: 18px sans-serif;
          }
    
          .chart-title {
            font: 24px sans-serif;
          }
    
          .x-axis .tick:first-of-type text {
            fill: none;
          }
    
          .body {
            display: flex;
          }
    
          .chart-group-container {
            margin: 10px 20px 20px 20px;
          }
    
          .controls-container {
            display: flex;
            flex-direction: column;
          }
    
          .controls-header {
            color: black;
            padding: 0.5rem 2rem;
            text-align: left;
    
          }
    
          .controls-body {
            overflow: auto;
            font-size: 0.8em;
            cursor: default;
          }
    
          .slidecontainer {
            text-align: left;
            margin: 10px;
            font-family: sans-serif;
            font-size: 14px;
          }
    
          #slider-x-range {
            vertical-align: bottom;
          }
    
        </style>
        <title>Document</title>
      </head>
    
      <body>
        <div class="chart-group-container">
          <div class="row">
            <div class="six columns">
              <div class="chart-container">
                <div class="viz">
    
                </div>
              </div>
            </div>
            <div class="six columns"></div>
            <div class="controls-container">
              <div class="controls-header">UI Controls</div>
              <div class="controls-body">
                <div class="slider-label">Adjust x axis</div>
                <div class="slidecontainer">
                  <span>10</span>
                  <input type="range" min="10" max="100" value="1" id="slider-x-range">
                  <span>100</span>
                </div>
    
              </div>
            </div>
          </div>
    
    
          <script src="https://d3js.org/d3.v5.min.js"></script>
          <script>
            //let sinWave = Math.sin(x)
    
            let range = function(start, stop, step) {
              step = step || 1;
              let arr = []
              for (let i = start; i < stop; i += step) {
                arr.push(i);
              }
              return arr;
            }
    
            let generateSinWave = function(x) {
              let y = []
              x.forEach(function(i) {
                y.push(Math.sin(i))
              });
              return y;
            }
    
            const generateData = (n) => {
              x = range(0, n, 1)
              y = generateSinWave(x)
    
              let labels = ['x', 'y']
    
              let data = []
              for (let i = 0; i < x.length; i++) {
                data.push({
                  x: x[i],
                  y: y[i]
                })
              }
              return data;
            }
    
          </script>
          <script>
            let margin = {
                top: 50,
                right: 30,
                bottom: 30,
                left: 100
              },
              width = 800 - margin.left - margin.right
            height = 400 - margin.top - margin.bottom;
    
            let xScale = d3.scaleLinear()
              .range([0, width])
    
            let yScale = d3.scaleLinear()
              .range([height, 0])
              .nice()
    
    
            let drawLine = d3.line()
              .x(d => xScale(d.x))
              .y(d => yScale(d.y))
              .curve(d3.curveBasis);
    
    
            let svg = d3.select('.viz')
              .append('svg')
              .attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
              .attr("preserveAspectRatio", "xMinYMin meet")
              //.attr('width', `${width + margin.left + margin.right}px`)
              //.attr('height', `${height + margin.top + margin.bottom}px`)
              //.classed("svg-content", true);
              .append('g')
              .attr('class', 'line-chart-container')
              .attr('transform', `translate(${margin.left}, ${margin.top})`);
    
            /* d3.select(".line-chart-container")
                      .attr("style", "outline: thin solid black;") 
                      .attr("margin-right", "102px") */
    
    
    
            const chartGroup = svg.append('g').attr('class', 'line-chart')
    
            // Draw x axis
            const xAxis = d3.axisBottom(xScale).tickSizeOuter(0);
    
            const xAxisDraw = svg
              .append('g')
              .attr('class', 'x-axis')
              //.style('font', '14px sans-serif')
              .attr('transform', `translate(0, ${height / 2})`)
              .call(xAxis);
    
            const yAxis = d3
              .axisLeft(yScale)
              .ticks(10)
            //.tickSizeInner(-width);
    
            const yAxisDraw = svg
              .append('g')
              .attr('class', 'y-axis')
              .call(yAxis);
    
            // x axis label
            svg.append('text')
              .attr('class', 'axis-label')
              .attr('text-anchor', 'end')
              .text('X axis')
              .attr('x', width)
              .attr('y', height - margin.bottom + 50)
    
            // y axis label
            svg.append('text')
              .attr('class', 'axis-label')
              .attr('text-anchor', 'end')
              .attr('transform', 'rotate(-90)')
              .attr('x', margin.top + 50 - (height / 2))
              .attr('y', margin.left - 160)
              .text('Y axis')
    
            // Draw Header
            const header = svg
              .append('g')
              .attr('class', 'chart-title')
              .attr('transform', `translate(${width / 2 - 75}, ${margin.top - 75})`)
              .append('text')
    
            header.append('tspan').text('Sine wave')
    
            function buildLine(data) {
    
              xScale.domain([d3.min(data, d => d.x), d3.max(data, d => d.x)])
    
              yScale.domain([d3.min(data, d => d.y), d3.max(data, d => d.y)])
    
              let path = chartGroup
                .selectAll('.line-series')
                .data([data]);
    
              path = path.enter().append('path')
                .attr('class', "line-series")
                .attr("d", d => drawLine(d))
                .style('stroke', 'dodgerblue')
                .style('stroke-width', 2)
                .style('fill', 'none')
                .style('opacity', 1)
                .merge(path)
    
              path.transition().duration(500).attr("d", d => drawLine(d))
            }
    
            let xRangeSlider = document.getElementById('slider-x-range');
            xRangeSlider.min = 10;
            xRangeSlider.max = 100;
    
            let data = generateData(xRangeSlider.value)
    
            buildLine(data)
    
            d3.select('#slider-x-range')
              .on("change", d => {
                data = generateData(xRangeSlider.value)
                buildLine(data)
              });
    
          </script>
      </body>
    
    </html>

    您会发现,转换可能不是您所期望的:这是一个不相关的问题,d 属性字符串的插值(您可以找到更多信息 here)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多