D3 has a force directed layout here. Is there a way to add zooming to this graph? Currently, I was able to capture the mouse wheel event but am not really sure how to write the redraw function itself. Any suggestions?
D3在这里有一个强制的布局。有没有办法在这个图中添加缩放?目前,我能够捕获鼠标滚轮事件,但不确定如何编写redraw函数本身。有什么建议吗?
var vis = d3.select("#graph")
.append("svg:svg")
.call(d3.behavior.zoom().on("zoom", redraw)) // <-- redraw function
.attr("width", w)
.attr("height", h);
6 个解决方案
#1
97
Update 6/4/14
更新6/4/14
See also Mike Bostock's answer here for changes in D3 v.3 and the related example. I think this probably supersedes the answer below.
关于D3 v的变化,请参见Mike Bostock的回答。3和相关的例子。我认为这可能会取代下面的答案。
Update 2/18/2014
更新2/18/2014
I think @ahaarnos's answer is preferable if you want the entire SVG to pan and zoom. The nested g
elements in my answer below are really only necessary if you have non-zooming elements in the same SVG (not the case in the original question). If you do apply the behavior to a g
element, then a background rect
or similar element is required to ensure that the g
receives pointer events.
我认为如果您希望整个SVG进行平移和缩放,@ahaarnos的答案是更好的。我下面回答的嵌套g元素只有在同一SVG中有非缩放元素时才需要(在最初的问题中不是这样)。如果您确实将行为应用到g元素,则需要一个背景矩形或类似的元素来确保g接收指针事件。
Original Answer
原来的答案
I got this working based on the zoom-pan-transform example - you can see my jsFiddle here: http://jsfiddle.net/nrabinowitz/QMKm3/
我在zoom-pan转换示例的基础上完成了这项工作——您可以在这里看到我的jsFiddle: http://jsfiddle.net/nrabinowitz/QMKm3/
It was a bit more complex than I had hoped - you have to nest several g
elements to get it to work, set the SVG's pointer-events
attribute to all
, and then append a background rectangle to receive the pointer events (otherwise it only works when the pointer is over a node or link). The redraw
function is comparatively simple, just setting a transform on the innermost g
:
有点更复杂的比我所希望的——你有巢几个g的元素让它工作,SVG的pointer-events属性设置为所有,然后添加一个背景矩形接收指针事件(否则指针时只能在一个节点或链接)。redraw函数比较简单,只需在最内层g上设置一个变换:
var vis = d3.select("#chart")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("pointer-events", "all")
.append('svg:g')
.call(d3.behavior.zoom().on("zoom", redraw))
.append('svg:g');
vis.append('svg:rect')
.attr('width', w)
.attr('height', h)
.attr('fill', 'white');
function redraw() {
console.log("here", d3.event.translate, d3.event.scale);
vis.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
This effectively scales the entire SVG, so it scales stroke width as well, like zooming in on an image.
这样可以有效地缩放整个SVG,因此它也可以缩放笔画宽度,就像放大图像一样。
There is another example that illustrates a similar technique.
还有另一个例子说明了类似的技术。
#2
17
Why the nested <g>
's?
为什么嵌套的< g >的吗?
This code below worked well for me (only one <g>
, with no random large white <rect>
:
下面的代码对我来说很好(只有一个
var svg = d3.select("body")
.append("svg")
.attr({
"width": "100%",
"height": "100%"
})
.attr("viewBox", "0 0 " + width + " " + height )
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("pointer-events", "all")
.call(d3.behavior.zoom().on("zoom", redraw));
var vis = svg
.append('svg:g');
function redraw() {
vis.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
Where all the elements in your svg are then appended to the vis
element.
然后将svg中的所有元素添加到vis元素中。
#3
14
The provided answers work in D3 v2 but not in v3. I've synthesized the responses into a clean solution and resolved the v3 issue using the answer provided here: Why does d3.js v3 break my force graph when implementing zooming when v2 doesn't?
所提供的答案在D3 v2中有效,但在v3中无效。我已经将响应合成为一个干净的解决方案,并使用这里提供的答案解决了v3问题:为什么d3。当v2不实现缩放时,js v3打破我的力图?
First the main code. This is a cleaned up version of @ahaarnos' answer:
第一个主代码。这是对@ahaarnos回答的整理版本:
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().on("zoom", redraw))
.append('g');
function redraw() {
svg.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
Now you have pan and zoom, but you won't be able to drag nodes because the pan functionality will override the drag functionality. So we need to do this:
现在有了平移和缩放,但是不能拖动节点,因为平移功能将覆盖拖动功能。所以我们需要这样做:
var drag = force.stop().drag()
.on("dragstart", function(d) {
d3.event.sourceEvent.stopPropagation(); // to prevent pan functionality from
//overriding node drag functionality.
// put any other 'dragstart' actions here
});
Here's @nrabinowitz' fiddle modified to use this cleaner zoom implementation, but illustrating how D3v3 breaks node drag: http://jsfiddle.net/QMKm3/718/
下面是@nrabinowitz修改的小提琴,以使用这个更简洁的缩放实现,但说明了D3v3如何破坏节点拖动:http://jsfiddle.net/QMKm3/718/
And here's the same fiddle modified to work with D3v3: http://jsfiddle.net/QMKm3/719/
这里是修改过的用于处理D3v3的小提琴:http://jsfiddle.net/QMKm3/719/
#4
2
I got my graph to work without the second "svg:g" append.
我让我的图形工作,没有第二个“svg:g”附加。
[...].attr("pointer-events", "all")
.attr("width", width2)
.attr("height", height2)
.append('svg:g')
.call(d3.behavior.zoom().on("zoom", redraw));
The rest is the same.
其余的都是一样的。
#5
0
I got a solution for D3 force directed graph with zooming option.
我得到了D3有向图的缩放选项。
var m = [40, 240, 40, 240],
width = 960,
height = 700,
root;
var svg = d3.select("body").append("svg")
.attr("class", "svg_container")
.attr("width", width)
.attr("height", height)
.style("overflow", "scroll")
.style("background-color", "#EEEEEE")
.append("svg:g")
.attr("class", "drawarea")
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
//applying zoom in&out for svg
d3.select("svg")
.call(d3.behavior.zoom()
.scaleExtent([0.5, 5])
.on("zoom", zoom));
//zooming
function zoom() { //zoom in&out function
var scale = d3.event.scale,
translation = d3.event.translate,
tbound = -height * scale,
bbound = height * scale,
lbound = (-width + m[1]) * scale,
rbound = (width - m[3]) * scale;
// limit translation to thresholds
translation = [
Math.max(Math.min(translation[0], rbound), lbound),
Math.max(Math.min(translation[1], bbound), tbound)
];
d3.select(".drawarea")
.attr("transform", "translate(" + translation + ")" +
" scale(" + scale + ")");
}
#6
0
If you want to zoom and pan force layout without changing node-size, try below. You can also drag nodes without trembling. This code is based on original force layout example. As for nodes and links data, please refer to original sample data. http://bl.ocks.org/mbostock/4062045
如果您想要缩放和平移不改变节点大小,请尝试下面。您还可以不颤抖地拖动节点。此代码基于原力布局示例。节点和链接数据请参考原始样本数据。http://bl.ocks.org/mbostock/4062045
Plz note the variables xScale and yScale, the functions dragstarted(), dragged(), and dragended(). Function tick() was changed as well.
Plz注意变量xScale和yScale、dragstarted()、drag()和dragended()函数。函数tick()也被更改。
You can see the result at http://steelblue.tistory.com/9 The language on the site is Korean. However you can easily find the result at the third example on the page.
你可以在http://steelblue.tistory.com/9上看到结果,网站上的语言是韩语。但是,您可以在页面上的第三个示例中轻松找到结果。
var graph = {
"nodes": [
{ "name": "Myriel", "group": 1 },
{ "name": "Napoleon", "group": 1 },
// ......
{ "name": "Mme.Hucheloup", "group": 8 }
],
"links": [
{ "source": 1, "target": 0, "value": 1 },
{ "source": 2, "target": 0, "value": 8 },
// .......
{ "source": 76, "target": 58, "value": 1 }
]
};
var width = 640,
height = 400;
var color = d3.scale.category20();
var xScale = d3.scale.linear()
.domain([0, width])
.range([0, width]);
var yScale = d3.scale.linear()
.domain([0, height])
.range([0, height]);
var zoomer = d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([0.1, 8]).on("zoom", zoom);
function zoom() {
tick();
};
var drag = d3.behavior.drag()
.origin(function (d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d.fixed |= 2;
}
function dragged(d) {
var mouse = d3.mouse(svg.node());
d.x = xScale.invert(mouse[0]);
d.y = yScale.invert(mouse[1]);
d.px = d.x;
d.py = d.y;
force.resume();
}
function dragended(d) {
d.fixed &= ~6; }
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.call(zoomer);
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function (d) { return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function (d) { return color(d.group); })
.call(drag);
node.append("title")
.text(function (d) { return d.name; });
force.on("tick",tick);
function tick(){
link.attr("x1", function (d) { return xScale(d.source.x); })
.attr("y1", function (d) { return yScale(d.source.y); })
.attr("x2", function (d) { return xScale(d.target.x); })
.attr("y2", function (d) { return yScale(d.target.y); });
node.attr("transform", function (d) {
return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")";
});
};
#1
97
Update 6/4/14
更新6/4/14
See also Mike Bostock's answer here for changes in D3 v.3 and the related example. I think this probably supersedes the answer below.
关于D3 v的变化,请参见Mike Bostock的回答。3和相关的例子。我认为这可能会取代下面的答案。
Update 2/18/2014
更新2/18/2014
I think @ahaarnos's answer is preferable if you want the entire SVG to pan and zoom. The nested g
elements in my answer below are really only necessary if you have non-zooming elements in the same SVG (not the case in the original question). If you do apply the behavior to a g
element, then a background rect
or similar element is required to ensure that the g
receives pointer events.
我认为如果您希望整个SVG进行平移和缩放,@ahaarnos的答案是更好的。我下面回答的嵌套g元素只有在同一SVG中有非缩放元素时才需要(在最初的问题中不是这样)。如果您确实将行为应用到g元素,则需要一个背景矩形或类似的元素来确保g接收指针事件。
Original Answer
原来的答案
I got this working based on the zoom-pan-transform example - you can see my jsFiddle here: http://jsfiddle.net/nrabinowitz/QMKm3/
我在zoom-pan转换示例的基础上完成了这项工作——您可以在这里看到我的jsFiddle: http://jsfiddle.net/nrabinowitz/QMKm3/
It was a bit more complex than I had hoped - you have to nest several g
elements to get it to work, set the SVG's pointer-events
attribute to all
, and then append a background rectangle to receive the pointer events (otherwise it only works when the pointer is over a node or link). The redraw
function is comparatively simple, just setting a transform on the innermost g
:
有点更复杂的比我所希望的——你有巢几个g的元素让它工作,SVG的pointer-events属性设置为所有,然后添加一个背景矩形接收指针事件(否则指针时只能在一个节点或链接)。redraw函数比较简单,只需在最内层g上设置一个变换:
var vis = d3.select("#chart")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("pointer-events", "all")
.append('svg:g')
.call(d3.behavior.zoom().on("zoom", redraw))
.append('svg:g');
vis.append('svg:rect')
.attr('width', w)
.attr('height', h)
.attr('fill', 'white');
function redraw() {
console.log("here", d3.event.translate, d3.event.scale);
vis.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
This effectively scales the entire SVG, so it scales stroke width as well, like zooming in on an image.
这样可以有效地缩放整个SVG,因此它也可以缩放笔画宽度,就像放大图像一样。
There is another example that illustrates a similar technique.
还有另一个例子说明了类似的技术。
#2
17
Why the nested <g>
's?
为什么嵌套的< g >的吗?
This code below worked well for me (only one <g>
, with no random large white <rect>
:
下面的代码对我来说很好(只有一个
var svg = d3.select("body")
.append("svg")
.attr({
"width": "100%",
"height": "100%"
})
.attr("viewBox", "0 0 " + width + " " + height )
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("pointer-events", "all")
.call(d3.behavior.zoom().on("zoom", redraw));
var vis = svg
.append('svg:g');
function redraw() {
vis.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
Where all the elements in your svg are then appended to the vis
element.
然后将svg中的所有元素添加到vis元素中。
#3
14
The provided answers work in D3 v2 but not in v3. I've synthesized the responses into a clean solution and resolved the v3 issue using the answer provided here: Why does d3.js v3 break my force graph when implementing zooming when v2 doesn't?
所提供的答案在D3 v2中有效,但在v3中无效。我已经将响应合成为一个干净的解决方案,并使用这里提供的答案解决了v3问题:为什么d3。当v2不实现缩放时,js v3打破我的力图?
First the main code. This is a cleaned up version of @ahaarnos' answer:
第一个主代码。这是对@ahaarnos回答的整理版本:
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().on("zoom", redraw))
.append('g');
function redraw() {
svg.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
Now you have pan and zoom, but you won't be able to drag nodes because the pan functionality will override the drag functionality. So we need to do this:
现在有了平移和缩放,但是不能拖动节点,因为平移功能将覆盖拖动功能。所以我们需要这样做:
var drag = force.stop().drag()
.on("dragstart", function(d) {
d3.event.sourceEvent.stopPropagation(); // to prevent pan functionality from
//overriding node drag functionality.
// put any other 'dragstart' actions here
});
Here's @nrabinowitz' fiddle modified to use this cleaner zoom implementation, but illustrating how D3v3 breaks node drag: http://jsfiddle.net/QMKm3/718/
下面是@nrabinowitz修改的小提琴,以使用这个更简洁的缩放实现,但说明了D3v3如何破坏节点拖动:http://jsfiddle.net/QMKm3/718/
And here's the same fiddle modified to work with D3v3: http://jsfiddle.net/QMKm3/719/
这里是修改过的用于处理D3v3的小提琴:http://jsfiddle.net/QMKm3/719/
#4
2
I got my graph to work without the second "svg:g" append.
我让我的图形工作,没有第二个“svg:g”附加。
[...].attr("pointer-events", "all")
.attr("width", width2)
.attr("height", height2)
.append('svg:g')
.call(d3.behavior.zoom().on("zoom", redraw));
The rest is the same.
其余的都是一样的。
#5
0
I got a solution for D3 force directed graph with zooming option.
我得到了D3有向图的缩放选项。
var m = [40, 240, 40, 240],
width = 960,
height = 700,
root;
var svg = d3.select("body").append("svg")
.attr("class", "svg_container")
.attr("width", width)
.attr("height", height)
.style("overflow", "scroll")
.style("background-color", "#EEEEEE")
.append("svg:g")
.attr("class", "drawarea")
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
//applying zoom in&out for svg
d3.select("svg")
.call(d3.behavior.zoom()
.scaleExtent([0.5, 5])
.on("zoom", zoom));
//zooming
function zoom() { //zoom in&out function
var scale = d3.event.scale,
translation = d3.event.translate,
tbound = -height * scale,
bbound = height * scale,
lbound = (-width + m[1]) * scale,
rbound = (width - m[3]) * scale;
// limit translation to thresholds
translation = [
Math.max(Math.min(translation[0], rbound), lbound),
Math.max(Math.min(translation[1], bbound), tbound)
];
d3.select(".drawarea")
.attr("transform", "translate(" + translation + ")" +
" scale(" + scale + ")");
}
#6
0
If you want to zoom and pan force layout without changing node-size, try below. You can also drag nodes without trembling. This code is based on original force layout example. As for nodes and links data, please refer to original sample data. http://bl.ocks.org/mbostock/4062045
如果您想要缩放和平移不改变节点大小,请尝试下面。您还可以不颤抖地拖动节点。此代码基于原力布局示例。节点和链接数据请参考原始样本数据。http://bl.ocks.org/mbostock/4062045
Plz note the variables xScale and yScale, the functions dragstarted(), dragged(), and dragended(). Function tick() was changed as well.
Plz注意变量xScale和yScale、dragstarted()、drag()和dragended()函数。函数tick()也被更改。
You can see the result at http://steelblue.tistory.com/9 The language on the site is Korean. However you can easily find the result at the third example on the page.
你可以在http://steelblue.tistory.com/9上看到结果,网站上的语言是韩语。但是,您可以在页面上的第三个示例中轻松找到结果。
var graph = {
"nodes": [
{ "name": "Myriel", "group": 1 },
{ "name": "Napoleon", "group": 1 },
// ......
{ "name": "Mme.Hucheloup", "group": 8 }
],
"links": [
{ "source": 1, "target": 0, "value": 1 },
{ "source": 2, "target": 0, "value": 8 },
// .......
{ "source": 76, "target": 58, "value": 1 }
]
};
var width = 640,
height = 400;
var color = d3.scale.category20();
var xScale = d3.scale.linear()
.domain([0, width])
.range([0, width]);
var yScale = d3.scale.linear()
.domain([0, height])
.range([0, height]);
var zoomer = d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([0.1, 8]).on("zoom", zoom);
function zoom() {
tick();
};
var drag = d3.behavior.drag()
.origin(function (d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d.fixed |= 2;
}
function dragged(d) {
var mouse = d3.mouse(svg.node());
d.x = xScale.invert(mouse[0]);
d.y = yScale.invert(mouse[1]);
d.px = d.x;
d.py = d.y;
force.resume();
}
function dragended(d) {
d.fixed &= ~6; }
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.call(zoomer);
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function (d) { return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function (d) { return color(d.group); })
.call(drag);
node.append("title")
.text(function (d) { return d.name; });
force.on("tick",tick);
function tick(){
link.attr("x1", function (d) { return xScale(d.source.x); })
.attr("y1", function (d) { return yScale(d.source.y); })
.attr("x2", function (d) { return xScale(d.target.x); })
.attr("y2", function (d) { return yScale(d.target.y); });
node.attr("transform", function (d) {
return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")";
});
};