我无法通过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);
};
我尝试用dragged()
替换console.log()
,即使在这种情况下也没有发生任何事情(显然函数没有被调用)。我以类似的方式实现了d3.zoom()
而没有任何问题。我做错了什么?
答案 0 :(得分:5)
您的拖动无响应的原因是您已经在该SVG上调用缩放,并且缩放处理平移。因此,要使d3.drag
起作用,您必须使拖动行为优先于平移。
除此之外,拖拽事件没有d3.event.transform
。根据{{3}},这些是事件对象中的属性:
- target - 关联的拖动行为。
- type - 字符串“start”,“drag”或“end”;见drag.on。
- subject - 拖动主题,由drag.subject定义。
- x - 主题的新x坐标;见drag.container。
- y - 主题的新y坐标;见drag.container。
- dx - 自上次拖动事件以来x坐标的变化。
- dy - 自上次拖动事件以来y坐标的变化。
- identifier - 字符串“mouse”或数字触摸标识符。
- 有效 - 当前有效拖动手势的数量
- sourceEvent - 基础输入事件,例如mousemove或 touchmove。
因此,您的transform
将为undefined
... ,如果,您可以致电dragged
。
只需放下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();
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
如果您真的想使用d3.drag
,这是我提出的解决方案:
首先,创建一个组来容纳你的地球:
var group = svg.append("g")
然后,将拖动调用到此组:
group.call(d3.drag().on('drag', dragged));
最后,在dragged
函数中,使用d3.event.x
和d3.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);
};
另外,不要忘记将路径的pointer-events
设置为all
:由于填充为none
,因此很难完全点击细线。
这是更新的代码,点击地球并拖动它:
function createMap() {
var width = 400,
height = 300,
scale = 100,
origin = {
x: 55,
y: -40
};
var svg = d3.select('body').append('svg')
.style('width', 400)
.style('height', 300)
.style('border', '1px lightgray solid');
var group = svg.append("g").datum({
x: 0,
y: 0
})
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));
group.call(d3.drag().on('drag', dragged));
// 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]);
group.append('path')
.datum(graticule)
.attr('class', 'graticule')
.attr('d', geoPath);
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);
};
function zoomed() {
var transform = d3.event.transform;
var k = Math.sqrt(100 / projection.scale());
projection.scale(scale * transform.k)
updatePaths(svg, graticule, geoPath);
};
};
function updatePaths(svg, graticule, geoPath) {
svg.selectAll('path.graticule').datum(graticule).attr('d', geoPath);
};
createMap();
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
pointer-events: all;
}
<script src="https://d3js.org/d3.v4.min.js"></script>