开发预览 更新于 2026-05-10

SunburstChart 旭日图

多层径向树形分区图,从内圈到外圈代表父→子层级,扇形面积 ∝ 值。

何时使用

  • 多层级占比:流量来源(渠道 → 平台)、成本归因(事业部 → 项目 → 任务)、文件系统大小(目录 → 子目录 → 文件)。
  • CfTreemap 的差异:Treemap 是矩形 slice-and-dice,单层;Sunburst 是径向,支持任意深度
  • CfDonutChart 的差异:DonutChart 是单环;Sunburst 是多环嵌套。
  • 不建议层级 > 4:外圈扇形过小、标签放不下,转用 Treemap 或 ScatterPlot 更可读。

基础用法

数据是一棵 SunburstNode 树。叶子节点提供 value,父节点的值=子节点求和。colorIndex 从内圈向外圈继承,外圈只能看不到时给 root 的孩子上色就够了。

背景 视口
搜索GoogleBingBaidu社交TwitterLinkedInReddit直访AppBookmark引荐BlogPartnerOther搜索GoogleBingBaidu社交TwitterLinkedInReddit直访AppBookmark引荐BlogPartner
src/App.vue
<script setup lang="ts">
import { CfSunburstChart } from '@chufix-design/vue';

const root = {
  name: '总流量',
  children: [
    {
      name: '搜索',
      colorIndex: 0,
      children: [
        { name: 'Google', value: 420 },
        { name: 'Bing', value: 80 },
        { name: 'Baidu', value: 60 },
      ],
    },
    {
      name: '社交',
      colorIndex: 1,
      children: [
        { name: 'Twitter', value: 180 },
        { name: 'LinkedIn', value: 90 },
        { name: 'Reddit', value: 60 },
      ],
    },
    {
      name: '直访',
      colorIndex: 2,
      children: [
        { name: 'App', value: 240 },
        { name: 'Bookmark', value: 120 },
      ],
    },
    {
      name: '引荐',
      colorIndex: 3,
      children: [
        { name: 'Blog', value: 70 },
        { name: 'Partner', value: 50 },
        { name: 'Other', value: 30 },
      ],
    },
  ],
};
</script>
<template>
  <CfSunburstChart :root="root" :size="320" />
</template>
<script setup>
import { CfSunburstChart } from '@chufix-design/vue';

const root = {
  name: '总流量',
  children: [
    {
      name: '搜索',
      colorIndex: 0,
      children: [
        { name: 'Google', value: 420 },
        { name: 'Bing', value: 80 },
        { name: 'Baidu', value: 60 },
      ],
    },
    {
      name: '社交',
      colorIndex: 1,
      children: [
        { name: 'Twitter', value: 180 },
        { name: 'LinkedIn', value: 90 },
        { name: 'Reddit', value: 60 },
      ],
    },
    {
      name: '直访',
      colorIndex: 2,
      children: [
        { name: 'App', value: 240 },
        { name: 'Bookmark', value: 120 },
      ],
    },
    {
      name: '引荐',
      colorIndex: 3,
      children: [
        { name: 'Blog', value: 70 },
        { name: 'Partner', value: 50 },
        { name: 'Other', value: 30 },
      ],
    },
  ],
};
</script>
<template>
  <CfSunburstChart :root="root" :size="320" />
</template>
import { CfSunburstChart } from '@chufix-design/react';

export default function Demo() {
  const root = {
    name: '总流量',
    children: [
      {
        name: '搜索',
        colorIndex: 0,
        children: [
          { name: 'Google', value: 420 },
          { name: 'Bing', value: 80 },
          { name: 'Baidu', value: 60 },
        ],
      },
      {
        name: '社交',
        colorIndex: 1,
        children: [
          { name: 'Twitter', value: 180 },
          { name: 'LinkedIn', value: 90 },
          { name: 'Reddit', value: 60 },
        ],
      },
      {
        name: '直访',
        colorIndex: 2,
        children: [
          { name: 'App', value: 240 },
          { name: 'Bookmark', value: 120 },
        ],
      },
      {
        name: '引荐',
        colorIndex: 3,
        children: [
          { name: 'Blog', value: 70 },
          { name: 'Partner', value: 50 },
          { name: 'Other', value: 30 },
        ],
      },
    ],
  };
  return (
    <>
      <CfSunburstChart root={root} size={320} />
    </>
  );
}
import { CfSunburstChart } from '@chufix-design/react';

export default function Demo() {
  const root = {
    name: '总流量',
    children: [
      {
        name: '搜索',
        colorIndex: 0,
        children: [
          { name: 'Google', value: 420 },
          { name: 'Bing', value: 80 },
          { name: 'Baidu', value: 60 },
        ],
      },
      {
        name: '社交',
        colorIndex: 1,
        children: [
          { name: 'Twitter', value: 180 },
          { name: 'LinkedIn', value: 90 },
          { name: 'Reddit', value: 60 },
        ],
      },
      {
        name: '直访',
        colorIndex: 2,
        children: [
          { name: 'App', value: 240 },
          { name: 'Bookmark', value: 120 },
        ],
      },
      {
        name: '引荐',
        colorIndex: 3,
        children: [
          { name: 'Blog', value: 70 },
          { name: 'Partner', value: 50 },
          { name: 'Other', value: 30 },
        ],
      },
    ],
  };
  return (
    <>
      <CfSunburstChart root={root} size={320} />
    </>
  );
}

钻取 / drill-down

drillable 默认为 true。点击任意有子节点的扇形 → 该子树成为新根重新渲染;面包屑显示当前路径,中心圆形按钮(↑)或顶部面包屑都能回到上层。监听 @drill 拿到当前焦点节点 + 完整路径。

焦点切换时整层做一次淡出 + 微缩动画(200ms),新焦点淡入 + 放大,营造”逐层钻取”的视觉过渡。prefers-reduced-motion: reduce 时动效自动关闭。

背景 视口

点击外环任意分类 → 该子树成为新焦点;中心 ↑ 或顶部面包屑回到上层。

产品设计前端后端移动端增长增长黑客内容BD后台HR财务法务行政产品设计前端后端移动端增长增长黑客内容BD后台HR财务行政

焦点路径 (顶层)

src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfSunburstChart, CfTag } from '@chufix-design/vue';

const root = {
  name: '全公司',
  children: [
    {
      name: '产品',
      colorIndex: 0,
      children: [
        { name: '设计', value: 12 },
        { name: '前端', value: 18 },
        { name: '后端', value: 22 },
        { name: '移动端', value: 8 },
      ],
    },
    {
      name: '增长',
      colorIndex: 1,
      children: [
        { name: '增长黑客', value: 6 },
        { name: '内容', value: 10 },
        { name: 'BD', value: 8 },
      ],
    },
    {
      name: '后台',
      colorIndex: 2,
      children: [
        { name: 'HR', value: 4 },
        { name: '财务', value: 5 },
        { name: '法务', value: 3 },
        { name: '行政', value: 4 },
      ],
    },
  ],
};

const path = ref<string[]>([]);
</script>
<template>
  <p style="margin: 0 0 8px; color: var(--fg-3); font-size: 12px;">
    点击外环任意分类 → 该子树成为新焦点;中心 ↑ 或顶部面包屑回到上层。
  </p>
  <CfSunburstChart
    :root="root"
    :size="340"
    @drill="(p: { pathNames: string[] }) => path = p.pathNames"
  />
  <p style="margin-top: 8px; font-size: 12px;">
    <CfTag tone="info" size="sm">焦点路径</CfTag>
    {{ path.length ? path.join(' / ') : '(顶层)' }}
  </p>
</template>
<script setup>
import { ref } from 'vue';
import { CfSunburstChart, CfTag } from '@chufix-design/vue';

const root = {
  name: '全公司',
  children: [
    {
      name: '产品',
      colorIndex: 0,
      children: [
        { name: '设计', value: 12 },
        { name: '前端', value: 18 },
        { name: '后端', value: 22 },
        { name: '移动端', value: 8 },
      ],
    },
    {
      name: '增长',
      colorIndex: 1,
      children: [
        { name: '增长黑客', value: 6 },
        { name: '内容', value: 10 },
        { name: 'BD', value: 8 },
      ],
    },
    {
      name: '后台',
      colorIndex: 2,
      children: [
        { name: 'HR', value: 4 },
        { name: '财务', value: 5 },
        { name: '法务', value: 3 },
        { name: '行政', value: 4 },
      ],
    },
  ],
};

const path = ref<string[]>([]);
</script>
<template>
  <p style="margin: 0 0 8px; color: var(--fg-3); font-size: 12px;">
    点击外环任意分类 → 该子树成为新焦点;中心 ↑ 或顶部面包屑回到上层。
  </p>
  <CfSunburstChart
    :root="root"
    :size="340"
    @drill="(p: { pathNames: string[] }) => path = p.pathNames"
  />
  <p style="margin-top: 8px; font-size: 12px;">
    <CfTag tone="info" size="sm">焦点路径</CfTag>
    {{ path.length ? path.join(' / ') : '(顶层)' }}
  </p>
</template>
import { useState } from 'react';
import { CfSunburstChart, CfTag } from '@chufix-design/react';

export default function Demo() {
  const root = {
    name: '全公司',
    children: [
      {
        name: '产品',
        colorIndex: 0,
        children: [
          { name: '设计', value: 12 },
          { name: '前端', value: 18 },
          { name: '后端', value: 22 },
          { name: '移动端', value: 8 },
        ],
      },
      {
        name: '增长',
        colorIndex: 1,
        children: [
          { name: '增长黑客', value: 6 },
          { name: '内容', value: 10 },
          { name: 'BD', value: 8 },
        ],
      },
      {
        name: '后台',
        colorIndex: 2,
        children: [
          { name: 'HR', value: 4 },
          { name: '财务', value: 5 },
          { name: '法务', value: 3 },
          { name: '行政', value: 4 },
        ],
      },
    ],
  };

  const [path, setPath] = useState<string[]>([]);
  return (
    <>
      <p style={{ margin: "0 0 8px", color: "var(--fg-3)", fontSize: 12 }}>
          点击外环任意分类 → 该子树成为新焦点;中心 ↑ 或顶部面包屑回到上层。
        </p>
        <CfSunburstChart root={root} size={340} onDrill={(p: { pathNames: string[] }) => setPath(p.pathNames)}
        />
        <p style={{ marginTop: 8, fontSize: 12 }}>
          <CfTag tone="info" size="sm">焦点路径</CfTag>
          {path.length ? path.join(' / ') : '(顶层)'}
        </p>
    </>
  );
}
import { useState } from 'react';
import { CfSunburstChart, CfTag } from '@chufix-design/react';

export default function Demo() {
  const root = {
    name: '全公司',
    children: [
      {
        name: '产品',
        colorIndex: 0,
        children: [
          { name: '设计', value: 12 },
          { name: '前端', value: 18 },
          { name: '后端', value: 22 },
          { name: '移动端', value: 8 },
        ],
      },
      {
        name: '增长',
        colorIndex: 1,
        children: [
          { name: '增长黑客', value: 6 },
          { name: '内容', value: 10 },
          { name: 'BD', value: 8 },
        ],
      },
      {
        name: '后台',
        colorIndex: 2,
        children: [
          { name: 'HR', value: 4 },
          { name: '财务', value: 5 },
          { name: '法务', value: 3 },
          { name: '行政', value: 4 },
        ],
      },
    ],
  };

  const [path, setPath] = useState<string[]>([]);
  return (
    <>
      <p style={{ margin: "0 0 8px", color: "var(--fg-3)", fontSize: 12 }}>
          点击外环任意分类 → 该子树成为新焦点;中心 ↑ 或顶部面包屑回到上层。
        </p>
        <CfSunburstChart root={root} size={340} onDrill={(p: { pathNames: string[] }) => setPath(p.pathNames)}
        />
        <p style={{ marginTop: 8, fontSize: 12 }}>
          <CfTag tone="info" size="sm">焦点路径</CfTag>
          {path.length ? path.join(' / ') : '(顶层)'}
        </p>
    </>
  );
}

API

属性类型默认值说明
rootSunburstNode根节点(其本身不渲染,孩子构成第 1 环)
sizenumber240直径
innerRadiusRatio0..10.2中心留白半径占总半径的比例
showLabelsbooleantrue大扇形上画标签
labelMinAnglenumber12小于该角度(度)的扇形不画标签
drillablebooleantrue点击有子节点的扇形钻取到该子树
showBreadcrumbbooleantrue钻取时显示顶部面包屑(受控钻取也用得到)
ariaLabelstring'旭日图'

Events

Vue 事件React 回调载荷说明
item-enteronItemEnterSunburstChartInteractionPayload鼠标进入某扇形
item-leaveonItemLeaveSunburstChartInteractionPayload鼠标离开
drillonDrillSunburstDrillPayload钻取后新焦点 + 路径

类型

interface SunburstNode {
  name: string;
  /** 叶子节点必填;父节点会从孩子求和。 */
  value?: number;
  /** 0..7,对应 --viz-1..8。父节点的色相会被孩子继承。 */
  colorIndex?: number;
  children?: SunburstNode[];
}

interface SunburstChartInteractionPayload {
  node: SunburstNode;
  /** 1 = 第一环;2 = 第二环;以此类推。 */
  depth: number;
  /** 从 root 到该节点的 name 路径(含 root.name)。 */
  pathNames: string[];
  /** node 及其后代的累计值。 */
  totalValue: number;
  nativeEvent?: PointerEvent;
}

反馈与讨论

SunburstChart 旭日图 的讨论

0
0 / 600
正在加载评论...