|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
body { |
|
background: #fcfcfa; |
|
} |
|
|
|
canvas { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
cursor: move; |
|
cursor: drag; |
|
} |
|
|
|
.blur { |
|
-webkit-filter: blur(6px); |
|
filter: blur(6px); |
|
} |
|
|
|
</style> |
|
<body> |
|
<script src="https://unpkg.com/d3"></script> |
|
<script src="https://unpkg.com/topojson"></script> |
|
<script src="https://unpkg.com/versor"></script> |
|
<script src="https://unpkg.com/d3-inertia"></script> |
|
<script> |
|
|
|
var width = 960, |
|
height = 960, |
|
speed = -1e-2, |
|
start = Date.now(); |
|
|
|
var sphere = {type: "Sphere"}; |
|
|
|
var projection = d3.geoOrthographic() |
|
.translate([width / 2, height / 2]) |
|
.precision(.5) |
|
.rotate([-70, -25]); |
|
|
|
var projection2 = d3.geoOrthographic() |
|
.translate([width / 2, height / 2]) |
|
.precision(.5) |
|
.rotate([-70, -25]) |
|
|
|
var graticule = d3.geoGraticule(); |
|
|
|
var canvas1 = d3.select("body").append("canvas"), |
|
canvas2 = d3.select("body").append("canvas").attr("class", "blur"), |
|
canvas3 = d3.select("body").append("canvas"); |
|
|
|
d3.selectAll("canvas") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
var context1 = canvas1.node().getContext("2d"), |
|
context2 = canvas2.node().getContext("2d"), |
|
context3 = canvas3.node().getContext("2d"); |
|
|
|
var path = d3.geoPath() |
|
.projection(projection); |
|
var path2 = d3.geoPath() |
|
.projection(projection2); |
|
|
|
projection.scale(width / 2.3).clipAngle(90); |
|
projection2.scale(width / 2.3).clipAngle(90); |
|
|
|
context1.beginPath(); |
|
path.context(context1)(sphere); |
|
context1.lineWidth = 3; |
|
context1.strokeStyle = "#000"; |
|
context1.stroke(); |
|
|
|
context1.beginPath(); |
|
path(sphere); |
|
context1.fillStyle = "#fff"; |
|
context1.fill(); |
|
|
|
context2.fillStyle = "rgba(0,0,0,.4)"; |
|
context3.strokeStyle = "rgba(0,0,0,.2)"; |
|
|
|
var render = function(){}; |
|
|
|
function draw() { |
|
render(); |
|
} |
|
|
|
d3.json("https://unpkg.com/world-atlas/world/110m.json", function(error, topo) { |
|
if (error) throw error; |
|
|
|
var land = topojson.feature(topo, topo.objects.land), |
|
grid = graticule(); |
|
|
|
render = function() { |
|
context2.clearRect(0, 0, width, height); |
|
context3.clearRect(0, 0, width, height); |
|
|
|
var rotate = projection.rotate() |
|
//console.log(rotate) |
|
projection2.rotate([rotate[0] *1.1, rotate[1] * 1.1, rotate[2]] * 1.1); |
|
|
|
projection.scale(width / 2.3).clipAngle(90); |
|
projection2.scale(width / 2.3).clipAngle(90); |
|
|
|
// shadow |
|
context2.beginPath(); |
|
path.context(context2)(land); |
|
context2.fill(); |
|
|
|
//grid |
|
context3.beginPath(); |
|
path.context(context3)(grid); |
|
context3.lineWidth = .5; |
|
context3.stroke(); |
|
|
|
projection.scale(width / 2.2).clipAngle(106.3); |
|
projection2.scale(width / 2.2).clipAngle(106.3); |
|
|
|
context3.beginPath(); |
|
// underside |
|
path(land); |
|
context3.fillStyle = "#737368"; |
|
context3.fill(); |
|
|
|
projection.scale(width / 2.2).clipAngle(90); |
|
projection2.scale(width / 2.2).clipAngle(90); |
|
|
|
context3.beginPath(); |
|
//topside |
|
path(land); |
|
context3.fillStyle = "#dadac4"; |
|
context3.fill(); |
|
}; |
|
|
|
render(); |
|
}); |
|
|
|
var inertia = d3.geoInertiaDrag(canvas3, draw, projection); |
|
|
|
d3.timer(function(e){ |
|
if (inertia.timer) return; |
|
var rotate = projection.rotate(); |
|
projection.rotate([rotate[0] + 0.1, rotate[1], rotate[2]]); |
|
//projection2.rotate([rotate[0] + 0.1, rotate[1], rotate[2]]); |
|
draw(); |
|
}) |
|
|
|
d3.select(self.frameElement).style("height", height + "px"); |
|
|
|
</script> |