问题
I have the following code in my Codepen that's able to render the sprites flags (from CSS), yet it's not in the position where it should be based on the nodes's position.
In fact, I am able to drag the nodes from the flags sprites from the top!
Looking at some console.logs, it seems that d.x
and d.y
position from function ticked()
is not passing when the nodes are called.
I am not sure how to fix this issue
const width = w - (margin.left + margin.right);
const height = h - (margin.top + margin.bottom);
let flagNodes = d3.select("#canvas")
.append("div")
.classed("flag-nodes",true)
let svg = d3.select("#canvas")
.append("svg")
.attr("id","chart")
.attr("width", w)
.attr("height", h)
let chart = svg.append("g")
.classed("display", true)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
let simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d,i) {
return i;
}))
.force("charge", d3.forceManyBody().strength(-4))
.force("center", d3.forceCenter(width/2, height/2))
let node = flagNodes.selectAll(".flag-nodes")
.data(data.nodes)
.enter()
.append("div")
.attr("class", function(d,i){
return `flag flag-${d.code}`
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
)
let link = chart.append("g")
.classed("links",true)
.selectAll("line")
.data(data.links)
.enter()
.append("line")
node.append("title")
.text(function(d) { return d.country; });
simulation
.nodes(data.nodes)
.on("tick", ticked);
simulation.force("link")
.links(data.links);
//functions provided by D3.js
//
function ticked() {
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
.style("left", function(d) {return d.x})
.style("top", function(d) {return d.y});
}
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;
}
Codepen
回答1:
You are super close! This took me quite a while to figure out.
I ended up creating a flag folder filled with country pngs in my project directory. Then dynamically loaded them using the country code.
Here is how I solved the problem:
const node = svg.selectAll(".flag")
.data(data.nodes)
.enter().append("image")
.attr("xlink:href", d => require(`./flag/${d.code}.png`))
.attr("width", radius)
.attr("height", radius)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
来源:https://stackoverflow.com/questions/39908583/d3-js-labeling-nodes-with-image-in-force-layout