Множественная анимация по траектории с D3 - PullRequest
1 голос
/ 09 июля 2020

Я хочу, чтобы по пути двигалось несколько кругов. Сделал по ссылке здесь

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>

Круги, анимированные вместе только на одном пути, не анимированные на другом пути, который я нарисовал.

Я прочитал ответ здесь , и пример прикреплен здесь , но я сбит с толку, когда добавляю свойства index, как в примере, я получаю сообщение об ошибке Cannot read property 'getTotalLength' of undefined

this - это отредактированная часть со свойствами 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 () на node (), и он работает в console.log(mypath.nodes()[0]).

Код .attrTween('transform', translateAlong(mypath.node()[0], index)) также работает, но по-прежнему только анимация в первом пути. когда я изменил [0] обратно на [index], он снова вернет Cannot read property 'getTotalLength' of undefined

1 Ответ

1 голос
/ 09 июля 2020

Я являюсь автором ответа , который вы связали. Как я объяснил в этом ответе, решение просто использует индексы кружков для выбора пути:

.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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...