问题描述
我有一个D3部队布局,其中有两种类型的节点:源和目标。 源应该在左侧,链接到右侧的目标。节点的位置按预期工作,但是当我向节点添加标签时,我无法防止标签与节点或其他标签重叠。
我尝试使用以下模板:
https://observablehq.com/@d3/voronoi-labels和http://bl.ocks.org/MoritzStefaner/1377729
当前状态:
PACKAGE_USAGE_STATS
const graph = {
links: [{
sourceid: 4433,targetid: 3528,source: 1,target: 0,value: 1
},{
sourceid: 4617,targetid: 96,source: 3,target: 2,value: 24
},{
sourceid: 4633,targetid: 149,source: 5,target: 4,targetid: 2876,target: 6,{
sourceid: 4753,source: 7,value: 2
},{
sourceid: 4758,targetid: 192,source: 9,target: 8,{
sourceid: 7174,targetid: 132,source: 11,target: 10,value: 32
},targetid: 178,target: 12,targetid: 603,target: 13,targetid: 3120,target: 14,value: 30
},targetid: 3678,target: 15,targetid: 3846,target: 16,{
sourceid: 14806,source: 17,value: 3
},{
sourceid: 19163,source: 18,{
sourceid: 19343,source: 19,value: 62
},{
sourceid: 20193,source: 20,value: 7
},{
sourceid: 20749,source: 21,{
sourceid: 21585,targetid: 67,source: 23,target: 22,value: 18
},value: 15
},targetid: 3808,target: 24,{
sourceid: 22373,source: 25,{
sourceid: 23200,source: 26,{
sourceid: 24201,targetid: 82,source: 28,target: 27,targetid: 86,target: 29,targetid: 113,target: 30,value: 10
},targetid: 373,target: 31,value: 12
},targetid: 702,target: 32,value: 9
},targetid: 2756,target: 33,value: 22
},{
sourceid: 24617,source: 34,{
sourceid: 25134,source: 35,{
sourceid: 25385,source: 36,targetid: 2753,target: 37,{
sourceid: 25823,source: 38,targetid: 3757,target: 39,{
sourceid: 26184,source: 40,value: 8
},value: 6
},targetid: 672,target: 41,targetid: 2874,target: 42,{
sourceid: 26388,source: 43,value: 21
},{
sourceid: 26510,source: 44,{
sourceid: 26560,source: 45,value: 5
},{
sourceid: 26571,source: 46,{
sourceid: 26572,source: 47,{
sourceid: 26574,source: 48,{
sourceid: 26597,source: 49,value: 13
},{
sourceid: 26603,source: 50,{
sourceid: 26613,source: 51,value: 11
},{
sourceid: 26647,source: 52,{
sourceid: 26668,source: 53,{
sourceid: 26798,source: 54,value: 7
}
],nodes: [{
id: 3528,value: 1,name: 'target1',target: true
},{
id: 4433,name: 'source1',target: false
},{
id: 96,value: 110,name: 'target2',{
id: 4617,value: 24,name: 'source2',{
id: 149,value: 95,name: 'target3',{
id: 4633,value: 2,name: 'source3',{
id: 2876,value: 78,name: 'target4',{
id: 4753,name: 'source4',{
id: 192,name: 'target5',{
id: 4758,name: 'source5',{
id: 132,value: 61,name: 'target6',{
id: 7174,value: 222,name: 'source6',{
id: 178,value: 42,name: 'target7',{
id: 603,value: 32,name: 'target8',{
id: 3120,value: 64,name: 'target9',{
id: 3678,name: 'target10',{
id: 3846,name: 'target11',{
id: 14806,value: 3,name: 'source7',{
id: 19163,name: 'source8',{
id: 19343,value: 62,name: 'source9',{
id: 20193,value: 7,name: 'source10',{
id: 20749,name: 'source11',{
id: 67,value: 17,name: 'target12',{
id: 21585,name: 'source12',{
id: 3808,value: 5,name: 'target13',{
id: 22373,name: 'source13',{
id: 23200,name: 'source14',{
id: 82,value: 40,name: 'target14',{
id: 24201,value: 174,name: 'source15',{
id: 86,name: 'target15',{
id: 113,value: 49,name: 'target16',{
id: 373,value: 12,name: 'target17',{
id: 702,value: 9,name: 'target18',{
id: 2756,name: 'target19',{
id: 24617,name: 'source16',{
id: 25134,name: 'source17',{
id: 25385,value: 36,name: 'source18',{
id: 2753,name: 'target20',{
id: 25823,name: 'source19',{
id: 3757,name: 'target21',{
id: 26184,value: 21,name: 'source20',{
id: 672,value: 29,name: 'target22',{
id: 2874,name: 'target23',{
id: 26388,name: 'source21',{
id: 26510,name: 'source22',{
id: 26560,value: 10,name: 'source23',{
id: 26571,value: 4,name: 'source24',{
id: 26572,value: 28,name: 'source25',{
id: 26574,name: 'source26',{
id: 26597,name: 'source27',{
id: 26603,name: 'source28',{
id: 26613,name: 'source29',{
id: 26647,name: 'source30',{
id: 26668,name: 'source31',{
id: 26798,name: 'source32',{
id: 3821,name: 'target24',{
id: 597,name: 'target25',{
id: 301,name: 'target26',target: true
}
]
};
/* max "value" of nodes (to size the bubbles) */
var maxNodeValue = 62;
/* max "value" of link (to size the width of lines) */
var maxLinkValue = 22;
var radiusScale = d3.scaleSqrt().domain([1,(maxNodeValue)]).range([2,10]);
var linkScale = d3.scaleSqrt().domain([1,(maxLinkValue)]).range([0.5,3]);
// svg objects
let link,node,labelNode;
const width = 1200;
const height = 800;
var categoryCenters = {
source: {
x: /*1**/ width / 4,y: height / 2
},target: {
x: 3 * width / 4,y: height / 2
}
};
var label = {
'nodes': [],'links': []
};
//////////// FORCE SIMULATION ////////////
// force simulator
var simulation = d3.forceSimulation();
var labelSimulation = d3.forceSimulation();
// set up the simulation and event to update locations after each tick
function initializeSimulation() {
simulation.nodes(graph.nodes);
labelSimulation.nodes(label.nodes);
initializeforces();
simulation.on("tick",ticked);
}
// values for all forces
const forceProperties = {
center: {
x: 0.5,y: 0.5
},charge: {
enabled: false,strength: -30,distanceMin: 1,distanceMax: 100
},collide: {
enabled: true,strength: 0.7,iterations: 1,radius: 5
},forceX: {
enabled: true,strength: 0.6,},forceY: {
enabled: true,strength: 0.1,link: {
enabled: true,iterations: 1
},labelCharge: {
enabled: true,strength: -30
},labelLink: {
distance: 0,strength: 2,iterations: 1
}
}
let sources = 0,targets = 0;
graph.nodes.forEach(function(d,i) {
d.target ? targets++ : sources++;
label.nodes.push({
node: d
});
label.nodes.push({
node: d
});
label.links.push({
source: i * 2,target: i * 2 + 1
});
});
initializedisplay();
initializeSimulation();
function nodeSpacer(d) {
const nodeSpaceValue = Math.round(Math.max(2,(height / (Math.max(sources,targets)))));
return nodeSpaceValue + radiusScale(d.value);
}
// add forces to the simulation
function initializeforces() {
// add forces and associate each with a name
simulation
.force("link",d3.forceLink())
.force("charge",d3.forceManyBody())
.force("collide",d3.forceCollide(nodeSpacer))
.force("center",d3.forceCenter())
.force("forceX",d3.forceX())
.force("forceY",d3.forceY());
labelSimulation
.force("link",d3.forceCollide());
// apply properties to each of the forces
updateForces();
}
// apply new force properties
function updateForces() {
// get each force by name and update the properties
simulation.force("center")
.x(width * forceProperties.center.x)
.y(height * forceProperties.center.y);
simulation.force("charge")
.strength(forceProperties.charge.strength * forceProperties.charge.enabled)
.distanceMin(forceProperties.charge.distanceMin)
.distanceMax(forceProperties.charge.distanceMax);
simulation.force("collide")
.strength(forceProperties.collide.strength * forceProperties.collide.enabled)
.iterations(forceProperties.collide.iterations);
simulation.force("forceX")
.strength(forceProperties.forceX.strength * forceProperties.forceX.enabled)
.x(function(d) {
return d.target ? categoryCenters.target.x : categoryCenters.source.x;
});
simulation.force("forceY")
.strength(forceProperties.forceY.strength * forceProperties.forceY.enabled)
.y(function(d) {
return d.target ? categoryCenters.target.y : categoryCenters.source.y;
});
simulation.force("link")
.id(function(d) {
return d.index;
})
.distance(function(d) {
return radiusScale(d.value) + width / 2.0;
})
.iterations(forceProperties.link.iterations)
.links(forceProperties.link.enabled ? graph.links : []);
labelSimulation.force("charge")
.strength(forceProperties.labelCharge.strength * forceProperties.labelCharge.enabled)
labelSimulation.force("link")
.distance(forceProperties.labelLink.distance)
.iterations(forceProperties.labelLink.iterations)
.links(label.links);
// updates ignored until this is run
// restarts the simulation (important if simulation has already slowed down)
simulation.alpha(1).restart();
labelSimulation.alpha(1).restart();
}
const adjlist = [];
graph.links.forEach(function(d) {
adjlist[d.source.index + "-" + d.target.index] = true;
adjlist[d.target.index + "-" + d.source.index] = true;
});
function neigh(a,b) {
return a == b || adjlist[a + "-" + b];
}
//////////// disPLAY ////////////
// generate the svg objects and force simulation
function initializedisplay() {
const svg = d3.select("#viz").attr("width",width).attr("height",height);
var container = svg.append("g");
link = container.append("g").attr("class","links")
.selectAll("line")
.data(graph.links)
.enter()
.append("line")
.attr("stroke","#aaa")
.attr("stroke-width",function(d) {
return linkScale(d.value);
});
node = container.append("g").attr("class","nodes")
.selectAll("g")
.data(graph.nodes)
.enter()
.append("circle")
.attr("r",function(d) {
return radiusScale(d.value);
})
.attr("fill",function(d) {
return d.target ? "#FF0000" : "#0000FF";
})
labelNode = container.append("g").attr("class","labelNodes")
.selectAll("text")
.data(label.nodes)
.enter()
.append("text")
.text(function(d,i) {
return i % 2 == 0 ? "" : d.node.name + "(" + d3.format(',.0f')(d.node.value) + ")";
})
.style("fill","#555")
.style("font-family","Arial")
.style("font-size",12)
.style("pointer-events","none"); // to prevent mouSEOver/drag capture*/
}
// update the display based on the forces (but not positions)
function updatedisplay() {
node
.attr("r",function(d) {
return radiusScale(d.value);
})
.attr("stroke",forceProperties.charge.strength > 0 ? "blue" : "red")
.attr("stroke-width",forceProperties.charge.enabled == false ? 0 : Math.abs(forceProperties.charge.strength) / 15);
link
.attr("stroke-width",function(d) {
return linkScale(d.value);
})
.attr("opacity",forceProperties.link.enabled ? 1 : 0);
}
// update the display positions after each simulation tick
function ticked() {
node.call(updateNode);
d3.select('#alpha_value').style('flex-basis',(simulation.alpha() * 100) + '%');
labelSimulation.alphaTarget(0.3).restart();
labelNode.each(function(d,i) {
if (i % 2 == 0) {
d.x = d.node.x;
d.y = d.node.y;
} else {
var b = this.getBBox();
var diffX = d.x - d.node.x;
var diffY = d.y - d.node.y;
var dist = Math.sqrt(diffX * diffX + diffY * diffY);
var shiftX = b.width * (diffX - dist) / (dist * 2);
shiftX = Math.max(-b.width,Math.min(0,shiftX));
var shiftY = 5;
this.setAttribute("transform","translate(" + shiftX + "," + shiftY + ")");
}
});
link.call(updateLink);
labelNode.call(updateNode);
}
function fixna(x) {
if (isFinite(x)) return x;
return 0;
}
function updateLink(link) {
link.attr("x1",function(d) {
return fixna(d.source.x);
})
.attr("y1",function(d) {
return fixna(d.source.y);
})
.attr("x2",function(d) {
return fixna(d.target.x);
})
.attr("y2",function(d) {
return fixna(d.target.y);
});
}
function updateNode(node) {
node.attr("transform",function(d) {
return "translate(" + fixna(d.x) + "," + fixna(d.y) + ")";
});
}
// convenience function to update everything (run after UI input)
var updateall = function() {
updateForces();
updatedisplay();
}
d3.selectAll("input").on("input",function() {
updateall();
});
带有强制选项的JSfiddle:https://jsfiddle.net/gothmogg/wh7px5oy/
欢迎任何输入。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)