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

如何用D3.js v5实现含核心大圈的模块化Swarm集群可视化?

Fixing D3.js Swarm Cluster Visualization for Your Dataset

Got it, let's get your swarm clusters working properly! The goal here is to create a separate cluster for each WFOE, with a large core circle (for the name/logo) surrounded by smaller product circles. Let's break down the solution to fix the issues you're facing—missing circles, broken variable radius, and non-functional force simulation.

Step 1: Prepare Your Node Data Correctly

First, we need to transform your raw dataset into a single array of nodes, where each WFOE has one core node and all its corresponding product nodes. This ensures we're creating all the circles you expect.

var data = [ 
  {'wfoe':'wfoe1','products':d3.range(20)}, 
  {'wfoe':'wfoe2','products':d3.range(40)}, 
  {'wfoe':'wfoe3','products':d3.range(10)} 
];

// Transform data into nodes array: core + product nodes
const nodes = [];
data.forEach(wfoeData => {
  // Add core node for the WFOE
  nodes.push({
    type: 'core',
    wfoe: wfoeData.wfoe,
    radius: 30 // Fixed size for core, or use a scale if needed
  });
  // Add product nodes for each product in the WFOE
  wfoeData.products.forEach(() => {
    nodes.push({
      type: 'product',
      wfoe: wfoeData.wfoe,
      radius: 8 // Fixed size for products, or use a scale here
    });
  });
});

Step 2: Configure the Force Simulation

The key to making clusters stay grouped is using custom forces:

  • A collision force to prevent circles from overlapping (uses the node radius, fixing your variable radius issue)
  • A link force to tie product nodes to their core (keeps products clustered around the core)
  • A charge force to repel nodes from each other (keeps separate clusters apart)
  • Initial positions for core nodes to spread them out initially
const width = 800;
const height = 600;

// Set initial positions for core nodes to spread them across the SVG
const coreNodes = nodes.filter(n => n.type === 'core');
coreNodes.forEach((core, index) => {
  core.x = (index + 1) * (width / (coreNodes.length + 1));
  core.y = height / 2;
});

// Create links: connect each product node to its WFOE's core
const getLinks = () => {
  const coreMap = new Map(coreNodes.map(c => [c.wfoe, c]));
  return nodes
    .filter(n => n.type === 'product')
    .map(product => ({ source: product, target: coreMap.get(product.wfoe) }));
};

// Initialize force simulation
const simulation = d3.forceSimulation(nodes)
  .force('charge', d3.forceManyBody()
    .strength(d => d.type === 'core' ? -600 : -150)) // Core nodes repel more strongly
  .force('collide', d3.forceCollide().radius(d => d.radius + 2)) // Respect node radii
  .force('link', d3.forceLink(getLinks()).distance(25)) // Keep products close to core
  .force('center', d3.forceCenter(width / 2, height / 2)); // Keep clusters centered

Step 3: Render the Nodes and Labels

Now we'll draw the circles and add labels to the core nodes. We'll also set up the tick event to update positions as the simulation runs.

// Create SVG container
const svg = d3.select('body')
  .append('svg')
  .attr('width', width)
  .attr('height', height);

// Draw circles for all nodes
const nodeElements = svg.selectAll('circle')
  .data(nodes)
  .enter()
  .append('circle')
  .attr('r', d => d.radius) // Uses the radius we defined earlier
  .attr('fill', d => d.type === 'core' ? '#2d3436' : '#0984e3');

// Add text labels to core nodes
const coreLabels = svg.selectAll('text')
  .data(coreNodes)
  .enter()
  .append('text')
  .text(d => d.wfoe)
  .attr('text-anchor', 'middle')
  .attr('fill', 'white')
  .style('font-size', '14px');

// Update positions on each simulation tick
simulation.on('tick', () => {
  nodeElements
    .attr('cx', d => d.x)
    .attr('cy', d => d.y);
  
  coreLabels
    .attr('x', d => d.x)
    .attr('y', d => d.y + 5); // Adjust vertical position for better alignment
});

Why Your Original Code Failed

  • Missing circles: You likely didn't iterate through all products to create individual nodes for each one.
  • Broken variable radius: The collision force wasn't configured to use each node's radius, or you didn't assign radius values to every node.
  • Non-functional simulation: You might have forgotten to bind the tick event to update element positions, or didn't set up forces that keep clusters grouped.

This solution will create distinct clusters for each WFOE, with the core node at the center and all product nodes surrounding it. You can tweak radii, colors, force strengths, and distances to match your design needs!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 08:17:40