问题描述
我正在开发一个关于通过图形表示模式的项目。我是来从 json 中检索数据以生成图形并定位与节点关联的标签(我也想定位与链接对应的标签)。但是,在移动节点时,我无法移动标签。有什么帮助吗? See the code here:
var width = 960,height = 500,resolution = 150,r = 15;
var graph = {
"nodes": [
{"task": "1","label": "1","social": "I","id": 1,"x": 150,"y": 450},{"task": "2","label": "2","social": "G","id": 2,"x": 300,"y": 150},{"task": "3","label": "3","social": "T","id": 3,"x": 450,"y": 300}
],"links": [
{"source": "1","target": "2","type": "N:1"},{"source": "2","target": "3","type": "1:N"},{"source": "1","type": "1:1"}
]
}
var margin = {
top: -5,right: -5,bottom: -5,left: -5
};
var colors = d3.scale.category20();
var svg = d3.select('body').append('svg')
.attr('width',width)
.attr('height',height);
var force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([width + margin.left + margin.right,height + margin.top + margin.bottom])
.nodes(graph.nodes);
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on('drag',dragged);
svg.selectAll('.vertical')
.data(d3.range(1,width / resolution))
.enter().append('line')
.attr('class','vertical')
.attr('x1',function(d) { return d * resolution; })
.attr('y1',0)
.attr('x2',function(d) { return d * resolution; })
.attr('y2',height);
svg.selectAll('.horizontal')
.data(d3.range(1,height / resolution))
.enter().append('line')
.attr('class','horizontal')
.attr('x1',0)
.attr('y1',function(d) { return d * resolution; })
.attr('x2',width)
.attr('y2',function(d) { return d * resolution; });
svg.append('defs').append('marker')
.attr({'id':'arrowhead','viewBox':'-0 -5 10 10','refX':25,'refY':0,'orient':'auto','markerWidth':5,'markerHeight':5,'xoverflow':'visible'})
.append('svg:path')
.attr('d','M 0,-5 L 10,0 L 0,5')
.attr('fill','#777')
.style('stroke','none');
var link = svg.selectAll("svg.link")
.data(graph.links)
.enter().append("line")
.attr("class","link")
.attr('marker-end','url(#arrowhead)')
.attr("data-source",function (d) {
return d.source;
})
.attr("data-target",function (d) {
return d.target;
})
.attr("x1",function (d) {
for (let node of graph.nodes) {
if (node.task === d.source)
return node.x;
}
})
.attr("x2",function (d) {
for (let node of graph.nodes) {
if (node.task === d.target)
return node.x;
}
})
.attr("y1",function (d) {
for (let node of graph.nodes) {
if (node.task === d.source)
return node.y;
}
})
.attr("y2",function (d) {
for (let node of graph.nodes) {
if (node.task === d.target)
return node.y;
}
})
.style("stroke-width",function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll("svg.node")
.data(graph.nodes)
.enter().append('g');
node.append("circle")
.attr("class","node")
.attr('cx',function(d) { return d.x; })
.attr('cy',function(d) { return d.y; })
.attr('r',r)
.call(drag);//
//text element
node.append("title")
.text(function (d) {return d.social});
node.append("text")
//.attr("dx",)
//.attr("dy",6)
.attr("class","font")
.attr('dx',function(d) { return d.x-6; })
.attr('dy',function(d) { return d.y+6; })
.text(function(d) { return d.social; });
force.on("tick",function () {
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; });
node.attr("transform",function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
function dragged(d) {
var x = d3.event.x,y = d3.event.y,gridX = round(Math.max(r,Math.min(width - r,x)),resolution),gridY = round(Math.max(r,Math.min(height - r,y)),resolution);
d3.select(this).attr('cx',d.x = gridX).attr('cy',d.y = gridY);
d3.select(this).attr('dx',d.x = gridX).attr('dy',d.y = gridY);
d3.selectAll(`[data-source='${d.task}']`).attr('x1',d.x).attr('y1',d.y);
d3.selectAll(`[data-target='${d.task}']`).attr('x2',d.x).attr('y2',d.y);
}
function round(p,n) {
return p % n < n / 2 ? p - (p % n) : p + n - (p % n);
}
.node {
fill:#ccc;
stroke: #fff;
stroke-width: 2px;
}
.node-active {
stroke: #555;
stroke-width: 1.5px;
}
link-active {
stroke: #555;
stroke-opacity: 5;
}
line {
stroke: rgb(212,212,212);
stroke-width: 1px;
shape-rendering: crispEdges;
}
.overlay {
fill: none;
pointer-events: all;
}
.link{
stroke: #ccc;
stroke-width: 2px;
}
svg {
box-sizing: border-box;
border: 1px solid rgb(212,212);
}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>[D3]</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex,nofollow">
<meta name="googlebot" content="noindex,nofollow">
<meta name="viewport" content="width=device-width,initial-scale=1">
<body>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</body>
解决方法
向 <circle>
和 <text>
元素添加标识属性:
...attr('node-id',d => d.social)
随着圆圈移动文本:
d3.select(`text[node-id='${d.social}']`).attr('dx',d.x).attr('dy',d.y)
对 <text>
标签使用文本锚和对齐基线而不是偏移量 (+6/-6):
...attr('text-anchor','middle')
.attr('alignment-baseline','middle')
.attr('dx',function(d) { return d.x; })
.attr('dy',function(d) { return d.y; })
我还放大了 <circle>
以使其易于拖动...
var width = 960,height = 500,resolution = 150,r = 15;
var graph = {
"nodes": [
{"task": "1","label": "1","social": "I","id": 1,"x": 150,"y": 450},{"task": "2","label": "2","social": "G","id": 2,"x": 300,"y": 150},{"task": "3","label": "3","social": "T","id": 3,"x": 450,"y": 300}
],"links": [
{"source": "1","target": "2","type": "N:1"},{"source": "2","target": "3","type": "1:N"},{"source": "1","type": "1:1"}
]
}
var margin = {
top: -5,right: -5,bottom: -5,left: -5
};
var colors = d3.scale.category20();
var svg = d3.select('body').append('svg')
.attr('width',width)
.attr('height',height);
var force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([width + margin.left + margin.right,height + margin.top + margin.bottom])
.nodes(graph.nodes);
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on('drag',dragged);
svg.selectAll('.vertical')
.data(d3.range(1,width / resolution))
.enter().append('line')
.attr('class','vertical')
.attr('x1',function(d) { return d * resolution; })
.attr('y1',0)
.attr('x2',function(d) { return d * resolution; })
.attr('y2',height);
svg.selectAll('.horizontal')
.data(d3.range(1,height / resolution))
.enter().append('line')
.attr('class','horizontal')
.attr('x1',0)
.attr('y1',function(d) { return d * resolution; })
.attr('x2',width)
.attr('y2',function(d) { return d * resolution; });
svg.append('defs').append('marker')
.attr({'id':'arrowhead','viewBox':'-0 -5 10 10','refX':40,'refY':0,'orient':'auto','markerWidth':5,'markerHeight':5,'xoverflow':'visible'})
.append('svg:path')
.attr('d','M 0,-5 L 10,0 L 0,5')
.attr('fill','#777')
.style('stroke','none');
var link = svg.selectAll("svg.link")
.data(graph.links)
.enter().append("line")
.attr("class","link")
.attr('marker-end','url(#arrowhead)')
.attr("data-source",function (d) {
return d.source;
})
.attr("data-target",function (d) {
return d.target;
})
.attr("x1",function (d) {
for (let node of graph.nodes) {
if (node.task === d.source)
return node.x;
}
})
.attr("x2",function (d) {
for (let node of graph.nodes) {
if (node.task === d.target)
return node.x;
}
})
.attr("y1",function (d) {
for (let node of graph.nodes) {
if (node.task === d.source)
return node.y;
}
})
.attr("y2",function (d) {
for (let node of graph.nodes) {
if (node.task === d.target)
return node.y;
}
})
.style("stroke-width",function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll("svg.node")
.data(graph.nodes)
.enter().append('g');
node.append("circle")
.attr("class","node")
.attr('node-id',d => d.social)
.attr('cx',function(d) { return d.x; })
.attr('cy',function(d) { return d.y; })
.attr('r',r * 2)
.call(drag);//
node.append("text")
.attr("class","label")
.attr('node-id',d => d.social)
.attr('text-anchor','middle')
.attr('alignment-baseline','middle')
.attr('dx',function(d) { return d.x; })
.attr('dy',function(d) { return d.y; })
.text(function(d) { return d.social; });
force.on("tick",function () {
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; });
node.attr("transform",function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
function dragged(d) {
var x = d3.event.x,y = d3.event.y,gridX = round(Math.max(r,Math.min(width - r,x)),resolution),gridY = round(Math.max(r,Math.min(height - r,y)),resolution);
d3.select(this).attr('cx',d.x = gridX).attr('cy',d.y = gridY);
d3.select(this).attr('dx',d.x = gridX).attr('dy',d.y = gridY);
d3.selectAll(`[data-source='${d.task}']`).attr('x1',d.x).attr('y1',d.y);
d3.selectAll(`[data-target='${d.task}']`).attr('x2',d.x).attr('y2',d.y);
d3.select(`text[node-id='${d.social}']`).attr('dx',d.y)
}
function round(p,n) {
return p % n < n / 2 ? p - (p % n) : p + n - (p % n);
}
.node {
fill:#ccc;
stroke: #fff;
stroke-width: 2px;
}
.node-active {
stroke: #555;
stroke-width: 1.5px;
}
link-active {
stroke: #555;
stroke-opacity: 5;
}
line {
stroke: rgb(212,212,212);
stroke-width: 1px;
shape-rendering: crispEdges;
}
.overlay {
fill: none;
pointer-events: all;
}
.link{
stroke: #ccc;
stroke-width: 2px;
}
svg {
box-sizing: border-box;
border: 1px solid rgb(212,212);
}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>[D3]</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex,nofollow">
<meta name="googlebot" content="noindex,nofollow">
<meta name="viewport" content="width=device-width,initial-scale=1">
<body>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</body>