Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3.js v4.0.0-alpha.35 Transitions not working

Tags:

d3.js

I try to mimic this collapsible tree using the newest version of d3.js, however I am running into some issues.

The nodeEnter part of the code is executing just fine but for whatever reason the nodeUpdate aka the transition part of the code will not execute. I look in the console to make sure it wasn't just a visibility issue, I get no errors, nothing... The coordinates stay the same as they were when they were initiated by the nodeEnter portion of the code. My only guess is that something changed with the new version of d3.js and I am missing something...

var t = d3.transition()
.duration(750)
.ease(d3.easeLinear);

var tree = d3.tree()
  .size([height, width]);

function updateDisplay(source){
  
  var treeData = tree(root).descendants(),
      linkData = treeData.slice(1); 
  
  treeData.forEach(function(d) {
    
    /*Normalize for fixed-depth between levels, so that y value 
    **  stays the same through transition as opposed to being 
    **  an even division of the svg width. */
    d.y = d.depth *180;
    
  });
  
                            
  var node = svg.selectAll("g.node")
                .data(treeData);

  var nodeEnter = node.enter()
                      .append("g")
                        .attr("class", function(d) {return "node" + (d.children ? " node--internal" : " node--leaf");})
                        .attr("transform", function(d) {return "translate(" + source.y0 + "," + source.x0 + ")";});
  
  console.log(nodeEnter);
  
  nodeEnter.append("rect")
            .attr("x", 3)
            .attr("y", -10)
            .attr("rx", 1e-6)//goes to 8 after transition
            .attr("ry", 1e-6)//goes to 8 after transition
            .attr("width", 1e-6)
            .attr("height", 1e-6)
            .attr("transform", function(d) {return d.children ? "scale(-1,1) translate(-20,0)" : "";})
            .attr("style", "fill:#EEEEEE;filter:url(#dropshadow)");

  nodeEnter.append("text")
            .attr("dy", 3)
            .attr("x", function(d) {return d.children ? -8 : 30;})
            .style("text-anchor", function(d) {return d.children ? "end" : "start";})
            .text(function(d) {return d.id;})
            .each(function(d) {d.NameWidth = this.getBBox().width;})
            .style("fill-opacity", 1e-6);
  

  var avatar = nodeEnter.append("g").attr("style", "filter:url(#dropshadow)");

  avatar.append("clipPath")
          .attr("id", "avatarClip")
          .append("circle")
            .attr("cx", 1e-6)//12.5
            .attr("cy", 1e-6)//12.5
            .attr("r", 1e-6);//12.5

  avatar.append("circle")
          .attr("cx", 1e-6)//12.5
          .attr("cy", 1e-6)//12.5
          .attr("r", 1e-6)//12.5
          .attr("style", "fill:white")
          .attr("transform", "translate(0,-12)");

  avatar.append("image")
          .attr("xlink:href", function(d) {return (d.data.img ? d.data.img : "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/ic_person_black_48px.svg");})
          .attr("clip-path", "url(#avatarClip)")
          .attr("class", function(d) {return (d.children ? "avatar--manager" : "");})
          .attr("width", 25)
          .attr("height", 25)
          .attr("transform", "translate(0,-12)");  
  
  avatar.on("click", function(d) {toggle(d);});
  
  //TRANSITION OF NODES TO NEW POSITIONS
  var nodeUpdate = node.transition(t)
                        .attr("class", function(d) {return "node" + (d.children ? " node--internal" : " node--leaf");})
                        .attr("transform", function(d) {return "translate(" + d.y + "," + d.x + ")";});
  
  nodeUpdate.select("text")
    .attr("x", function(d) {return d.children ? -8 : 30;})
    .style("text-anchor", function(d) {return d.children ? "end" : "start";})
    .text(function(d) {return d.id;})
    .style("fill-opacity", 1);

  nodeUpdate.select("rect")
              .attr("transform", function(d) {return d.children ? "scale(-1,1) translate(-20,0)" : "";})
              .attr("height", 20)
              .attr("width", function(d) {return (d.NameWidth + 35);});
  
  nodeUpdate.select("clipPath")
              .attr("cx", 12.5)
              .attr("cy", 12.5)
              .attr("r", 12.5);
  
  nodeUpdate.select("circle")
             .attr("cx", 12.5)
             .attr("cy", 12.5)
             .attr("r", 12.5);

  nodeUpdate.select("image")
    .attr("xlink:href", function(d) {return (d.data.img ? d.data.img : "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/ic_person_black_48px.svg");})
    .attr("clip-path", "url(#avatarClip)")
    .attr("class", function(d) {return (d.children ? "avatar--manager" : "");});
  
  //TRANSITIONING EXITING NODES
  var nodeExit = node.exit()
                     .transition(t)
                     .attr("transform", function(d) {return "translate(" + source.y + "," + source.x + ")";})
                     .remove();
  
  
 
  /*var link = svg.selectAll(".link")
                 .data(linkData);*/
  

  // Stash the old positions for transition.
   root.each(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
  
}

PS: My code isn't an exact replica of the link above, I put my own spin when it came to design...

like image 581
Tekill Avatar asked Oct 25 '25 01:10

Tekill


1 Answers

There has been a conceptual change of the enter and update selections from v3 to v4. Whereas in v3 the enter selection was automatically merged into the update selection you have to explicitely call selection.merge() from v4 upwards to get the same result.

The documentation for selection.enter() of v3 tells us:

The enter selection merges into the update selection when you append or insert.

The documentation of the same method of v4, on the other hand, reads:

The enter selection is typically only used transiently to append elements, and is often merged with the update selection after appending, such that modifications can be applied to both entering and updating elements.

Have a look at this simplified example using v3, which should come with no surprises:

var update = d3.select("svg").selectAll("circle")
  .data([1]);
  
var enter = update.enter()
  .append("circle")
    .attr("cx", "50")
    .attr("cy", "50")
    .attr("r", "20")
    .attr("fill", "red");
    
 update
   .transition().duration(2000)
     .attr("fill", "blue");
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg></svg>

Doing the same with v4 requires a little modification, though:

var update = d3.select("svg").selectAll("circle")
  .data([1]);
  
var enter = update.enter()
  .append("circle")
    .attr("cx", "50")
    .attr("cy", "50")
    .attr("r", "20")
    .attr("fill", "red");
    
 update
   .merge(enter)  // This merges both the enter and the update selection
   .transition().duration(2000)
     .attr("fill", "blue");
<script src="https://d3js.org/d3.v4.min.js"></script>

<svg></svg>

Commenting out the .merge() line shows the effect you described, because the update selection will be empty, even though you entered new elements using the enter selection before.

like image 121
altocumulus Avatar answered Oct 28 '25 03:10

altocumulus



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!