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

如何调整D3可缩放旭日图中最内层圆的尺寸?

这是个很实用的需求——要让旭日图的中心区域始终保持固定大小来展示悬停信息,确实得调整默认的D3分区布局逻辑。我来分享个可行的实现思路和代码示例:

核心思路

默认的d3.partition()布局会按比例分配每个层级的半径,缩放时整个视图会按比例缩小/放大。我们要做的是:

  • 强制当前层级的中心节点(也就是当前根节点)始终使用固定半径
  • 基于这个固定半径,重新计算外层节点的尺寸,保持层级间的相对比例
  • 独立维护一个中心信息元素,不受缩放逻辑影响
具体实现步骤
  1. 定义固定参数:先设定好中心圆的固定半径,以及旭日图的最大外层半径
  2. 重写布局计算:在每次更新布局(比如缩放层级)后,覆盖中心节点的半径值,并调整外层节点的尺寸范围
  3. 绑定交互事件:给节点添加悬停事件,将信息渲染到中心固定位置
  4. 平滑过渡:确保缩放时外层节点的动画过渡自然,中心圆保持稳定
代码示例
// 基础配置
const width = 800;
const height = 800;
const fixedInnerRadius = 60; // 你想要的中心固定尺寸
const outerRadius = Math.min(width, height) / 2;

// 创建SVG容器
const svg = d3.select('body')
  .append('svg')
  .attr('width', width)
  .attr('height', height)
  .append('g')
  .attr('transform', `translate(${width/2}, ${height/2})`);

// 初始化中心信息文本(始终固定在中心)
const centerInfo = svg.append('text')
  .attr('text-anchor', 'middle')
  .attr('dominant-baseline', 'middle')
  .style('font-size', '16px')
  .style('fill', '#333')
  .text('Hover over a node');

// 加载数据(这里用示例数据,替换成你的实际数据)
const data = {
  name: 'Root',
  children: [
    { name: 'Category 1', children: [{ name: 'Item 1a' }, { name: 'Item 1b' }] },
    { name: 'Category 2', children: [{ name: 'Item 2a' }, { name: 'Item 2b' }, { name: 'Item 2c' }] }
  ]
};

// 转换数据为分层结构
const root = d3.hierarchy(data)
  .sum(d => 1); // 按节点数量分配尺寸,可替换成你的数值字段

// 定义分区布局和弧生成器
const partition = d3.partition()
  .size([2 * Math.PI, outerRadius]);

const arc = d3.arc()
  .startAngle(d => d.x0)
  .endAngle(d => d.x1)
  .innerRadius(d => d.r0)
  .outerRadius(d => d.r1);

// 更新函数:处理布局调整和渲染
function update(currentRoot) {
  // 先执行默认分区计算
  partition(currentRoot);

  // 强制中心节点(当前根)使用固定半径,调整外层节点尺寸
  currentRoot.each(d => {
    if (d.depth === 0) {
      // 当前根节点(中心)固定尺寸
      d.r0 = 0;
      d.r1 = fixedInnerRadius;
    } else {
      // 外层节点基于中心固定半径重新计算范围
      const parentOuter = d.parent.r1;
      // 保留原布局的比例关系
      const sizeRatio = (d.r1 - d.r0) / outerRadius;
      d.r0 = parentOuter;
      d.r1 = parentOuter + (outerRadius - fixedInnerRadius) * sizeRatio;
    }
  });

  // 绑定数据并更新路径
  const paths = svg.selectAll('path')
    .data(currentRoot.descendants().filter(d => d.depth > 0)); // 过滤中心节点,单独渲染背景圆

  // 新增节点
  paths.enter()
    .append('path')
    .attr('fill', (d, i) => d3.schemeCategory10[i % 10])
    .attr('d', arc)
    .on('mouseover', (e, d) => {
      // 悬停时更新中心信息
      centerInfo.text(`Name: ${d.data.name}\nDepth: ${d.depth}`);
    })
    .on('mouseout', () => {
      centerInfo.text('Hover over a node');
    })
    .on('click', (e, d) => {
      // 点击节点缩放层级
      update(d);
    })
    .merge(paths)
    .transition()
    .duration(750)
    .attr('d', arc);

  // 移除多余节点
  paths.exit().remove();

  // 渲染中心固定背景圆(可选,用来突出中心区域)
  svg.selectAll('.center-circle')
    .data([currentRoot])
    .enter()
    .append('circle')
    .attr('class', 'center-circle')
    .merge(svg.select('.center-circle'))
    .attr('r', fixedInnerRadius)
    .attr('fill', '#fff')
    .attr('stroke', '#ccc');
}

// 初始渲染
update(root);
额外提示
  • 如果你的数据是按数值权重分配尺寸,只需要把d3.hierarchy(data).sum(d => 1)里的1替换成对应的数值字段(比如d.value)即可
  • 中心信息可以扩展成更复杂的DOM元素(比如分组包含图标、多行文本),只要保持它的位置固定在SVG中心
  • 可以调整过渡时间(duration(750))来匹配你的交互体验需求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:57:14