Takes mbostock’s shape tweening example as a starting point, then devolves into ridiculousness. Try mousing over paths, and also clicking! Never use this for a real mapping project, except perhaps as inspiration for creating a legitimate outlines-to-Dorling transition.
Last active
August 29, 2015 13:57
-
-
Save scotthmurray/9471486 to your computer and use it in GitHub Desktop.
The United States of Bouncy Balls
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>The United States of Bouncy Balls</title> | |
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script> | |
<style type="text/css"> | |
path { | |
stroke: white; | |
stroke-width: 0.5; | |
} | |
</style> | |
</head> | |
<body> | |
<script> | |
var width = 960; | |
var height = 500; | |
var dorlingView = false; | |
var projection = d3.geo.albersUsa() | |
.scale(1000); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.on("click", function() { | |
if (!dorlingView) { | |
//Make all into circles | |
d3.selectAll("path") | |
.classed("dropped", false) | |
.transition() | |
.duration(500) | |
.attr("transform", "translate(0,0)") | |
.attr("d", function(d) { | |
return d.endPath; | |
}); | |
} else { | |
//Restore all original paths | |
d3.selectAll("path") | |
.classed("dropped", false) | |
.transition() | |
.duration(500) | |
.attr("transform", "translate(0,0)") | |
.attr("d", function(d) { | |
return d.startPath; | |
}); | |
} | |
dorlingView = !dorlingView; | |
}); | |
//Load JSON data | |
d3.json("us-states.json", function(json) { | |
//We're going to extract some coordinates | |
//and calculate some new path values, | |
//to be stored here: | |
var derivedPathData = []; | |
//Function to calc start/end paths for each polygon | |
var calcCoords = function(polygon) { | |
var startCoords = polygon.map(projection); | |
var endCoords = circle(startCoords); | |
var start = "M" + startCoords.join("L") + "Z"; | |
var end = "M" + endCoords.join("L") + "Z"; | |
derivedPathData.push({ startPath: start, endPath: end }); | |
}; | |
//Loop through once for each feature | |
for (var i = 0; i < json.features.length; i++) { | |
//Extract the geometry from this feature | |
var geometry = json.features[i].geometry; | |
//If this is a normal polygon | |
if (geometry.type == "Polygon") { | |
//Just calc the coordinate for its single polygon | |
calcCoords(geometry.coordinates[0]); | |
} else if (geometry.type == "MultiPolygon") { | |
//Otheriwse, calc the coordinates for its multiple polygons | |
for (var j = 0; j < geometry.coordinates.length; j++) { | |
calcCoords(geometry.coordinates[j][0]); | |
} | |
} | |
} | |
//Use this derived data to make new path elements | |
var paths = d3.select("svg") | |
.selectAll("path") | |
.data(derivedPathData) | |
.enter() | |
.append("path") | |
.attr("transform", "translate(0,0)") | |
.style("fill", "#ccc") | |
.attr("d", function(d) { | |
return d.startPath; | |
}) | |
.on("mouseover", function(d) { | |
//If this path has already been 'dropped' | |
if (!d3.select(this).classed("dropped")) { | |
//Move to front and drop it down | |
d3.select(this) | |
.each(moveToFront) | |
.transition() | |
.duration(500) | |
.style("fill", "#f13") | |
.attr("d", function(d) { | |
return d.endPath; | |
}) | |
.each("end", function() { | |
var bbox = d3.select(this).node().getBBox(); | |
var newY = -bbox.y + (height - bbox.height); | |
d3.select(this) | |
.classed("dropped", true) | |
.transition() | |
.duration(750) | |
.ease("bounce") | |
.attr("transform", "translate(0," + newY + ")"); | |
}); | |
} else { | |
//Otherwise, restore its original position | |
d3.select(this) | |
.classed("dropped", false) | |
.transition() | |
.duration(500) | |
.style("fill", "#ccc") | |
.attr("transform", "translate(0,0)") | |
.attr("d", function(d) { | |
return d.startPath; | |
}); | |
} | |
}); | |
}); | |
//Function to take coordinates and calculate a circular | |
//path using the same number of points. (Thanks, @mbostock!) | |
//In this example, we use the circle as our 'endPath'. | |
var circle = function(coordinates) { | |
var circle = [], | |
length = 0, | |
lengths = [length], | |
polygon = d3.geom.polygon(coordinates), | |
p0 = coordinates[0], | |
p1, | |
x, | |
y, | |
i = 0, | |
n = coordinates.length; | |
// Compute the distances of each coordinate. | |
while (++i < n) { | |
p1 = coordinates[i]; | |
//console.log(p1); | |
x = p1[0] - p0[0]; | |
y = p1[1] - p0[1]; | |
lengths.push(length += Math.sqrt(x * x + y * y)); | |
p0 = p1; | |
} | |
var area = polygon.area(), | |
radius = Math.sqrt(Math.abs(area) / Math.PI), | |
centroid = polygon.centroid(-1 / (6 * area)), | |
angleOffset = -Math.PI / 2, // TODO compute automatically | |
angle, | |
i = -1, | |
k = 2 * Math.PI / lengths[lengths.length - 1]; | |
// Compute points along the circle’s circumference at equivalent distances. | |
while (++i < n) { | |
angle = angleOffset + lengths[i] * k; | |
circle.push([ | |
centroid[0] + radius * Math.cos(angle), | |
centroid[1] + radius * Math.sin(angle) | |
]); | |
} | |
return circle; | |
}; | |
//Move SVG elements to the end of their container, | |
//so they appear "on top". | |
var moveToFront = function() { | |
this.parentNode.appendChild(this); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment