Reputation: 91233
I have the following d3 chart. It's simply a handful of nodes and links between them. The chart does 2 things:
d3.behavior.zoom
.My problem I'm seeing is that if I drag a node around (for example, move a node 20px to the right), and then try to pan the chart, the entire graph immediately jumps 20px to the right (per the example). d3.behavior.zoom
seems to initially use the d3.event
of dragging the node around or something like that. Not sure. Here is the code:
var data = {
nodes: [{
name: "A",
x: 200,
y: 150,
size: 30
}, {
name: "B",
x: 140,
y: 300,
size: 15
}, {
name: "C",
x: 300,
y: 300,
size: 15
}, {
name: "D",
x: 300,
y: 180,
size: 45
}],
links: [{
source: 0,
target: 1
}, {
source: 1,
target: 2
}, {
source: 2,
target: 3
}, ]
};
var dragging = false;
var svg = d3.select("body")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.call(d3.behavior.zoom().on("zoom", function() {
if (dragging === false) {
svg.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")")
}
})).append("g");
var links = svg.selectAll("link")
.data(data.links)
.enter()
.append("line")
.attr("class", "link")
.attr("x1", function(l) {
var sourceNode = data.nodes.filter(function(d, i) {
return i == l.source
})[0];
d3.select(this).attr("y1", sourceNode.y);
return sourceNode.x
})
.attr("x2", function(l) {
var targetNode = data.nodes.filter(function(d, i) {
return i == l.target
})[0];
d3.select(this).attr("y2", targetNode.y);
return targetNode.x
})
.attr("fill", "none")
.attr("stroke", "white");
var nodes = svg.selectAll("node")
.data(data.nodes)
.enter()
.append("circle")
.each(function(d, i) {
d.object = d3.select(this);
console.log(d);
})
.attr("class", "node")
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("r", function(d) {
return d.size;
})
.on('click', function(d) {
})
.call(d3.behavior.drag()
.on('dragstart', function() {
dragging = true;
})
.on('dragend', function() {
dragging = false;
})
.on("drag", function(d, i) {
d.x += d3.event.dx
d.y += d3.event.dy
d3.select(this).attr("cx", d.x).attr("cy", d.y);
links.each(function(l, li) {
if (l.source == i) {
d3.select(this).attr("x1", d.x).attr("y1", d.y);
} else if (l.target == i) {
d3.select(this).attr("x2", d.x).attr("y2", d.y);
}
});
})
);
Here is a demo:
https://jsfiddle.net/25q1Lu3x/2/
How can I prevent that initially jump when trying to pan around? I have read a few things that suggest making an extra nested svg g
element and move that around, but that doesn't seem to make a difference.
Upvotes: 1
Views: 454
Reputation: 102208
According to the D3 v3 API:
When combining drag behaviors with other event listeners for interaction events (such as having drag take precedence over zoom), you may also consider stopping propagation on the source event to prevent multiple actions
So, in your dragstart
, just add d3.event.sourceEvent.stopPropagation();
:
.on('dragstart', function() {
d3.event.sourceEvent.stopPropagation();
dragging = true;
});
Here is your fiddle: https://jsfiddle.net/hj7krohd/
Upvotes: 2