D3 强制布局缩放和平移不起作用
D3 forced layout zoom and pan not working
刚刚开始接触D3的强制布局,实现了一个径向图实现参考:http://flowingdata.com/2012/08/02/how-to-make-an-interactive-network-visualization/
我面临的问题是缩放不起作用。这是我引用的代码:http://jsfiddle.net/blt909/aVhd8/20/ 用于缩放功能
这是我的代码:
var w = $("#network-plot").width(),
h = 800,
r = 6,
fill = d3.scale.category20();
var payload={js:'mis1'}
$.ajax({
url: "/getSource",
type: "post",
async: false,
data: payload,
success: function (data) {
mis1=JSON.parse(data);
},
});
var force = d3.layout.force()
.charge(-30)
.linkDistance(310)
.size([w, h]);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svgnt = d3.select("#network-plot-svg")
.attr("width", w)
.attr("height", h)
.style('margin-top', h/15)
.call(zoom);
var vis = svgnt.append("svg:g");
var rect = vis.append("svg:rect")
.attr("width", w)
.attr("height", h)
.attr("fill", '#fff')
.style("pointer-events", "all");
function zoomed() {
vis.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var radius = d3.scale.log();
var mis1;
//**********RADIAL CODE****************
var unsortednodes = {};
var sortednodes = [];
var sortagainst = [];
var heaviest_node_length = 0;
for(var i = 0; i < mis1.nodes.length; i++) {
var count_conn = 0;
var heaviest_node;
for(var j = 0; j < mis1.links.length; j++) {
if(mis1.links[j].source == mis1.nodes[i].group || mis1.links[j].target == mis1.nodes[i].group) {
count_conn++;
}
}
if(count_conn > heaviest_node_length) {
heaviest_node_length = count_conn;
heaviest_node = i;
}
unsortednodes[i] = 10 + count_conn;
sortagainst[i] = 10 + count_conn;
}
sortagainst.sort();
sortagainst.reverse();
for(i in unsortednodes) {
var index = sortagainst.indexOf(unsortednodes[i]);
sortednodes[index] = i;
sortagainst[index] = '';
}
var c = {"x":w/2, "y":h/2};
var negativeY = 0;
var positiveY = 0;
var negativeX = 0;
var positiveX = 0;
var groupcenters = radialPlacement(c, 300, 18, sortednodes);
//following lines for readjustment code in case svg container is outgrown by graph
h = Math.abs(negativeY) + Math.abs(positiveY) + 100; //TBD: extra padding needs to be dynamic
w = Math.abs(negativeX) + Math.abs(positiveX) + 200;
c = {"x":w/2, "y":h/2};
groupcenters = radialPlacement(c, 300, 18, sortednodes);
svgnt.attr("height", h);
vis.attr("height", h);
svgnt.attr("width", w);
vis.attr("width", w);
function radialLocation(center, angle, radius) {
x = (center.x + radius * Math.cos(angle * Math.PI / 180));
y = (center.y + radius * Math.sin(angle * Math.PI / 180));
if(y < negativeY) {
negativeY = y;
}
if(y > positiveY) {
positiveY = y;
}
if(x < negativeX) {
negativeX = x;
}
if(x > positiveX) {
positiveX = x;
}
return {"x":x,"y":y};
}
function radialPlacement(center, radius, increment, keys) {
var values_circle = {};
var increment;
var start = -90;
var current = start;
var total_nodes = keys.length;
var circles = Math.floor(Math.sqrt((total_nodes - 1)/5));
if(circles == 0) {
circles = 1;
}
var ratio;
var r = [];
var circleKeys = [];
var radius = 140;
r[0] = radius;
var sum_r = r[0];
for(var j = 1; j < circles; j++){
r[j] = r[j-1] + 100 //TBD: should radius be linearly incremented like this?
sum_r += r[j];
}
ratio = 1/sum_r;
var temp = 0;
for(j = 0; j < circles; j++) {
if(j == circles - 1)
circleKeys[j] = total_nodes - temp;
else {
circleKeys[j] = Math.floor(total_nodes * r[j] * ratio);
temp += circleKeys[j];
}
}
var k = 0;
for(var i = 0; i < circleKeys.length; i++) {
increment = 360/circleKeys[i];
for(j = 0; j < circleKeys[i]; j++, k++) {
if(k == 0) {
values_circle[keys[k]] = radialLocation(center, -90, 0);
}
else {
values_circle[keys[k]] = radialLocation(center, current, r[i]);;
current += increment;
}
}
}
return values_circle;
}
//************RADIAL CODE ENDS***************
d3.json(mis1, function() {
var link = svgnt.selectAll("line")
.data(mis1.links)
.enter()
.append("svg:line")
.style("stroke","#ddd");
var node = svgnt.selectAll("circle")
.data(mis1.nodes)
.enter()
.append("svg:circle")
.attr("r", function(d, j){
var count = 0;
for(var i=0; i<mis1.links.length; i++) {
if(mis1.links[i].source == d.group || mis1.links[i].target == d.group){
count ++;
}
}
return (10+count);
})
.style("fill", function(d) {
return fill(d.group);
})
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
texts = svgnt.selectAll("text.label")
.data(mis1.nodes)
.enter().append("text")
.attr("class", "label")
.attr("fill", "black")
.text(function(d) { return d.name; });
force.nodes(mis1.nodes).links(mis1.links).on("tick", tick).start();
var linkedByIndex = {};
mis1.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
function tick() {
node.attr("cx", function(d, i) {
return d.x = groupcenters[i].x;
}).attr("cy", function(d, i) {
return d.y = groupcenters[i].y;
});
link.attr("x1", function(d) {
return d.source.x;
}).attr("y1", function(d) {
return d.source.y;
}).attr("x2", function(d) {
return d.target.x;
}).attr("y2", function(d) {
return d.target.y;
});
texts.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
知道我做错了什么吗?图形显示正常,但没有任何缩放功能。
问题是,您将所有节点和链接直接附加到 svg
而不是 g
元素。由于缩放变换应用于 vis
(g
元素),因此节点和链接不会 zoom/pan。
所以不用下面的代码
var link = svgnt.selectAll("line")
.data(mis1.links)
.enter()
.append("svg:line")
.style("stroke","#ddd");
var node = svgnt.selectAll("circle")
.data(mis1.nodes)
.enter()
.append("svg:circle")
----------------------
----------------------
----------------------
试试这个代码。
var link = vis.selectAll("line")
.data(mis1.links)
.enter()
.append("svg:line")
.style("stroke","#ddd");
var node = vis.selectAll("circle")
.data(mis1.nodes)
.enter()
.append("svg:circle")
----------------------
----------------------
----------------------
刚刚开始接触D3的强制布局,实现了一个径向图实现参考:http://flowingdata.com/2012/08/02/how-to-make-an-interactive-network-visualization/
我面临的问题是缩放不起作用。这是我引用的代码:http://jsfiddle.net/blt909/aVhd8/20/ 用于缩放功能
这是我的代码:
var w = $("#network-plot").width(),
h = 800,
r = 6,
fill = d3.scale.category20();
var payload={js:'mis1'}
$.ajax({
url: "/getSource",
type: "post",
async: false,
data: payload,
success: function (data) {
mis1=JSON.parse(data);
},
});
var force = d3.layout.force()
.charge(-30)
.linkDistance(310)
.size([w, h]);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svgnt = d3.select("#network-plot-svg")
.attr("width", w)
.attr("height", h)
.style('margin-top', h/15)
.call(zoom);
var vis = svgnt.append("svg:g");
var rect = vis.append("svg:rect")
.attr("width", w)
.attr("height", h)
.attr("fill", '#fff')
.style("pointer-events", "all");
function zoomed() {
vis.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var radius = d3.scale.log();
var mis1;
//**********RADIAL CODE****************
var unsortednodes = {};
var sortednodes = [];
var sortagainst = [];
var heaviest_node_length = 0;
for(var i = 0; i < mis1.nodes.length; i++) {
var count_conn = 0;
var heaviest_node;
for(var j = 0; j < mis1.links.length; j++) {
if(mis1.links[j].source == mis1.nodes[i].group || mis1.links[j].target == mis1.nodes[i].group) {
count_conn++;
}
}
if(count_conn > heaviest_node_length) {
heaviest_node_length = count_conn;
heaviest_node = i;
}
unsortednodes[i] = 10 + count_conn;
sortagainst[i] = 10 + count_conn;
}
sortagainst.sort();
sortagainst.reverse();
for(i in unsortednodes) {
var index = sortagainst.indexOf(unsortednodes[i]);
sortednodes[index] = i;
sortagainst[index] = '';
}
var c = {"x":w/2, "y":h/2};
var negativeY = 0;
var positiveY = 0;
var negativeX = 0;
var positiveX = 0;
var groupcenters = radialPlacement(c, 300, 18, sortednodes);
//following lines for readjustment code in case svg container is outgrown by graph
h = Math.abs(negativeY) + Math.abs(positiveY) + 100; //TBD: extra padding needs to be dynamic
w = Math.abs(negativeX) + Math.abs(positiveX) + 200;
c = {"x":w/2, "y":h/2};
groupcenters = radialPlacement(c, 300, 18, sortednodes);
svgnt.attr("height", h);
vis.attr("height", h);
svgnt.attr("width", w);
vis.attr("width", w);
function radialLocation(center, angle, radius) {
x = (center.x + radius * Math.cos(angle * Math.PI / 180));
y = (center.y + radius * Math.sin(angle * Math.PI / 180));
if(y < negativeY) {
negativeY = y;
}
if(y > positiveY) {
positiveY = y;
}
if(x < negativeX) {
negativeX = x;
}
if(x > positiveX) {
positiveX = x;
}
return {"x":x,"y":y};
}
function radialPlacement(center, radius, increment, keys) {
var values_circle = {};
var increment;
var start = -90;
var current = start;
var total_nodes = keys.length;
var circles = Math.floor(Math.sqrt((total_nodes - 1)/5));
if(circles == 0) {
circles = 1;
}
var ratio;
var r = [];
var circleKeys = [];
var radius = 140;
r[0] = radius;
var sum_r = r[0];
for(var j = 1; j < circles; j++){
r[j] = r[j-1] + 100 //TBD: should radius be linearly incremented like this?
sum_r += r[j];
}
ratio = 1/sum_r;
var temp = 0;
for(j = 0; j < circles; j++) {
if(j == circles - 1)
circleKeys[j] = total_nodes - temp;
else {
circleKeys[j] = Math.floor(total_nodes * r[j] * ratio);
temp += circleKeys[j];
}
}
var k = 0;
for(var i = 0; i < circleKeys.length; i++) {
increment = 360/circleKeys[i];
for(j = 0; j < circleKeys[i]; j++, k++) {
if(k == 0) {
values_circle[keys[k]] = radialLocation(center, -90, 0);
}
else {
values_circle[keys[k]] = radialLocation(center, current, r[i]);;
current += increment;
}
}
}
return values_circle;
}
//************RADIAL CODE ENDS***************
d3.json(mis1, function() {
var link = svgnt.selectAll("line")
.data(mis1.links)
.enter()
.append("svg:line")
.style("stroke","#ddd");
var node = svgnt.selectAll("circle")
.data(mis1.nodes)
.enter()
.append("svg:circle")
.attr("r", function(d, j){
var count = 0;
for(var i=0; i<mis1.links.length; i++) {
if(mis1.links[i].source == d.group || mis1.links[i].target == d.group){
count ++;
}
}
return (10+count);
})
.style("fill", function(d) {
return fill(d.group);
})
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
texts = svgnt.selectAll("text.label")
.data(mis1.nodes)
.enter().append("text")
.attr("class", "label")
.attr("fill", "black")
.text(function(d) { return d.name; });
force.nodes(mis1.nodes).links(mis1.links).on("tick", tick).start();
var linkedByIndex = {};
mis1.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
function tick() {
node.attr("cx", function(d, i) {
return d.x = groupcenters[i].x;
}).attr("cy", function(d, i) {
return d.y = groupcenters[i].y;
});
link.attr("x1", function(d) {
return d.source.x;
}).attr("y1", function(d) {
return d.source.y;
}).attr("x2", function(d) {
return d.target.x;
}).attr("y2", function(d) {
return d.target.y;
});
texts.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
知道我做错了什么吗?图形显示正常,但没有任何缩放功能。
问题是,您将所有节点和链接直接附加到 svg
而不是 g
元素。由于缩放变换应用于 vis
(g
元素),因此节点和链接不会 zoom/pan。
所以不用下面的代码
var link = svgnt.selectAll("line")
.data(mis1.links)
.enter()
.append("svg:line")
.style("stroke","#ddd");
var node = svgnt.selectAll("circle")
.data(mis1.nodes)
.enter()
.append("svg:circle")
----------------------
----------------------
----------------------
试试这个代码。
var link = vis.selectAll("line")
.data(mis1.links)
.enter()
.append("svg:line")
.style("stroke","#ddd");
var node = vis.selectAll("circle")
.data(mis1.nodes)
.enter()
.append("svg:circle")
----------------------
----------------------
----------------------