You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

D3力导向图节点未按规则分布:节点重叠问题求助

解决D3.js力导向图节点重叠与排列异常问题

核心问题分析

你的代码存在几个关键问题,导致节点重叠、排列不符合预期:

  1. 节点位置更新方式错误:选择的是<g>元素,却尝试设置cx/cy属性——这两个属性仅适用于<circle>/<ellipse>等图形元素,<g>元素需要通过transform属性更新位置。
  2. 碰撞力间距不足:forceCollide仅使用了节点自身半径,未预留额外间距,容易导致相邻节点挤压重叠。
  3. 节点初始位置未初始化:模拟启动时节点无初始坐标,初始阶段混乱会干扰最终排列效果。
  4. 文本属性引用错误:第二个文本元素使用了d.r,但节点数据中不存在r字段,应替换为rScale(d.Count)。
  5. 力的强度不足:forceY的拉力强度不够,节点难以贴合Count对应的Y轴位置。

修复后的完整代码

test()

function test() {
        var nodes = [
                {"Name":"A","Count":10},
                {"Name":"B","Count":8},
                {"Name":"C","Count":6},
                {"Name":"D","Count":5},
                {"Name":"E","Count":4},
                {"Name":"F","Count":4},
                {"Name":"G","Count":2},
                {"Name":"H","Count":7},
        ]
        
        var width = 600
        var height = 600
        var margin = 100
        var color = d3.scaleOrdinal(d3.schemeCategory10)
        
        let extentCount = d3.extent(nodes, d => d.Count)            
        let maxRadius = 50 
        let collisionPadding = 5 // 碰撞预留间距
        
        let yScale = d3.scaleLinear()
            .domain(extentCount)
            .range([height - maxRadius, maxRadius])
        
        let rScale = d3.scaleSqrt()
            .domain(extentCount)
            .range([5, maxRadius])
        
        // 初始化节点初始位置
        nodes.forEach(d => {
            d.x = width / 2
            d.y = yScale(d.Count)
        })
        
        var svg = d3.select("body")
                .append("svg")
                .attr("width", width + margin + margin)
                .attr("height", height + margin + margin)
                .attr("class", "bubble")
                .style('border','2px solid red')
                .style('background','#ececec')
        
        var g = svg.append("g")
            .attr('transform', `translate(${margin},${margin})`)

        var simulation = d3.forceSimulation(nodes)
                .force("forceX", d3.forceX().strength(0.1).x(width/2))
                .force("forceY", d3.forceY().strength(0.2).y(d => yScale(d.Count)))
                .force('collision', d3.forceCollide().radius(d => rScale(d.Count) + collisionPadding))

        var node = g.selectAll(".node")
                .data(nodes)
                .enter()
                .append("g")
                .attr("class", "node")
                .attr("transform", function(d) {
                        return `translate(${d.x},${d.y})`;
                });

        node.append("circle")
                .attr("r", d => rScale(d.Count))
                .style("fill", function(d,i) {
                        return color(i);
                });

        node.append("text")
                .attr("dy", ".2em")
                .style("text-anchor", "middle")
                .text(function(d) {
                        return d.Name.substring(0, rScale(d.Count) / 3);
                })
                .attr("font-family", "sans-serif")
                .attr("font-size", function(d){
                        return rScale(d.Count)/5;
                })
                .attr("fill", "white");

        node.append("text")
                .attr("dy", "1.3em")
                .style("text-anchor", "middle")
                .text(function(d) {
                        return d.Count;
                })
                .attr("font-family",  "Gill Sans", "Gill Sans MT")
                .attr("font-size", function(d){
                        return rScale(d.Count)/5;
                })
                .attr("fill", "white");

        function ticked() {
            node.attr("transform", d => `translate(${d.x},${d.y})`)
        }
        
        simulation.on("tick",ticked)
        simulation.alpha(1).restart()
}
<script src="http://d3js.org/d3.v7.min.js" charset="utf-8"></script>

关键修复点说明

  • 修正位置更新逻辑:将cx/cy属性替换为transform,正确更新<g>元素的位置。
  • 添加碰撞间距:在forceCollide的半径计算中加入固定间距,避免节点边缘紧贴重叠。
  • 初始化节点坐标:提前给每个节点设置初始的居中X坐标和对应Count的Y坐标,让模拟从有序状态启动。
  • 调整力的强度:增大forceY的拉力强度,确保节点贴合预期的Y轴位置;小幅提升forceX强度,保证节点居中排列。
  • 修复文本引用错误:将无效的d.r替换为rScale(d.Count),确保文本大小正常显示。

内容的提问来源于stack exchange,提问作者lucky1928

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 17:00:09