问题
I am trying to replicate the zoom functionality shown in Jason Davies "Rotate the World" visualization (https://www.jasondavies.com/maps/rotate/)
I am able to rotate and zoom, however, if I zoom after rotating, my projection is zoomed at an angle (meaning, if I turn the globe 15 degrees to the left and then zoom, the globe no longer stays centered in the canvas). Below is my code, any help would be greatly appreciated!
d3.select(window)
.on("mousemove", mousemove)
.on("mouseup", mouseup);
var width = 960,
height = 500;
var proj = d3.geo.orthographic()
.scale(220)
.translate([width / 2, height / 2])
.clipAngle(90);
var path = d3.geo.path().projection(proj).pointRadius(1.5);
var graticule = d3.geo.graticule();
var svg = d3.select("#content").append("svg")
.attr("width", width)
.attr("height", height)
.on("mousedown", mousedown);
var zoom = d3.behavior.zoom()
.center([width / 2, height / 2])
//.scaleExtent([.5, 10])
.on("zoom", zoomed);
svg.call(zoom);
queue()
.defer(d3.json, "/static/json/world.json")
.await(ready);
function ready(error, world) {
/*
Define gradients
*/
// ocean
var ocean_fill = svg.append("defs").append("radialGradient")
.attr("id", "ocean_fill")
.attr("cx", "75%")
.attr("cy", "25%");
ocean_fill.append("stop").attr("offset", "5%").attr("stop-color", "#777");
ocean_fill.append("stop").attr("offset", "100%").attr("stop-color", "#555");
// globe highlight
var globe_highlight = svg.append("defs").append("radialGradient")
.attr("id", "globe_highlight")
.attr("cx", "75%")
.attr("cy", "25%");
globe_highlight.append("stop")
.attr("offset", "5%").attr("stop-color", "#bbb")
.attr("stop-opacity","0.6");
globe_highlight.append("stop")
.attr("offset", "100%").attr("stop-color", "#999")
.attr("stop-opacity","0.2");
// globe shadow
var globe_shading = svg.append("defs").append("radialGradient")
.attr("id", "globe_shading")
.attr("cx", "50%")
.attr("cy", "40%");
globe_shading.append("stop")
.attr("offset","50%").attr("stop-color", "#333")
.attr("stop-opacity","0");
globe_shading.append("stop")
.attr("offset","100%").attr("stop-color", "#111")
.attr("stop-opacity","0.3");
// drop shadow
var drop_shadow = svg.append("defs").append("radialGradient")
.attr("id", "drop_shadow")
.attr("cx", "50%")
.attr("cy", "50%");
drop_shadow.append("stop")
.attr("offset","20%").attr("stop-color", "#000")
.attr("stop-opacity",".5");
drop_shadow.append("stop")
.attr("offset","100%").attr("stop-color", "#000")
.attr("stop-opacity","0");
/*
Draw globe objects
*/
// drop shadow
svg.append("ellipse")
.attr("cx", 440).attr("cy", 450)
.attr("rx", proj.scale()*.90)
.attr("ry", proj.scale()*.25)
.attr("class", "noclicks")
.style("fill", "url(#drop_shadow)");
// globe
svg.append("circle")
.attr("cx", width / 2).attr("cy", height / 2)
.attr("r", proj.scale())
.attr("class", "noclicks")
.style("fill", "url(#ocean_fill)");
// land
svg.append("path")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
svg.append("path")
.datum(graticule)
.attr("class", "graticule noclicks")
.attr("d", path);
svg.append("circle")
.attr("cx", width / 2).attr("cy", height / 2)
.attr("r", proj.scale())
.attr("class","noclicks")
.style("fill", "url(#globe_highlight)");
svg.append("circle")
.attr("cx", width / 2).attr("cy", height / 2)
.attr("r", proj.scale())
.attr("class","noclicks")
.style("fill", "url(#globe_shading)");
/* svg.append("g").attr("class","points")
.selectAll("text").data(places.features)
.enter().append("path")
.attr("class", "point")
.attr("d", path);
svg.append("g").attr("class","labels")
.selectAll("text").data(places.features)
.enter().append("text")
.attr("class", "label")
.text(function(d) { return d.properties.name })*/
svg.append("g").attr("class","countries")
.selectAll("path")
.data(topojson.feature(world2, world2.objects.countries).features)
.enter().append("path")
.attr("d", path);
}
// modified from http://bl.ocks.org/1392560
var m0, o0;
function mousedown() {
m0 = [d3.event.pageX, d3.event.pageY];
o0 = proj.rotate();
d3.event.preventDefault();
}
function mousemove() {
if (m0) {
var m1 = [d3.event.pageX, d3.event.pageY]
, o1 = [o0[0] + (m1[0] - m0[0]) / 6, o0[1] + (m0[1] - m1[1]) / 6];
o1[1] = o1[1] > 30 ? 30 :
o1[1] < -30 ? -30 :
o1[1];
proj.rotate(o1);
refresh();
}
}
function mouseup() {
if (m0) {
mousemove();
m0 = null;
}
}
function refresh() {
svg.selectAll(".land").attr("d", path);
svg.selectAll(".countries path").attr("d", path);
svg.selectAll(".graticule").attr("d", path);
svg.selectAll(".point").attr("d", path);
//position_labels();
}
var slast = 1;
function zoomed() {
if (slast != d3.event.scale) {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
slast = d3.event.scale;
};
}
来源:https://stackoverflow.com/questions/24497194/how-to-keep-d3-orthographic-projection-centered-when-zooming