在地球上放大 D3 v4

Zooming in D3 v4 on a globe

我正在寻找一种在 D3 v4 中从一个地方缩放到另一个地方的方法(v4 很重要)。我要找的基本上就是这个:https://www.jasondavies.com/maps/zoom/ My problem is that Jason Davies obfuscated his code, so I can't read it, and I can't find a bl.ock containing that project or anything similar to it. I'll provide a link to what I've got here: http://plnkr.co/edit/0mjyR3ovTfkDXB8FTG0j?p=preview 相关的大概在.tween():

里面
.tween("rotate", function () {
                var p = d3.geoCentroid(points[i]),
                    r = d3.geoInterpolate(projection.rotate(), [-p[0], -p[1]]);

                return function (t) {
                    projection.rotate(r(t));
                    convertedLongLats = [projection(points[0].coordinates), projection(points[1].coordinates)]
                    c.clearRect(0, 0, width, height);
                    c.fillStyle = colorGlobe, c.beginPath(), path(sphere), c.fill();
                    c.fillStyle = colorLand, c.beginPath(), path(land), c.fill();
                    for (var j = 0; j < points.length; j++) {
                        var textCoords = projection(points[j].coordinates);
                        c.fillStyle = textColors, c.textAlign = "center", c.font = "18px FontAwesome", c.fillText(points[j].icon, textCoords[0], textCoords[1]);
                        textCoords[0] += 15;
                        c.textAlign = "left", c.font = " 12px Roboto", c.fillText(points[j].location, textCoords[0], textCoords[1]);
                    }
                    c.strokeStyle = textColors, c.lineWidth = 4, c.setLineDash([10, 10]), c.beginPath(), c.moveTo(convertedLongLats[0][0], convertedLongLats[0][1]), c.lineTo(convertedLongLats[1][0], convertedLongLats[1][1]), c.stroke();
                };
            })

基本上,我想要我现在得到的东西,但我希望它放大,就像我在上面提供的动画世界缩放示例中一样。我并不是真的在寻找代码,我宁愿有人通过示例或其他东西为我指明正确的方向(值得注意的是我对 d3 相当陌生并且这个项目主要基于 mbostock 的 World Tour,所以它使用 canvas)。提前谢谢大家!

根据您的 plunker 和评论,在过渡中缩小两点之间的一个挑战是插值器只会在两个值之间进行插值。 plunker 中的解决方案依赖于两个插值器,一个用于放大和缩小。正如您所注意到的,这种方法增加了不必要的复杂性和沿线的某个地方,它跳到了一个不正确的比例。你可以简化这个:

采用在-1和1之间进行插值的插值器,并根据插值器的绝对值对每个尺度进行加权。在零时,缩放应该一直缩小,而在 -1,1 时,你应该放大:

var s = d3.interpolate(-1,1);

// get the appropriate scale:
scale = Math.abs(0-s(t))*startEndScale + (1-Mat.abs(0-s(t)))*middleScale

这有点笨拙,因为它从缩小到放大相当突然,因此您可以使用正弦类型缓动来缓解它:

var s = d3.interpolate(0.0000001,Math.PI);

// get the appropriate scale:    
scale = (1-Math.abs(Math.sin(s(t))))*startEndScale + Math.abs(Math.sin(s(t)))*middleScale

我已将此应用于您的 plunker here

对于使用我建议的示例和你的两个点和路径(并使用你的 plunkr 作为基础)的简单和最小的示例,剥离动画线和图标,我可能会把类似的东西放在一起(plunker,下面的片段最好全屏观看):

var width = 600,
    height = 600;

var points = [{
    type: "Point",
    coordinates: [-74.2582011, 40.7058316],
    location: "Your Location",
    icon: "\uF015"
}, {
    type: "Point",
    coordinates: [34.8887969, 32.4406351],
    location: "Caribe Royale Orlando",
    icon: "\uF236"
}];

var canvas = d3.select("body").append("canvas")
    .attr("width", width)
    .attr("height", height);

var c = canvas.node().getContext("2d");

var point = points[0].coordinates;

var projection = d3.geoOrthographic()
    .translate([width / 2, height / 2])
    .scale(width / 2)
    .clipAngle(90)
    .precision(0.6)
    .rotate([-point[0], -point[1]]);

var path = d3.geoPath()
    .projection(projection)
    .context(c);

var colorLand = "#4d4f51",
    colorGlobe = "#2e3133",
    textColors = "#fff";

d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function (error, world) {
    if (error) throw error;

    var sphere = { type: "Sphere" };
    var land = topojson.feature(world, world.objects.land);
    var i = 0;
    
    var scaleMiddle = width/2;
    var scaleStartEnd = width * 2;
    
    loop();
    
    function loop() {
      d3.transition()
        .tween("rotate",function() {
            var flightPath = {
                type: 'Feature',
                geometry: {
                    type: "LineString",
                    coordinates: [points[i++%2].coordinates, points[i%2].coordinates]
                }
            };

          // next point:
          var p = points[i%2].coordinates;
          // current rotation:
          var currentRotation = projection.rotate();  
          // next rotation:
          var nextRotation = projection.rotate([-p[0],-p[1]]).rotate();
          
          // Interpolaters:
          var r = d3.geoInterpolate(currentRotation,nextRotation);
          var s = d3.interpolate(0.0000001,Math.PI);
          
          return function(t) {
            // apply interpolated values
            projection.rotate(r(t))
              .scale(  (1-Math.abs(Math.sin(s(t))))*scaleStartEnd + Math.abs(Math.sin(s(t)))*scaleMiddle  ) ;          

            c.clearRect(0, 0, width, height);
            c.fillStyle = colorGlobe, c.beginPath(), path(sphere), c.fill();
            c.fillStyle = colorLand, c.beginPath(), path(land), c.fill();
            c.beginPath(), path(flightPath), c.globalAlpha = 0.5, c.shadowColor = "#fff", c.shadowBlur = 5, c.lineWidth = 0.5, c.strokeStyle = "#fff", c.stroke(), c.shadowBlur = 0, c.globalAlpha = 1;

          }
        })
        .duration(3000)
        .on("end", function() {  loop();  })
      
      
    }
    
});
<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>