Skip to content

Instantly share code, notes, and snippets.

Last active Jan 16, 2018
What would you like to do?
Dynamic Globe Rotation with Inertia
license: gpl-3.0

A remake of a previous block, Dynamic Globe Rotation, but with Fil's d3-inertia module.


  1. Currently, you must wait for the globe to come to a complete stop before you can adjust any of the range sliders. It would be cool if you could adjust one of the angles even as the other two continue on their paths.

    UPDATE Fixed, thanks to Fil! This fix doesn't let you stop only one angle of the inertial movement while the other two continue, but it's an improvement.

    if (inertia.timer) inertia.timer.stop();
  2. Currently, the range sliders just behave like normal range sliders – when you release the slider, it stops moving, no matter how fast you slide it. It would be cool to somehow add inertia to the behavior of the range sliders.

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
body {
margin: 0;
#rotation {
position: absolute;
font-family: monospace;
padding: 10px;
background: rgba(255, 255, 255, .5);
#rotation input {
width: 300px;
.graticule {
fill: none;
stroke: #ccc;
.country {
stroke: #fff;
line {
stroke: tomato;
stroke-width: 3px;
<div id="rotation"></div>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src=""></script>
var angles = ["λ", "φ", "γ"];
angles.forEach(function(angle, index){"#rotation").append("div")
.attr("class", "angle-label angle-label-" + index)
.html(angle + ": <span>0</span>")"#rotation").append("input")
.attr("type", "range")
.attr("class", "angle angle-" + index)
.attr("min", "-180")
.attr("max", "180")
.attr("value", "0");
var width = window.innerWidth, height = window.innerHeight;
var svg ="body").append("svg")
.attr("width", width)
.attr("height", height);
var projection = d3.geoOrthographic()
.scale(d3.min([width / 2, height / 2]))
.translate([width / 2, height / 2])
var path = d3.geoPath()
// append the graticule
.datum(d3.geoGraticule().step([10, 10]))
.attr("class", "graticule")
.attr("d", path);
var line = svg.append("line"),
// inertia versor dragging
var inertia = d3.geoInertiaDrag(svg, function() { render(); }, projection);
function render(){
if (inertia.t){
// make the line
pts = [
[inertia.position[0] + inertia.velocity[0] / 10, inertia.position[1] + inertia.velocity[1] / 10],
[inertia.position[0] + inertia.velocity[0] * inertia.t / 10, inertia.position[1] + inertia.velocity[1] * inertia.t / 10]
.attr("x1", pts[0][0])
.attr("y1", pts[0][1])
.attr("x2", pts[1][0])
.attr("y2", pts[1][1]);
d3.selectAll("input").on("input", function(){
var p = [];
d3.selectAll("input").each(function(d, i){
if (inertia.timer) {
// this fixes a strange behavior, where when you click on the svg while the globe is moving,
// the movement stops but the line remains
svg.on("click", function(){
function update(eulerAngles){
angles.forEach(function(angle, index){".angle-label-" + index + " span").html(Math.round(eulerAngles[index]))".angle-" + index).property("value", eulerAngles[index])
svg.selectAll("path").attr("d", path);
function removeLine(){
line.attr("x1", 0).attr("y1", 0).attr("x2", 0).attr("y2", 0);
d3.json("countries.json", function(error, countries){
if (error) throw error;
.data(topojson.feature(countries, countries.objects.polygons).features)
.attr("class", "country")
.attr("d", path);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment