Вопрос

I have json data: an array of frames, each frame is an array of 10 nodes, and each node is a dictionary with the node id and x,y coordinates. I want to create a d3 animation that transitions between the positions of the node in each frame.

I am using setInterval(...) to set up the transitions to the next set of node positions. However, a bunch of new nodes are added instead of the existing nodes updating by going through the transition. Am I doing something wrong with the key function, or something else?

Here's a sample of what the json looks like:

[[{"y": "0.898287995669", "x": "0.824201870934", "v": "0"}, {"y": "0.738482578278", "x": "0.645352934631", "v": "1"}, {"y": "0.12255740116", "x": "0.113578656145", "v": "2"}, {"y": "0.603047665154", "x": "0.609252770235", "v": "3"}, {"y": "0.497780597993", "x": "0.490370637301", "v": "4"}, {"y": "0.450309984776", "x": "0.428403273731", "v": "5"}, {"y": "0.512235180495", "x": "0.552584811488", "v": "6"}, {"y": "0.808721117001", "x": "0.829379563316", "v": "7"}, {"y": "0.771161177414", "x": "0.378854605349", "v": "8"}, {"y": "0.48703522369", "x": "0.162086851046", "v": "9"}], [{"y": "0.848393476015", "x": "0.0502693724025", "v": "0"}, {"y": "0.220251557696", "x": "0.653522019136", "v": "1"}, {"y": "0.0885518976544", "x": "0.17830262921", "v": "2"}, {"y": "0.179368661434", "x": "0.597837803239", "v": "3"}, {"y": "0.0333228956342", "x": "0.985417214534", "v": "4"}, {"y": "0.502384226146", "x": "0.668414759643", "v": "5"}, {"y": "0.242769234767", "x": "0.827774288391", "v": "6"}, {"y": "0.227109062114", "x": "0.352454692633", "v": "7"}, {"y": "0.830790971204", "x": "0.57313727618", "v": "8"}, {"y": "0.854927945281", "x": "0.0879122992914", "v": "9"}], ... ]

and here's what my current code is:

var width = 960,
    height = 500;
var padding = 10;

// inverted because of svg inverted y axis
var yscale = d3.scale.linear().domain([1,0]).range([padding, height - padding]);
var xscale = d3.scale.linear().domain([0,1]).range([padding, width - padding]);


var color = d3.scale.category20();

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

d3.json("out.json", function(error, json) {
  if (error) return console.warn(error);
  var graphFrames = json;
  startDrawing(graphFrames);
});


function drawFrame(graphFrame) {
    // DATA JOIN
    // Join new data with old elements, if any.
    var nodes = svg.selectAll("node")
        .data(graphFrame, function(d){ return d.v; });


    // UPDATE
    // Update old elements as needed.
    nodes.transition()
        .duration(750)
        .attr("cx", function(d) { return xscale(d.x); })
        .attr("cy", function(d) { return yscale(d.y); });


    // ENTER
    // Create new elements as needed.
    nodes.enter().append("circle")
        .attr("class", "node")
        .attr("r", 5)
        .attr("cx", function(d) { return xscale(d.x); })
        .attr("cy", function(d) { return yscale(d.y); })
        .style("fill", function(d, i){ return color(d.v % 20); });

    // EXIT
    // Remove old elements as needed.
    //nodes.exit().remove();
}


var nextFrameIdx = 1;
function startDrawing(graphFrames){
    // The initial display.
    drawFrame(graphFrames[0]);

    // Draw the next set of node coordinates at each interval
    setInterval(function() {
        drawFrame(graphFrames[nextFrameIdx]);
        nextFrameIdx = (nextFrameIdx + 1) % graphFrames.length;
    }, 1500);

}
Это было полезно?

Решение

You were very close. Just make sure you select the elements you have created (.attr("class", "node");). So:

var nodes = svg.selectAll(".node")
    .data(graphFrame);

You were selecting on "node" which does not exist and so the enter selection was always set. And it is fine to just let the data bind be based on the index, the default, since you are basically moving the same items (=same index) in the selection.

Here is a complete working PLUNK.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top