我已经创建了一个连接的节点强制布局图,但是想更进一步,并在每个节点(团队)中插入节点(玩家)(类似于背包布局)
虽然在模拟开始后我已经成功地提取了每个节点(团队)的位置并将节点(玩家)置于各自团队的中心,但是我无法创建具有碰撞/抖动的间隔开的节点(玩家)在每个节点(团队)中。
我尝试再次运行新的模拟以获得碰撞效果,但是它不起作用。
到目前为止已完成的工作:https://bl.ocks.org/dianaow/722db505e9971b50b575ef0051ea0295
相关代码:
var team_stats1 = d3.nest()
.key(function(d) { return d.club })
.entries(data)
enter()
// Initialize force simulation
var simulation = d3.forceSimulation()
.force("link", d3.forceLink()
.id(function(d) { return d.id; })
.strength(function(d) {return d.strength})
)
.force("collide", d3.forceCollide().radius(function(d) { return d.size }))
simulation
.nodes(nodes)
.on("tick", update)
simulation.force("link")
.links(links)
// Initialize second force simulation just to get collision between nodes(players) within each node(team)
var simulation1 = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(-300))
.force("collide", d3.forceCollide().radius(function(d) { return d.size }))
function enter() {
path = svg.selectAll('line')
.data(links).enter().append('line')
.attr('stroke-linecap', 'round')
circle = svg.selectAll('circle')
.data(nodes)
.enter().append('circle')
.attr('stroke-width', 2)
circle.each(function (nodeElement) {
if(countries.indexOf(nodeElement.id) < 0){
var idx = nodeElement.id.replace(/[^A-Z0-9]+/ig, "_")
var data_filtered = team_stats1.filter(d=>d.key==nodeElement.id)[0].values
svg.selectAll('#' + idx + '.team_players')
.data(data_filtered)
.enter().append('circle')
.attr("class", "team_players")
.attr('id', idx)
.attr("r", 3)
.attr("fill", 'grey')
.attr("stroke", 'none')
.attr("stroke-width", 0)
}
})
}
function update() {
path.attr('stroke-width', function(d) {return d.size})
.attr('stroke', function(d) {return d.stroke})
.attr('fill', function(d) {return d.stroke})
.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})
circle.attr('r', function(d) {return d.size})
.attr('fill', function(d) {return d.fill || '#fff'})
.attr('cx', function(d) {return d.x})
.attr('cy', function(d) {return d.y})
.attr('id', function(d) {return d.id.replace(/[^A-Z0-9]+/ig, "_")}) // remove spaces because they cannot be contained in class names
circle.each(function (nodeElement) {
if(countries.indexOf(nodeElement.id) < 0){ // filter out the country nodes around team nodes
var idx = nodeElement.id.replace(/[^A-Z0-9]+/ig, "_")
var data_filtered = team_stats1.filter(d=>d.key==nodeElement.id)[0].values
data_filtered.forEach((d,i) => {
d.x = nodeElement.x
d.y = nodeElement.y
})
var player = svg.selectAll('#' + idx + '.team_players')
.data(data_filtered)
.attr('cx', function(d) {return d.x})
.attr('cy', function(d) {return d.y})
simulation1
.nodes(data_filtered)
.on("tick", updatePlayers)
function updatePlayers() {
player.attr('cx', function(d) {return d.x})
.attr('cy', function(d) {return d.y})
}
}
})
}