在我的力有向圖中,我希望我的每個節點都有:
- 兩個圈子。
- 兩個圓圈的大小必須相同。
- 圓圈需要在彼此的頂部,以便視覺上我們只能看到一個。
- 下面的圓圈是有紅色填充的圓圈。
- 頂部的圓圈是填充 url 的圓圈。
我究竟做錯了什么 ?
const node = svg.append("g")
.attr("stroke", nodeStroke)
.attr("stroke-opacity", nodeStrokeOpacity)
.attr("stroke-width", nodeStrokeWidth)
.selectAll("circle")
.data(nodes)
.join("circle")
.style("fill", "red")
.attr("r", 30)
node.selectAll("circle")
.data(nodes)
.join("circle")
.style("fill", d => `url(#${d.id})`)
.attr("r", 30)
.call(drag(simulation))
編輯 2 在嘗試將建議的解決方案應用到我的代碼之后,我設法創建了我想要的圓圈。剩下的問題是我的圈子似乎都堆疊在我的 svg 的中心,因為我的拖動(模擬)不再適用于圈子。
下面是我的代碼的螢屏截圖和更詳細的部分:

ForceGraph(
nodes, // an iterable of node objects (typically [{id}, …])
links // an iterable of link objects (typically [{src, target}, …])
){
var nodeId = d => d.id // given d in nodes, returns a unique identifier (string)
const nodeStrength = -450 // -1750
const linkDistance = 100
const linkStrokeOpacity = 1 // link stroke opacity
const linkStrokeWidth = 3 // given d in links, returns a stroke width in pixels
const linkStrokeLinecap = "round" // link stroke linecap
const linkStrength =1
var width = this.$refs.mapFrame.clientWidth // scale to parent container
var height = this.$refs.mapFrame.clientHeight // scale to parent container
const N = d3.map(nodes, nodeId);
// Replace the input nodes and links with mutable objects for the simulation.
nodes = nodes.map(n => Object.assign({}, n));
links = links.map(l => ({
orig: l,
//Object.assign({}, l)
source: l.src,
target: l.target
}));
// Construct the forces.
const forceNode = d3.forceManyBody();
const forceLink = d3.forceLink(links).id(({index: i}) => N[i]);
forceNode.strength(nodeStrength);
forceLink.strength(linkStrength);
forceLink.distance(linkDistance)
const simulation = d3.forceSimulation(nodes)
.force(link, forceLink)
.force("charge", forceNode)
.force("x", d3.forceX())
.force("y", d3.forceY())
.on("tick", ticked);
const svg = d3.create("svg")
.attr("id", "svgId")
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("viewBox", [-width/2,-height/2, width,height])
.classed("svg-content-responsive", true)
const defs = svg.append('svg:defs');
defs.selectAll("pattern")
.data(nodes)
.join(
enter => {
// For every new <pattern>, set the constants and append an <image> tag
const patterns = enter
.append("pattern")
.attr("preserveAspectRatio", "none")
.attr("viewBox", [0,0, 100,100])
.attr("width", 1)
.attr("height", 1);
patterns
.append("image")
.attr("width", 80)
.attr("height", 80)
.attr("x", 10)
.attr("y", 10);
return patterns;
}
)
// For every <pattern>, set it to point to the correct
// URL and have the correct (company) ID
.attr("id", d => d.id)
.select("image")
.datum(d => {
return d;
})
.attr("xlink:href", d => {
return d.image
})
const link = svg.append("g")
.attr("stroke-opacity", linkStrokeOpacity)
.attr("stroke-width", linkStrokeWidth)
.attr("stroke-linecap", linkStrokeLinecap)
.selectAll("line")
.data(links)
.join("line")
;
link.attr("stroke", "white")
var node
var group = svg
.selectAll(".circle-group")
.data(nodes)
.join(enter => {
node = enter.append("g")
.attr("class", "circle-group");
node.append("circle")
.attr("class", "background") // classes aren't necessary here, but they can help with selections/styling
.style("fill", "red")
.attr("r", 30);
node.append("circle")
.attr("class", "foreground") // classes aren't necessary here, but they can help with selections/styling
.style("fill", d => `url(#${d.id})`)
.attr("r", 30)
}).call(drag(simulation))
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 drag(simulation) {
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
return Object.assign(svg.node() );
}//forcegraph
uj5u.com熱心網友回復:
正如 Andrew Reid 所暗示的,由于您想將多個圓圈添加到同一個節點,您可能希望每個節點(g元素)使用一個 svg 組元素來執行此操作。從那里您可以通過重復選擇相同的組并附加到它來添加多個圓圈。
沒有更新
例如,這是一個使用預填充影像的簡單示例,由 Wikimedia Commons 提供。它適用于您只設定一次資料的情況:
var nodeStroke = 'black';
var nodeStrokeWidth = 5;
var nodeStrokeOpacity = 0.8;
var nodes = [{id: 'image-1' }, {id: 'image-2' }];
const svg = d3.select('svg')
.attr("stroke", nodeStroke)
.attr("stroke-opacity", nodeStrokeOpacity)
.attr("stroke-width", nodeStrokeWidth);
var group = svg
.selectAll()
.data(nodes)
.join("g")
.attr("class", "node")
.attr("transform", (d,i)=>`translate(${i * 100 50},100)`);
var background = group.append("circle")
.attr("class", "background") // classes aren't necessary here, but they can help with selections/styling
.style("fill", "red")
.attr("r", 30)
var foreground = group.append("circle")
.attr("class", "foreground") // classes aren't necessary here, but they can help with selections/styling
.style("fill", d => `url(#${d.id})`)
.attr("r", 30)
// can also start drag simulation here:
// group.call(drag(simulation));
<script src="https://d3js.org/d3.v7.min.js"></script>
<svg>
<defs>
<pattern id="image-1" x="30" y="30" patternUnits="userSpaceOnUse" height="60" width="60">
<image x="-44" y="-30" xlink:href="https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Mona_Lisa.jpg/158px-Mona_Lisa.jpg"></image>
</pattern>
<pattern id="image-2" x="30" y="30" patternUnits="userSpaceOnUse" height="60" width="60">
<image x="-40" y="-30" xlink:href="https://upload.wikimedia.org/wikipedia/commons/thumb/9/90/Monet_w1709.jpg/163px-Monet_w1709.jpg"></image>
</pattern>
</defs>
</svg>
有更新
更新資料時,您只想向新元素添加圓圈。在這里,我們可以將圓圈附加到join(輸入函式)的第一個函式引數中。
var nodeStroke = 'black';
var nodeStrokeWidth = 5;
var nodeStrokeOpacity = 0.8;
// if using a drag simulation, may need to have only one instance of it:
// var dragSimulation = drag(simulation);
const svg = d3.select('svg')
.attr("stroke", nodeStroke)
.attr("stroke-opacity", nodeStrokeOpacity)
.attr("stroke-width", nodeStrokeWidth);
function updateData(nodes) {
var group = svg
.selectAll(".circle-group")
.data(nodes)
.join(enter => {
var newNodes = enter.append("g")
.attr("class", "circle-group");
newNodes.append("circle")
.attr("class", "background") // classes aren't necessary here, but they can help with selections/styling
.style("fill", "red")
.attr("r", 30);
newNodes.append("circle")
.attr("class", "foreground") // classes aren't necessary here, but they can help with selections/styling
.style("fill", d => `url(#${d.id})`)
.attr("r", 30)
// if using the drag simulation:
// newNodes.call(dragSimulation);
return newNodes;
})
.attr("transform", (d,i)=>`translate(${i * 100 50},100)`);
}
var data = [{id: 'image-1' }, {id: 'image-2' }];
updateData(data);
setTimeout(function () {
data.push({id: 'image-1' });
updateData(data);
}, 2000);
<script src="https://d3js.org/d3.v7.min.js"></script>
<svg>
<defs>
<pattern id="image-1" x="30" y="30" patternUnits="userSpaceOnUse" height="60" width="60">
<image x="-44" y="-30" xlink:href="https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/Mona_Lisa.jpg/158px-Mona_Lisa.jpg"></image>
</pattern>
<pattern id="image-2" x="30" y="30" patternUnits="userSpaceOnUse" height="60" width="60">
<image x="-40" y="-30" xlink:href="https://upload.wikimedia.org/wikipedia/commons/thumb/9/90/Monet_w1709.jpg/163px-Monet_w1709.jpg"></image>
</pattern>
</defs>
</svg>
uj5u.com熱心網友回復:
這是預期的行為:
node是一個圓圈的選擇 - 你的代碼塊在這里:
svg.append("g")
.attr("stroke", nodeStroke)
.attr("stroke-opacity", nodeStrokeOpacity)
.attr("stroke-width", nodeStrokeWidth)
.selectAll("circle")
.data(nodes)
.join("circle")
.style("fill", "red")
.attr("r", 30)
最終回傳一個選擇的圓圈,這就是nodes所指的。第二個 selectAll 陳述句嘗試將一個圓圈附加到此選擇的圓圈中。圓形 SVG 元素不能包含子圓形元素,因為這是無效的語法,因此這些子圓形都不會呈現。
如果我們打破這個鏈,我們可以選擇一個父節點g來附加兩組圓:
const g = svg.append("g")
.attr("stroke", nodeStroke)
.attr("stroke-opacity", nodeStrokeOpacity)
.attr("stroke-width", nodeStrokeWidth)
然后我們可以使用 g.selectAll() 來確保我們將圓圈附加到合法父級,即g. 但是,如果我們不稍微調整您的代碼,您將只會得到一組圓圈,因為第二個 selectAll() 陳述句將選擇在第一個 selectAll() 陳述句之后添加的所有圓圈:第一次執行此操作時,您沒有圓圈,所有圓圈都被輸入/附加。第二次執行此操作時,您有圈子,資料陣列中的每個專案都有一個圈子,因此不會附加新圈子。
您可以應用類名來區分 selectAll 陳述句:
.selectAll(".circleA")
.data(...)
.join("circle")
.attr("class", "circleA")
但是,如果您從不打算添加圈子并且不修改其資料,則可以使用.selectAll("null").
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/462647.html
標籤:d3.js
