【问题标题】:Fix the position of one bubble in d3 pack layout修复 d3 包布局中一个气泡的位置
【发布时间】:2020-07-09 12:29:18
【问题描述】:

我正在使用 D3 js (v5) 构建气泡图,以显示一些研究结果。
基本上,我有一个 JSON,其中总作为根,子作为每个结果。

其中一个结果必须突出显示并位于圆圈的顶部中心。 我设法在那里展示了这个结果,但其他气泡是重叠的(可能是因为我改变了 D3 层次算法提供的位置)。

是否可以以编程方式仅固定一个气泡(在我的示例中为蓝色气泡)的位置并重新定位其他气泡,以免它们相互重叠?

JFiddle

const data = {
  name: 'Total',
  size: 1999999,
  children: [{
      name: 'Result A',
      size: 69936,
    },
    {
      name: 'Result b',
      size: 45000,
    },
    {
      name: 'Result C',
      size: 250000,
    },
    {
      name: 'Result D',
      size: 426791,
    },
    {
      name: 'Result E',
      size: 56000,
    },
    {
      name: 'Result F',
      size: 61050,
    },
    {
      name: 'Result G',
      size: 30000,
    },
  ],
};

// Fix this bubble at the top
const FIXED_BUBBLE_NAME = 'Result b';

const GREEN = '#90E0C2';
const BLUE = '#73A1FC';

const GRAPH_DIMENSIONS = {
  WIDTH: 234,
  HEIGHT: 234,
  PADDING: 10,
};

const buildDataTree = () => {
  const packLayout = d3
    .pack()
    .size([
      GRAPH_DIMENSIONS.WIDTH,
      GRAPH_DIMENSIONS.HEIGHT,
    ])
    .padding(GRAPH_DIMENSIONS.PADDING);

  const rootNode = d3
    .hierarchy(data)
    .sum((d) => d.size)
    .sort((a, b) => {
      return b.value - a.value;
    })

  return packLayout(rootNode);
};

const getSvgRoot = () => {
  return d3
    .select('#graph-container')
    .append('svg')
    .attr('id', 'graph-container-svg')
    .attr('width', GRAPH_DIMENSIONS.WIDTH + GRAPH_DIMENSIONS.PADDING)
    .attr('height', GRAPH_DIMENSIONS.HEIGHT + GRAPH_DIMENSIONS.PADDING)
    .style('overflow', 'visible');
};


const rootNodeDataTree = buildDataTree();

const svgRoot = getSvgRoot();

const node = svgRoot
  .selectAll('g')
  .data(
    d3
    .nest()
    .key((d) => d.id)
    .entries(rootNodeDataTree.descendants()),
  )
  .join('g')
  .selectAll('g')
  .data(d => d.values)
  .join('g');

node
  .append('circle')
  .attr('r', (d) => d.r)
  .attr('cx', (d) => {
    // if it is the selected bubble it must be at the center
    if (d.data.name === FIXED_BUBBLE_NAME) {
      return GRAPH_DIMENSIONS.WIDTH / 2 + d.r / 2;
    }
    return d.x + GRAPH_DIMENSIONS.PADDING;
  })
  .attr('cy', (d) => {
    // if it is the selected bubble it must be at the center
    if (d.data.name === FIXED_BUBBLE_NAME) {
      return d.r + GRAPH_DIMENSIONS.PADDING * 2;
    }
    return d.y + GRAPH_DIMENSIONS.PADDING;
  })
  .attr('cursor', 'pointer')
  .attr('stroke', (d) => (d.children ? GREEN : ''))
  .attr('stroke-width', (d) => (d.children ? 2 : 0))
  .attr('fill-opacity', (d) => (d.children ? 0.24 : 1))
  .attr('fill', (d) => {
    if (d.data.name === FIXED_BUBBLE_NAME) {
      return BLUE;
    } else {
      return GREEN;
    }
  })
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="graph-container">

</div>

【问题讨论】:

  • 你熟悉d3力模拟吗?在打包计算之后,我会运行一个带有碰撞检测的力模拟来修复你想要的节点,所以模拟应该重新安排其他节点
  • 我不熟悉,但我会看看,谢谢@GuillermoGarcia的提示
  • 我会试一试,会告诉你的
  • 您是否期望拥有重要的圈数?旋转整个图表以使蓝色圆圈位于顶部将是一个简单的解决方案。
  • @Mehdi 在Guilhermo 的回答之前我正在尝试哈哈,我一直在尝试将蓝色气泡放在两侧并将圆圈从气泡位置旋转到顶部。它确实会起作用。谢谢

标签: javascript d3.js charts bubble-chart


【解决方案1】:

这是我的建议:https://jsfiddle.net/9r8kt1e0/1/

力模拟代码为:

const simulation = d3
  .forceSimulation(allNodes.filter( (d) => d.depth !== 0))
  .force("collide",d3.forceCollide(d => { return d.r;}))
  .force("charge", d3.forceManyBody().strength(0))
  .force("center", d3.forceCenter(GRAPH_DIMENSIONS.WIDTH/2+10, GRAPH_DIMENSIONS.HEIGHT/2))
  .force("bound", (alpha) => {
    for (let i = 0; i < allNodes.length; ++i) {
      n = allNodes[i];
      n.x = pythag(n.r, n.y, n.x);
      n.y = pythag(n.r, n.x, n.y);
    }
  })
  .stop();

for (var i = 0; i < 100; ++i) {
  simulation.tick();
}

请注意,我们正在传递一个名为 bound 的自定义力,这个力应该负责将节点保持在父圆内。

这样就解决了重叠问题,您需要根据自己的需要对力量进行一些调整。

根据文档,力是一个修改节点位置的函数,你可以有多个力,所以希望稍微玩一下力应该可以解决你的问题。

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-20
    • 1970-01-01
    • 1970-01-01
    • 2018-12-24
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    相关资源
    最近更新 更多