简体   繁体   中英

Multiple animation along path with D3

I want to make multiple circles travel along the path. I made from the reference 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 dx; }).attr("cy", function(d) { return dy; }).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 => dd).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>

The circles animated together only along 1 path, not animated in a different path I made.

I've read an answer here , and the example attached here , but I'm confused when I add the index properties like the example, I get an error message Cannot read property 'getTotalLength' of undefined

this is the edited part with the index properties, I made the transition inside the setTimeout as a function

// 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()

reference https://bl.ocks.org/mbostock/1705868

UPDATE: I change the node() to nodes(), and it works in console.log(mypath.nodes()[0]) .

The code .attrTween('transform', translateAlong(mypath.node()[0], index)) also work, but still only animating in the first path. when I changed the [0] back to [index] , it returns Cannot read property 'getTotalLength' of undefined again

I am the author of the answer you linked. Just as I explained in that answer, the solution is just using the circles' indices to select the path:

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

Pay attention to the fact that it is mypath.nodes() , not mypath.node() (which will return just the first path), and that since translateAlong is now inside a function you have to call it.

Here is the code with that change only:

 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 dx; }).attr("cy", function(d) { return dy; }).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 => dd).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(" + px + "," + py + ")"; } } } // 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM