Last active
October 17, 2017 14:48
-
-
Save Andrew-Reid/d95e59b71544706515632c4b7fb0402a to your computer and use it in GitHub Desktop.
Transition Rotation & Scale of Orthographic Projection with Easing
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> | |
<meta charset="utf-8"> | |
<style> | |
.background { | |
fill: none; | |
pointer-events: all; | |
stroke:grey; | |
} | |
.feature, { | |
fill: #ccc; | |
cursor: pointer; | |
} | |
.feature.active { | |
fill: orange; | |
} | |
.mesh,.land { | |
fill: black; | |
stroke: #ddd; | |
stroke-linecap: round; | |
stroke-linejoin: round; | |
} | |
.water { | |
fill: #00248F; | |
} | |
</style> | |
<body> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script src="//d3js.org/topojson.v1.min.js"></script> | |
<script src="//d3js.org/queue.v1.min.js"></script> | |
<script> | |
var width = 960, | |
height = 600, | |
active = d3.select(null); | |
var projection = d3.geo.orthographic() | |
.scale(250) | |
.translate([width / 2, height / 2]) | |
.clipAngle(90); | |
var path = d3.geo.path() | |
.projection(projection); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
svg.append("rect") | |
.attr("class", "background") | |
.attr("width", width) | |
.attr("height", height) | |
.on("click", reset); | |
var g = svg.append("g") | |
.style("stroke-width", "1.5px"); | |
var countries; | |
var countryIDs; | |
queue() | |
.defer(d3.json, "world.json") | |
.await(ready) | |
function ready(error, world) { | |
if (error) throw error; | |
countries = topojson.feature(world, world.objects.countries).features; | |
//Adding water | |
g.append("path") | |
.datum({type: "Sphere"}) | |
.attr("class", "water") | |
.attr("d", path); | |
var world = g.selectAll("path.land") | |
.data(countries) | |
.enter().append("path") | |
.attr("class", "land") | |
.attr("d", path) | |
.on("click", clicked) | |
}; | |
function clicked(d) { | |
if (active.node() === this) return reset(); | |
active.classed("active", false); | |
active = d3.select(this).classed("active", true); | |
// Clicked on feature: | |
var p = d3.geo.centroid(d); | |
// Store the current rotation and scale: | |
var currentRotate = projection.rotate(); | |
var currentScale = projection.scale(); | |
// Calculate the future bounding box after applying a rotation: | |
projection.rotate([-p[0], -p[1]]); | |
path.projection(projection); | |
// calculate the scale and translate required: | |
var b = path.bounds(d); | |
var nextScale = currentScale * 1 / Math.max((b[1][0] - b[0][0]) / (width/2), (b[1][1] - b[0][1]) / (height/2)); | |
var nextRotate = projection.rotate(); | |
// Update the map: | |
d3.selectAll("path") | |
.transition() | |
.attrTween("d", function(d) { | |
var r = d3.interpolate(currentRotate, nextRotate); | |
var s = d3.interpolate(currentScale, nextScale); | |
return function(t) { | |
projection | |
.rotate(r(Math.pow(t,0.33))) | |
.scale( currentScale > nextScale ? s(Math.pow(t,0.1)) : s(Math.pow(t,3)) ); | |
path.projection(projection); | |
return path(d); | |
} | |
}) | |
.duration(1000); | |
} | |
function reset() { | |
active.classed("active", false); | |
active = d3.select(null); | |
d3.selectAll("path") | |
.transition() | |
.attrTween("d", function(d) { | |
var s = d3.interpolate(projection.scale(), 250); | |
return function(t) { | |
projection | |
.scale(s(t)); | |
path.projection(projection); | |
return path(d); | |
} | |
}) | |
.duration(1000); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment