问题
After following up from this question Insert text inside Circle in D3 chart
My nodes are sticking to the center. I am not sure which property is directing my nodes and their x and y coordinates. I recently chnaged my code to add a g layer to the circles so that i can append text along with shape.
DATA
https://api.myjson.com/bins/hwtj0
UPDATED CODE
async function d3function() {
d3.selectAll("svg > *").remove();
const svg = d3.select("svg");
file = document.getElementById("selectFile").value;
console.log("File: " + file)
var width = 900
var height = 900
svg.style("width", width + 'px').style("height", height + 'px');
data = (await fetch(file)).json()
d3.json(file).then(function(data) {
const links = data.links.map(d => Object.create(d));
const nodes = data.nodes.map(d => Object.create(d));
console.log(links.length);
console.log(nodes.length);
const simulation = forceSimulation(nodes, links).on("tick", ticked);
var categorical = [
{ "name" : "schemeAccent", "n": 8},
{ "name" : "schemeDark2", "n": 8},
]
// var colorScale = d3.scaleOrdinal(d3[categorical[6].name])
var color = d3.scaleOrdinal(d3[categorical[1].name]);
var drag = simulation => {
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.enter().append("line")
.attr("stroke-width", d => Math.sqrt(d.value));
// link.append("title").text(d => d.value);
// var circles = svg.append("g")
// .attr("stroke", "#fff")
// .attr("stroke-width", 1.5)
// .selectAll(".circle")
// .data(nodes)
// const node = circles.enter().append("circle")
// .attr("r", 5)
// .attr("fill", d => color(d.group))
// .call(drag(simulation));
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circles")
.data(nodes)
.enter()
.append("g")
.classed('circles', true)
.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');
node.append("circle")
.classed('circle', true)
.attr("r", 5)
.attr("fill", d => color(d.group))
.call(drag(simulation));
node
.append("text")
.classed('circleText', true)
.attr('dy', '0.35em')
.attr('dx', 5)
.text(d => "Node: " + d.id);
node.append("title").text(d => "Node: " + d.id);
function ticked() {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
}
});
}
function forceSimulation(nodes, links) {
return d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter());
}
UPDATED OUTPUT
EXPECTED OUTPUT
UPDATED HTML
<g stroke="#fff" stroke-width="1.5">
<g class="circle" cx="-35.89111508769784" cy="131.13965804447696">
<circle class="circle" r="5" fill="#1b9e77"></circle>
<text class="circleText" dy="0.35em" dx="5">Node: 0</text>
<title>Node: 0</title>
</g>
<g class="circle" cx="70.97799024729613" cy="-195.71408429254427">
<circle class="circle" r="5" fill="#d95f02"></circle>
<text class="circleText" dy="0.35em" dx="5">Node: 3</text>
<title>Node: 3</title>
</g>
[....]
</g>
回答1:
You have to adapt your code slightly as it currently assumes that you're working with circle elements, where you specify the centres using cx and cy, but you are now using g elements, which use standard x and y coordinates.
First, remove the transform from the g element (that's a leftover from my demo code):
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll(".circles") // note - should be .circles!
.data(nodes)
.enter()
.append("g")
.classed('circles', true)
and in the ticked() function, change the node updating code into a transform that works on g elements (which don't have cx or cy):
node.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')' )
Demo:
var json = {"nodes":[{"id":"0","group":0},{"id":"1","group":1},{"id":"2","group":2},{"id":"3","group":3},{"id":"4","group":4},{"id":"5","group":5},{"id":"6","group":6},{"id":"7","group":7},{"id":"8","group":8},{"id":"9","group":9},{"id":"10","group":10},{"id":"11","group":11},{"id":"12","group":12},{"id":"13","group":13},{"id":"14","group":14},{"id":"15","group":15},{"id":"16","group":16},{"id":"17","group":17},{"id":"18","group":18},{"id":"19","group":19}],"links":[{"source":"0","target":"1","value":1},{"source":"0","target":"18","value":1},{"source":"0","target":"10","value":1},{"source":"0","target":"12","value":1},{"source":"0","target":"5","value":1},{"source":"0","target":"8","value":1},{"source":"1","target":"0","value":1},{"source":"1","target":"9","value":1},{"source":"1","target":"4","value":1},{"source":"2","target":"4","value":1},{"source":"2","target":"17","value":1},{"source":"2","target":"13","value":1},{"source":"2","target":"15","value":1},{"source":"3","target":"6","value":1},{"source":"4","target":"14","value":1},{"source":"4","target":"2","value":1},{"source":"4","target":"5","value":1},{"source":"4","target":"19","value":1},{"source":"4","target":"1","value":1},{"source":"5","target":"4","value":1},{"source":"5","target":"0","value":1},{"source":"6","target":"3","value":1},{"source":"7","target":"18","value":1},{"source":"7","target":"16","value":1},{"source":"8","target":"0","value":1},{"source":"9","target":"1","value":1},{"source":"10","target":"0","value":1},{"source":"10","target":"15","value":1},{"source":"12","target":"0","value":1},{"source":"13","target":"15","value":1},{"source":"13","target":"2","value":1},{"source":"14","target":"4","value":1},{"source":"15","target":"13","value":1},{"source":"15","target":"10","value":1},{"source":"15","target":"2","value":1},{"source":"16","target":"7","value":1},{"source":"17","target":"2","value":1},{"source":"18","target":"0","value":1},{"source":"18","target":"7","value":1},{"source":"19","target":"4","value":1},{"source":"19","target":"4","value":1}]};
d3.selectAll("svg > *").remove();
const svg = d3.select("svg");
var width = 900
var height = 900
svg.style("width", width + 'px').style("height", height + 'px');
const links = json.links.map(d => Object.create(d));
const nodes = json.nodes.map(d => Object.create(d));
const simulation = forceSimulation(nodes, links).on("tick", ticked);
var categorical = [
{
"name": "schemeAccent",
"n": 8
},
{
"name": "schemeDark2",
"n": 8
}, ]
var color = d3.scaleOrdinal(d3[categorical[1].name]);
var drag = simulation => {
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.enter().append("line")
.attr("stroke-width", d => Math.sqrt(d.value));
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll(".circles")
.data(nodes)
.enter()
.append("g")
.classed('circles', true)
.call(drag(simulation))
// .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');
const circle = node.append("circle")
.classed('circle', true)
.attr("r", 5)
.attr("fill", d => color(d.group))
node
.append("text")
.classed('circleText', true)
.attr('dy', '0.35em')
.attr('dx', 5)
.text(d => "Node: " + d.id);
node.append("title").text(d => "Node: " + d.id);
function ticked() {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')')
}
function forceSimulation(nodes, links) {
return d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter());
}
.circleText { fill: black; stroke: none }
<script src="//d3js.org/d3.v5.js"></script>
<svg></svg>
来源:https://stackoverflow.com/questions/52804351/force-directed-graphs-nodes-stick-to-the-center