D3 v4中的拖动旋转投影

sir*_*len 2 d3.js

我无法使正射投影通过旋转d3.drag()。它没有响应:

var projection = d3.geoOrthographic()
  .scale(100)
  .translate([500, 300])
  .rotate([55,-40])
  .center([0,0])
  .clipAngle(90);

var geoPath = d3.geoPath().projection(projection);

svg.call(d3.drag().on('drag', dragged));

function dragged() {
    var transform = d3.event.transform;
    var r = {x: ?(transform.x), y: ?(transform.y)};
    projection.rotate([origin.x + r.x * k, origin.y + r.y]);
    updatePaths(svg, graticule, geoPath);
};
Run Code Online (Sandbox Code Playgroud)

我尝试用dragged()a 替换,console.log()即使在这种情况下也没有任何反应(显然没有调用该函数)。我d3.zoom()以类似的方式实施,没有任何问题。我究竟做错了什么?

jsFiddle:https ://jsfiddle.net/sirallen/2L4ajskL/

Ger*_*ado 5

拖动没有响应的原因是您已经在该SVG上调用了缩放,并且缩放处理了平移。因此,d3.drag要使它起作用,必须使拖动行为优先于平移。

除此之外,没有d3.event.transform拖动事件。根据API,这些是事件对象中的属性:

  • target-关联的拖动行为。
  • 类型-字符串“开始”,“拖动”或“结束”;参见drag.on。
  • subject-拖动主题,由drag.subject定义。
  • x-主题的新x坐标;参见drag.container。
  • y-主题的新y坐标;参见drag.container。
  • dx-自上一次拖动事件以来的x坐标更改。
  • dy-自上次拖动事件以来y坐标的变化。
  • 标识符-字符串“ mouse”或数字触摸标识符。
  • active-当前活动的拖动手势的数量
  • sourceEvent-基础输入事件,例如mousemove或touchmove。

所以,你transformundefined...... 如果你可以调用dragged

解决方案1

只需将放下d3.drag,即可使用缩放的单击和拖动(平移)旋转地球仪。

这是您的代码,只需最少的更改(我将所有内容放入zoomed)。使用鼠标滚轮进行缩放,然后单击地球以将其拖动:

function createMap() {
  var width = 400,
    height = 300,
    scale = 100,
    lastX = 0,
    lastY = 0,
    origin = {
      x: 55,
      y: -40
    };

  var svg = d3.select('body').append('svg')
    .style('width', 400)
    .style('height', 300)
    .style('border', '1px lightgray solid')

  var projection = d3.geoOrthographic()
    .scale(scale)
    .translate([width / 2, height / 2])
    .rotate([origin.x, origin.y])
    .center([0, 0])
    .clipAngle(90);

  var geoPath = d3.geoPath()
    .projection(projection);

  var graticule = d3.geoGraticule();

  // zoom AND rotate
  svg.call(d3.zoom().on('zoom', zoomed));

  // code snippet from http://stackoverflow.com/questions/36614251
  var ? = d3.scaleLinear()
    .domain([-width, width])
    .range([-180, 180])

  var ? = d3.scaleLinear()
    .domain([-height, height])
    .range([90, -90]);

  svg.append('path')
    .datum(graticule)
    .attr('class', 'graticule')
    .attr('d', geoPath);

  function zoomed() {
    var transform = d3.event.transform;
    var r = {
      x: ?(transform.x),
      y: ?(transform.y)
    };
    var k = Math.sqrt(100 / projection.scale());
    if (d3.event.sourceEvent.wheelDelta) {
      projection.scale(scale * transform.k)
      transform.x = lastX;
      transform.y = lastY;
    } else {
      projection.rotate([origin.x + r.x, origin.y + r.y]);
      lastX = transform.x;
      lastY = transform.y;
    }
    updatePaths(svg, graticule, geoPath);
  };

};

function updatePaths(svg, graticule, geoPath) {
  svg.selectAll('path.graticule').datum(graticule).attr('d', geoPath);
};

createMap();
Run Code Online (Sandbox Code Playgroud)
.graticule {
  fill: none;
  stroke: #777;
  stroke-width: .5px;
  stroke-opacity: .5;
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://d3js.org/d3.v4.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

解决方案2

如果您真的想使用d3.drag,这是我建议的解决方案:

首先,创建一个小组来举行您的地球仪:

var group = svg.append("g")
Run Code Online (Sandbox Code Playgroud)

然后,调用拖动到该组:

group.call(d3.drag().on('drag', dragged));
Run Code Online (Sandbox Code Playgroud)

最后,在dragged函数中,使用d3.event.xd3.event.y

function dragged(d) {
    var r = {
        x: ?((d.x = d3.event.x)),
        y: ?((d.y = d3.event.y))
    };
    projection.rotate([origin.x + r.x, origin.y + r.y]);
    updatePaths(svg, graticule, geoPath);
};
Run Code Online (Sandbox Code Playgroud)

另外,不要忘记将pointer-events的路径设置为all:由于其填充为none,因此很难在细线上精确地单击。

这是更新的代码,单击地球并拖动它:

var group = svg.append("g")
Run Code Online (Sandbox Code Playgroud)
group.call(d3.drag().on('drag', dragged));
Run Code Online (Sandbox Code Playgroud)
function dragged(d) {
    var r = {
        x: ?((d.x = d3.event.x)),
        y: ?((d.y = d3.event.y))
    };
    projection.rotate([origin.x + r.x, origin.y + r.y]);
    updatePaths(svg, graticule, geoPath);
};
Run Code Online (Sandbox Code Playgroud)