Skip to content

Instantly share code, notes, and snippets.

@cherdarchuk
Forked from scotthmurray/README.md
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cherdarchuk/9614216 to your computer and use it in GitHub Desktop.
Save cherdarchuk/9614216 to your computer and use it in GitHub Desktop.

Click each shape above to move it to the front.

An ever so slight change to Scott Murray's implementation, to prevent shapes in the middle of the stack from fading back a little before fading to front.

SVG doesn’t include a concept of z-index, layering, or depth, except that elements that exist later in an SVG document are drawn “on top”. But when designing interactive pieces, we often want shapes that the user has selected / clicked / interacted with to be moved “on top,” to prevent occlusion and ensure visibility.

It’s easy to take an element and simply move it to the bottom of its parent container:

var moveToFront = function() { 
	this.parentNode.appendChild(this); 
}

…but this results in the element jumping into place abruptly. What if we want a smooth transition, like a fade over time?

The sneaky approach demonstrated here:

  1. duplicates the original element
  2. places the duplicate at the end of the parent container (“on top”)
  3. makes the duplicate completely transparent, then fades it in over time

fades out the original over the same period of time 5. deletes the original once it’s invisible

The visual effect is of an element gradually fading forward, into its new layer position, relative to other elements.

The node-cloning portion of this code is adapted from work by mbostock originally posted on the Google Group.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Fade to Front</title>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
svg * {
cursor: pointer;
}
circle {
fill: #E80D4B;
}
rect {
fill: #34AEFF;
}
ellipse {
fill: #00FF66;
}
</style>
</head>
<body>
<script>
var width = 960;
var height = 500;
var speed = 1000;
//Create the SVG
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//Make a circle
svg.append("circle")
.attr("cx", width * 0.3)
.attr("cy", height * 0.5)
.attr("r", height * 0.4)
.style("opacity", 1.0);
//Make a rectangle
svg.append("rect")
.attr("x", width * 0.3)
.attr("y", height * 0.2)
.attr("width", width * 0.3)
.attr("height", height * 0.6)
.style("opacity", 1.0);
//Make an ellipse
svg.append("ellipse")
.attr("cx", width * 0.7)
.attr("cy", height * 0.5)
.attr("rx", height * 0.55)
.attr("ry", height * 0.3)
.style("opacity", 1.0);
//Bind the same click behavior to all three
d3.selectAll("circle, rect, ellipse")
.on("click", function() {
//When clicked, call fadeToFront()
d3.select(this).each(fadeToFront);
});
//Moves SVG elements to the end of their container, so they appear "on top".
//Achieves a nice, smooth fade by duplicating the clicked element, moving the
//dupe to the front, then fading it in, while fading out the original element
//at the same time. Tricky!
var fadeToFront = function() {
//Select this element, that we want to move to front
var orig = d3.select(this);
var origNode = orig.node();
//Clone it, and append the copy on "top" (meaning, at the end of
//the parent element, which is <svg> in this case)
var dupe = d3.select(origNode.parentNode.appendChild(origNode.cloneNode(true), origNode.nextSibling));
//Make the new element transparent immediately, then fade it in over time
dupe.style("opacity", 0.0)
.transition()
.duration(speed)
.style("opacity", 1.0)
.each("end", function() {
//When the fade-in is complete, add the click event
d3.select(this).on("click", function() {
d3.select(this).each(fadeToFront);
});
});
//Fade the old one out at the same rate that the new one is faded in
orig.transition()
.duration(speed)
//not necessary
//.style("opacity", 0.0)
.each("end", function() {
//When the dupe transistion is complete, delete this element
d3.select(this).remove();
});
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment