block by StewartNoyce 9457213

Box Transition Lesson

Full Screen

This example shows the use of the d3 transition library, particularly with regards to timing. Four boxes are displayed, each containing a circle of a different color. One of them is open and the others are closed. Click on a box to open it.

Three separate transitions are initiated on three different element selections. Yet, the box outline and circle opacity transitions start after the box position transitions.

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.min.js"></script>
<style>

body {
  font: 10px sans-serif;
}

path {
  fill: #fff;
  stroke: #555;
}

text {
    font-size: 14px;
}

</style>
</head>
<body>
<script type="text/javascript">

var width = 960,
    height = 500;
    
var boxes = [],
    boxwidth = 40,
    openboxwidth = 180,
    boxheight = 180,
    margin = 40,
    openbox = 0;
    
color = [ "#d7191c", "#e66101", "#1a9641", "#2b83ba" ];

// place boxes at the top left margin
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("id", "boxes")
    .attr("transform", "translate(40, 40)");
     
// compute the initial layout using global coordinates, and an open box index
var boxes = boxLayout(openbox);
    
// make the boxes
d3.select("#boxes").selectAll("g").data(boxes).enter().append("g");
          
// translate the box to a starting point in the layout
var boxEnter = d3.selectAll("#boxes > g")
    .attr("class", function(d) { return d.status; })
    .attr("transform", function(d) { return "translate("+d.t+",0)";  })
    .on('click', function(d) { togglebox(); update(); });
 
// make the outline of the box as a path
boxEnter.append('g')
    .attr("class", "outline")
    .append("path")
  .transition()
    .duration(500)
    .attr('d', function(d) {
        return 'M '+d.x0+' '+d.y0+' L '+d.x1+' '+d.y1+' L '+d.x2+' '+d.y2+' L '+d.x3+' '+d.y3+' z';
    });

// make a drawing inside the box        
boxEnter.append("g")
    .attr("class", "drawing")
    .style("opacity", function(d) { return d.opacity; })
  .append("circle")
    .attr("fill", function(d) { return color[d.id]; })
    .attr("r", boxwidth)
    .attr("cx", openboxwidth/2)
    .attr("cy", boxheight/2); 

// tell visitors what to do with the boxes
d3.select("body > svg").append("text")
    .attr("text-anchor", "left") 
    .attr("x", margin)
    .attr("y", (2*margin + boxheight))
    .text("Click on a box to open it.");
    
// create a new set of box transform and path extremity points         
function boxLayout(index) {

    var b,
        bl = [],
        bx = 0;
        
    for (var i=0; i<4; i++) {
        b = {};
        b.id = i;
        b.t = bx;
        b.x0 = b.x3 = 0;
        b.x1 = b.x2 = (i === index) ? openboxwidth : boxwidth;
        b.y0 = b.y1 = 0;
        b.y2 = b.y3 = boxheight;
        b.opacity = (i === index) ? 1 : 0;
        b.status = (i === index) ? 'open' : 'closed';
        bl.push(b);
        bx += (i === index) ? openboxwidth : boxwidth;
    }
    
    return bl;
}
    
function update() {
     
    // set the openbox index
    d3.selectAll("#boxes").select(".open").each(function(d) { openbox = d.id; });

    // make the layout with the new openbox
    var boxes = boxLayout(openbox);
            
    // move the boxes into place
    var boxUpdate = d3.selectAll("#boxes > g")
        .data(boxes, function(d) { return d.id; })
      .transition()
        .duration(500)
        .attr("transform", function(d) { return "translate("+d.t+",0)";  });

    // update the box frames
    boxUpdate.select("g.outline > path")
      .transition()
        .duration(500)
        .attr('d', function(d) {
            return 'M '+d.x0+' '+d.y0+' L '+d.x1+' '+d.y1+' L '+d.x2+' '+d.y2+' L '+d.x3+' '+d.y3+' z';
        });
           
    // update the drawing opacity (exposes the new openbox and hides the old)
    boxUpdate.select("g.drawing")
        .style("opacity", 0)
      .transition()
        .duration(1000)
        .style("opacity", function(d) { return d.opacity; });  
}
 
function togglebox() {  

    // close the open box
    d3.select("#boxes").select(".open").attr("class", "closed");
    
    // open the selected box
    d3.select(d3.event.currentTarget).attr("class","open");
}

</script>
</body>
</html>