【问题标题】:Can the 'enter' selection for a line graph be animated?可以为折线图的“输入”选择设置动画吗?
【发布时间】:2018-05-01 23:04:11
【问题描述】:

我有一个可以很好地更新的折线图。直到新数据进入问题。此时,“更新”选择会按原样向下动画,但新数据不会。

很难解释发生了什么,但一个 gif 值得一千个字(观察它何时转换向下):

我已经用更新和渲染方法将图形包装在一个类中。这是我写的渲染方法。

render(){
    // Update both Axes
    this.xAxis.transition()
        .call( d3.axisBottom( this.xScale ) );
    this.yAxis.transition()
        .call( this.createYAxis );

    // Exit
    this.line.exit().remove();

    // Enter
    this.line
      .enter().append( 'path' )
        .attr( 'class', 'valueLine' )
        .transition()
        .attr( 'd', this.valueLine );

    // Update
    this.line.transition().attr( 'd', this.valueLine );

    // Allows method chaining
    return this;
}

为什么“新”数据没有动画效果?

【问题讨论】:

    标签: javascript d3.js linegraph


    【解决方案1】:

    简答

    可以,但您必须为d 属性定义起始值。

    长答案

    从起始值到目标值的转换。现在,在您的“输入”选择中,d 属性没有起始值(即在transition() 之前):

    // Enter
    this.line
        .enter().append( 'path' )
        .attr( 'class', 'valueLine' )
        .transition()//no 'd' attribute before this line
        .attr( 'd', this.valueLine );
    

    因此,该路径的d 属性为null(阅读更多关于此in this answer 的信息)。

    让我们看看:

    var data = [10, 180, 30, 60, 190, 180, 50, 110, 40, 90, 90];
    var svg = d3.select("svg");
    var lineGenerator = d3.line()
      .y(function(d) {
        return d
      })
      .x(function(d, i) {
        return i * 60
      });
    var path = svg.selectAll(null)
      .data([data])
      .enter()
      .append("path");
      
    console.log(path.attr("d"))
    path {
      fill: none;
      stroke-width: 2px;
      stroke: teal;
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <svg width="600" height="200"></svg>

    这里同样的代码,设置d属性:

    var data = [10, 180, 30, 60, 190, 180, 50, 110, 40, 90, 90];
    var svg = d3.select("svg");
    var lineGenerator = d3.line()
      .y(function(d) {
        return d
      })
      .x(function(d, i) {
        return i * 60
      });
    var path = svg.selectAll(null)
      .data([data])
      .enter()
      .append("path")
      .attr("d", lineGenerator);
    path {
      fill: none;
      stroke-width: 2px;
      stroke: teal;
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <svg width="600" height="200"></svg>

    回到你的问题:

    您无法将d 属性从null 转换为您的字符串。不可能。不过,有一些可能的解决方法。

    其中之一是在转换之前创建一个默认的d 属性。例如:

    var data = [10, 180, 30, 60, 190, 180, 50, 110, 40, 90, 90];
    var svg = d3.select("svg");
    var lineGenerator = d3.line()
      .y(function(d) {
        return d
      })
      .x(function(d, i) {
        return i * 60
      });
    var path = svg.selectAll(null)
      .data([data])
      .enter()
      .append("path")
      .attr("d", "M0,100 L600,100")
      .transition()
      .duration(2000)
      .attr("d", lineGenerator);
    path {
      fill: none;
      stroke-width: 2px;
      stroke: teal;
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <svg width="600" height="200"></svg>

    但这是一个可怕的转变!问题是初始的d 属性点太少。因此,字符串插值器无法创建我们希望的插值。

    因此,我们可以使用自定义函数,例如 Mike Bostock 的 this one。这是演示:

    var data = [10, 180, 30, 60, 190, 180, 50, 110, 40, 90, 90];
    var svg = d3.select("svg");
    var lineGenerator = d3.line()
      .y(function(d) {
        return d
      })
      .x(function(d, i) {
        return i * 60
      });
    var path = svg.selectAll(null)
      .data([data])
      .enter()
      .append("path")
      .attr("d", "M0,100 L600,100")
      .transition()
      .duration(2000)
      .attrTween("d", function(d) {
        return pathTween(lineGenerator(d), 4, this)()
      });
    
    function pathTween(d1, precision, self) {
      return function() {
        var path0 = self,
          path1 = path0.cloneNode(),
          n0 = path0.getTotalLength(),
          n1 = (path1.setAttribute("d", d1), path1).getTotalLength();
    
        // Uniform sampling of distance based on specified precision.
        var distances = [0],
          i = 0,
          dt = precision / Math.max(n0, n1);
        while ((i += dt) < 1) distances.push(i);
        distances.push(1);
    
        // Compute point-interpolators at each distance.
        var points = distances.map(function(t) {
          var p0 = path0.getPointAtLength(t * n0),
            p1 = path1.getPointAtLength(t * n1);
          return d3.interpolate([p0.x, p0.y], [p1.x, p1.y]);
        });
    
        return function(t) {
          return t < 1 ? "M" + points.map(function(p) {
            return p(t);
          }).join("L") : d1;
        };
      };
    }
    path {
      fill: none;
      stroke-width: 2px;
      stroke: teal;
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <svg width="600" height="200"></svg>

    【讨论】:

    • 也许我问错了问题,因为这并没有解决问题。我确实接受了您的回答,因为它确实回答了我提出的问题。您是否在 GIF 中看到发生转换时右侧的线是如何存在的?而且它不动,线条的左侧动画下来迎接它?我正在尝试为线条的“右侧”设置动画,以便每一侧彼此相遇。
    • 另外,这在我的课程中无论如何都不起作用,因为我需要对象的上下文来调用 this.lineGenerator
    • 你知道我应该问什么问题吗?因为我不太明白这个问题。
    • 不要尝试转换输入选择:使用我共享的功能计算整个新行并在更新选择中进行转换。但是,为了获得正确的答案,我建议您创建一个运行代码来复制您的 GIF,并将其作为一个新问题发布(因为,正如您所说,您在这里提出了错误的问题)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多