【问题标题】:Multiple animation along path with D3使用 D3 沿路径的多个动画
【发布时间】:2020-07-09 10:44:16
【问题描述】:

我想让多个圆圈沿着路径移动。我参考here

var w = $(window).innerWidth();
var h = $(window).innerHeight();

var svg = d3
    .select('.container')
    .append('svg')
    .attr('width', w)
    .attr('height', h);

// FIRST (START) POINT & SECOND (FINISH) POINT ARRAY
    var points = [{
      x: 0,
      y: 0,
      r: 5
    }, {
      x: 700,
      y: 100,
      r: 5
    }];
    
    // ADD GROUP 'G'
    var group = svg
        .append('g')
        .attr('transform', 'translate(20,20)');
    
    // ADD CIRCLE AT START & FINISH AS AN VISUAL ANCHOR
    group.selectAll("circle")
      .data(points)
      .enter()
      .append("circle")
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      })
      .attr("r", function(d) {
        return d.r;
      });
    
    // DETERMINED THE SUBPATH FOR THE CURVE
    var cpx = ((points[1].x - points[0].x)),
        cpy = ((points[1].y - points[0].y)) ;
    
    // EMPTY ARRAY
    var linesArray = [];
    
    //POPULATE ARRAY WITH RANDOM CURVE SVG
    for (let i = 0; i <= 10; i++) { // FOR LOOP UNTUK ISI ARRAY DENGAN KORDINAT GARIS MELENGKUNG
        var path = d3.path();
            path.moveTo(points[0].x, points[0].y);
            path.quadraticCurveTo(Math.random(1) * cpx + points[0].x, Math.random() * cpy + points[0].y, points[1].x, points[1].y);
    
        var l = path.toString();
        linesArray.push({
            d: l
        })
    }
    
    // DRAW THE PATH
    var mypath = group
        .selectAll('path')
        .data(linesArray)
        .enter()
        .append("path")
        .attr("d", d => d.d)
        .attr("stroke", "firebrick")
        .attr("stroke-width", 2)
        .attr("fill", "none");
    
    // DRAW THE CIRCLE I WILL ANIMATE
    var circle = group
        .selectAll('circle')
        .data(linesArray)
        .enter()
        .append("circle")
        .attr('id','travel')
        .attr("r", 8)
        .style("fill", 'steelblue')
        .style('opacity', 0.5)
        .attr("transform", "translate(" + points[0].x + ","+points[0].y+" )");
    
    // ANIMATE ALONG PATH FUNCTION
    function translateAlong(mypath) {
        var length = mypath.getTotalLength();
        return () => {
            return x => {
                var p = mypath.getPointAtLength(x * length);
                return "translate("+p.x+","+p.y+")";
            }
        }
    
    }
    
    // ANIMATE THE CIRCLE ALONG PATH
    setTimeout(() => {
        circle
            .each(() => {
                d3
                    .selectAll('#travel')
                    .transition()
                    .duration(10000)
                    .style('fill', 'red')
                    .attrTween('transform', translateAlong(mypath.node()))
                    .remove();
            })
    
    },0)
<div class='container'></div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

圆圈仅沿 1 条路径一起动画,而不是在我制作的其他路径中动画。

我已经阅读了答案here,并附上了示例here,但是当我像示例一样添加index 属性时我很困惑,我收到一条错误消息Cannot read property 'getTotalLength' of undefined

这是带有index 属性的编辑部分,我在setTimeout 中作为函数进行了转换

// this part are modified based on reference below
// https://bl.ocks.org/anonymous/f54345ed04e1a66b7cff3ebeef271428/76fc9fbaeed5dfa867fdd57b24c6451346852568
function transitionCircle(pathItem, index) {
    circle
        .each(() => {
            d3
                .selectAll('#travel')
                .transition()
                .duration(10000)
                .style('fill', 'red')
                .attrTween('transform', translateAlong(mypath.node()[index], index))
                .remove();
        }) // end each
} // end transitionCircle

setTimeout(() => {
    transitionCircle();
},0)// end timeout

function translateAlong(mypath, offset) { //mypath value supposed to be mypath.node()[index] right?
    var length = mypath.getTotalLength();
    return () => {
        return x => {
            var p = mypath.getPointAtLength(x * length);
            return "translate("+p.x+","+p.y+")";
        }
    }
} //end function translateAlong()

参考https://bl.ocks.org/mbostock/1705868

更新: 我把node()改成nodes(),就可以在console.log(mypath.nodes()[0])工作了。

代码.attrTween('transform', translateAlong(mypath.node()[0], index)) 也可以工作,但仍然只在第一条路径中设置动画。当我将[0] 改回[index] 时,它再次返回Cannot read property 'getTotalLength' of undefined

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    我是您链接的answer 的作者。正如我在那个答案中解释的那样,解决方案只是使用圆圈的索引来选择路径:

    .attrTween('transform', (_,i) => translateAlong(mypath.nodes()[i])())
    

    注意它是mypath.nodes(),而不是mypath.node()(它将只返回第一个路径),并且由于translateAlong现在在一个函数中,你必须调用它。

    以下是仅进行该更改的代码:

    var w = $(window).innerWidth();
    var h = $(window).innerHeight();
    
    var svg = d3
      .select('.container')
      .append('svg')
      .attr('width', w)
      .attr('height', h);
    
    // FIRST (START) POINT & SECOND (FINISH) POINT ARRAY
    var points = [{
      x: 0,
      y: 0,
      r: 5
    }, {
      x: 700,
      y: 100,
      r: 5
    }];
    
    // ADD GROUP 'G'
    var group = svg
      .append('g')
      .attr('transform', 'translate(20,20)');
    
    // ADD CIRCLE AT START & FINISH AS AN VISUAL ANCHOR
    group.selectAll("circle")
      .data(points)
      .enter()
      .append("circle")
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      })
      .attr("r", function(d) {
        return d.r;
      });
    
    // DETERMINED THE SUBPATH FOR THE CURVE
    var cpx = ((points[1].x - points[0].x)),
      cpy = ((points[1].y - points[0].y));
    
    // EMPTY ARRAY
    var linesArray = [];
    
    //POPULATE ARRAY WITH RANDOM CURVE SVG
    for (let i = 0; i <= 10; i++) { // FOR LOOP UNTUK ISI ARRAY DENGAN KORDINAT GARIS MELENGKUNG
      var path = d3.path();
      path.moveTo(points[0].x, points[0].y);
      path.quadraticCurveTo(Math.random(1) * cpx + points[0].x, Math.random() * cpy + points[0].y, points[1].x, points[1].y);
    
      var l = path.toString();
      linesArray.push({
        d: l
      })
    }
    
    // DRAW THE PATH
    var mypath = group
      .selectAll('path')
      .data(linesArray)
      .enter()
      .append("path")
      .attr("d", d => d.d)
      .attr("stroke", "firebrick")
      .attr("stroke-width", 2)
      .attr("fill", "none");
    
    // DRAW THE CIRCLE I WILL ANIMATE
    var circle = group
      .selectAll('circle')
      .data(linesArray)
      .enter()
      .append("circle")
      .attr('id', 'travel')
      .attr("r", 8)
      .style("fill", 'steelblue')
      .style('opacity', 0.5)
      .attr("transform", "translate(" + points[0].x + "," + points[0].y + " )");
    
    // ANIMATE ALONG PATH FUNCTION
    function translateAlong(mypath) {
      var length = mypath.getTotalLength();
      return () => {
        return x => {
          var p = mypath.getPointAtLength(x * length);
          return "translate(" + p.x + "," + p.y + ")";
        }
      }
    
    }
    
    // ANIMATE THE CIRCLE ALONG PATH
    setTimeout(() => {
      circle
        .each(() => {
          d3
            .selectAll('#travel')
            .transition()
            .duration(10000)
            .style('fill', 'red')
            .attrTween('transform', (_, i) => translateAlong(mypath.nodes()[i])())
            .remove();
        })
    
    }, 0)
    <div class='container'></div>
    
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

    【讨论】:

    • 您的回答 here 不包括在内。不过我现在明白了,谢谢。
    猜你喜欢
    • 2018-10-24
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 2013-04-29
    相关资源
    最近更新 更多