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

Treemap 矩形树图

层级矩形面积图,支持任意深度嵌套 + 点击下钻聚焦。

基础用法

数据通过 props 传入,纯 SVG 渲染,无第三方图表库依赖。 配色取自 --viz-1..8 token,色盲友好。

背景 视口
react-dom1,200react720lodash540moment480rxjs380d3three
src/App.vue
<script setup lang="ts">
import { CfTreemap } from '@chufix-design/vue';
const nodes = [
  { name: 'react-dom', value: 1200 },
  { name: 'react', value: 720 },
  { name: 'lodash', value: 540 },
  { name: 'moment', value: 480 },
  { name: 'rxjs', value: 380 },
  { name: 'd3', value: 320 },
  { name: 'three', value: 280 },
  { name: 'others', value: 200 },
];
</script>
<template>
  <CfTreemap :nodes="nodes" />
</template>
<script setup>
import { CfTreemap } from '@chufix-design/vue';
const nodes = [
  { name: 'react-dom', value: 1200 },
  { name: 'react', value: 720 },
  { name: 'lodash', value: 540 },
  { name: 'moment', value: 480 },
  { name: 'rxjs', value: 380 },
  { name: 'd3', value: 320 },
  { name: 'three', value: 280 },
  { name: 'others', value: 200 },
];
</script>
<template>
  <CfTreemap :nodes="nodes" />
</template>
import { CfTreemap } from '@chufix-design/react';

export default function Demo() {
  const nodes = [
    { name: 'react-dom', value: 1200 },
    { name: 'react', value: 720 },
    { name: 'lodash', value: 540 },
    { name: 'moment', value: 480 },
    { name: 'rxjs', value: 380 },
    { name: 'd3', value: 320 },
    { name: 'three', value: 280 },
    { name: 'others', value: 200 },
  ];
  return (
    <>
      <CfTreemap nodes={nodes} />
    </>
  );
}
import { CfTreemap } from '@chufix-design/react';

export default function Demo() {
  const nodes = [
    { name: 'react-dom', value: 1200 },
    { name: 'react', value: 720 },
    { name: 'lodash', value: 540 },
    { name: 'moment', value: 480 },
    { name: 'rxjs', value: 380 },
    { name: 'd3', value: 320 },
    { name: 'three', value: 280 },
    { name: 'others', value: 200 },
  ];
  return (
    <>
      <CfTreemap nodes={nodes} />
    </>
  );
}

label 显隐

showLabels=false 时只展示色块,适合微缩组合。仅当矩形足够大(w>50, h>20)时才显示文字。

背景 视口
5 个节点(label 显示)
依赖 A280依赖 B220依赖 C180依赖 D120其他80
label 隐藏
src/App.vue
<script setup lang="ts">
import { CfTreemap } from '@chufix-design/vue';
const small = [
  { name: '依赖 A', value: 280 },
  { name: '依赖 B', value: 220 },
  { name: '依赖 C', value: 180 },
  { name: '依赖 D', value: 120 },
  { name: '其他', value: 80 },
];
</script>
<template>
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
    <div>
      <div style="font-size: 11px; color: var(--fg-3); margin-bottom: 4px;">5 个节点(label 显示)</div>
      <CfTreemap :nodes="small" :height="180" />
    </div>
    <div>
      <div style="font-size: 11px; color: var(--fg-3); margin-bottom: 4px;">label 隐藏</div>
      <CfTreemap :nodes="small" :height="180" :show-labels="false" />
    </div>
  </div>
</template>
<script setup>
import { CfTreemap } from '@chufix-design/vue';
const small = [
  { name: '依赖 A', value: 280 },
  { name: '依赖 B', value: 220 },
  { name: '依赖 C', value: 180 },
  { name: '依赖 D', value: 120 },
  { name: '其他', value: 80 },
];
</script>
<template>
  <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
    <div>
      <div style="font-size: 11px; color: var(--fg-3); margin-bottom: 4px;">5 个节点(label 显示)</div>
      <CfTreemap :nodes="small" :height="180" />
    </div>
    <div>
      <div style="font-size: 11px; color: var(--fg-3); margin-bottom: 4px;">label 隐藏</div>
      <CfTreemap :nodes="small" :height="180" :show-labels="false" />
    </div>
  </div>
</template>
import { CfTreemap } from '@chufix-design/react';

export default function Demo() {
  const small = [
    { name: '依赖 A', value: 280 },
    { name: '依赖 B', value: 220 },
    { name: '依赖 C', value: 180 },
    { name: '依赖 D', value: 120 },
    { name: '其他', value: 80 },
  ];
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
          <div>
            <div style={{ fontSize: 11, color: "var(--fg-3)", marginBottom: 4 }}>5 个节点(label 显示)</div>
            <CfTreemap nodes={small} height={180} />
          </div>
          <div>
            <div style={{ fontSize: 11, color: "var(--fg-3)", marginBottom: 4 }}>label 隐藏</div>
            <CfTreemap nodes={small} height={180} showLabels={false} />
          </div>
        </div>
    </>
  );
}
import { CfTreemap } from '@chufix-design/react';

export default function Demo() {
  const small = [
    { name: '依赖 A', value: 280 },
    { name: '依赖 B', value: 220 },
    { name: '依赖 C', value: 180 },
    { name: '依赖 D', value: 120 },
    { name: '其他', value: 80 },
  ];
  return (
    <>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
          <div>
            <div style={{ fontSize: 11, color: "var(--fg-3)", marginBottom: 4 }}>5 个节点(label 显示)</div>
            <CfTreemap nodes={small} height={180} />
          </div>
          <div>
            <div style={{ fontSize: 11, color: "var(--fg-3)", marginBottom: 4 }}>label 隐藏</div>
            <CfTreemap nodes={small} height={180} showLabels={false} />
          </div>
        </div>
    </>
  );
}

嵌套层级 + 下钻

给任意节点加 children,组件会递归排版,父矩形顶部留 headerHeight 像素当作分组标题。点击有子节点的顶层矩形 → 该子树成为新焦点;面包屑、↑ 上一层、底部 footer 都跟着切换。drillable={false} 即关掉钻取行为退回静态视图。

默认 layout="squarify" 用经典的 Squarified Treemap 算法 (Bruls et al. 2000),贪心地让每个矩形的长宽比接近 1,长宽悬殊的数据集也能保持可读;需要传统纵横交替切分时传 layout="slice-and-dice" 退回旧行为。

背景 视口

顶层方块直接展示子分类的嵌套排版;单击有子项的方块即可下钻聚焦到该子树。

亚太中国4,280日本1,820东南亚1,240澳洲760北美美国3,920加拿大1,080欧洲英国1,420德国1,380法国980北欧820其他南美中东

焦点 全部

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

const nodes = [
  {
    name: '亚太',
    children: [
      { name: '中国', value: 4280 },
      { name: '日本', value: 1820 },
      { name: '东南亚', value: 1240 },
      { name: '澳洲', value: 760 },
    ],
  },
  {
    name: '欧洲',
    children: [
      { name: '英国', value: 1420 },
      { name: '德国', value: 1380 },
      { name: '法国', value: 980 },
      { name: '北欧', value: 820 },
    ],
  },
  {
    name: '北美',
    children: [
      { name: '美国', value: 3920 },
      { name: '加拿大', value: 1080 },
    ],
  },
  {
    name: '其他',
    children: [
      { name: '南美', value: 620 },
      { name: '中东', value: 540 },
      { name: '非洲', value: 380 },
    ],
  },
];

const focus = ref<string[]>([]);
</script>
<template>
  <p style="margin: 0 0 8px; color: var(--fg-3); font-size: 12px;">
    顶层方块直接展示子分类的嵌套排版;单击有子项的方块即可下钻聚焦到该子树。
  </p>
  <CfTreemap
    :nodes="nodes"
    :width="640"
    :height="320"
    @drill="(p: { pathNames: string[] }) => focus = p.pathNames"
  />
  <p style="margin-top: 8px; font-size: 12px;">
    <CfTag tone="info" size="sm">焦点</CfTag>
    {{ focus.length ? focus.join(' / ') : '全部' }}
  </p>
</template>
<script setup>
import { ref } from 'vue';
import { CfTag, CfTreemap } from '@chufix-design/vue';

const nodes = [
  {
    name: '亚太',
    children: [
      { name: '中国', value: 4280 },
      { name: '日本', value: 1820 },
      { name: '东南亚', value: 1240 },
      { name: '澳洲', value: 760 },
    ],
  },
  {
    name: '欧洲',
    children: [
      { name: '英国', value: 1420 },
      { name: '德国', value: 1380 },
      { name: '法国', value: 980 },
      { name: '北欧', value: 820 },
    ],
  },
  {
    name: '北美',
    children: [
      { name: '美国', value: 3920 },
      { name: '加拿大', value: 1080 },
    ],
  },
  {
    name: '其他',
    children: [
      { name: '南美', value: 620 },
      { name: '中东', value: 540 },
      { name: '非洲', value: 380 },
    ],
  },
];

const focus = ref<string[]>([]);
</script>
<template>
  <p style="margin: 0 0 8px; color: var(--fg-3); font-size: 12px;">
    顶层方块直接展示子分类的嵌套排版;单击有子项的方块即可下钻聚焦到该子树。
  </p>
  <CfTreemap
    :nodes="nodes"
    :width="640"
    :height="320"
    @drill="(p: { pathNames: string[] }) => focus = p.pathNames"
  />
  <p style="margin-top: 8px; font-size: 12px;">
    <CfTag tone="info" size="sm">焦点</CfTag>
    {{ focus.length ? focus.join(' / ') : '全部' }}
  </p>
</template>
import { useState } from 'react';
import { CfTag, CfTreemap } from '@chufix-design/react';

export default function Demo() {
  const nodes = [
    {
      name: '亚太',
      children: [
        { name: '中国', value: 4280 },
        { name: '日本', value: 1820 },
        { name: '东南亚', value: 1240 },
        { name: '澳洲', value: 760 },
      ],
    },
    {
      name: '欧洲',
      children: [
        { name: '英国', value: 1420 },
        { name: '德国', value: 1380 },
        { name: '法国', value: 980 },
        { name: '北欧', value: 820 },
      ],
    },
    {
      name: '北美',
      children: [
        { name: '美国', value: 3920 },
        { name: '加拿大', value: 1080 },
      ],
    },
    {
      name: '其他',
      children: [
        { name: '南美', value: 620 },
        { name: '中东', value: 540 },
        { name: '非洲', value: 380 },
      ],
    },
  ];

  const [focus, setFocus] = useState<string[]>([]);
  return (
    <>
      <p style={{ margin: "0 0 8px", color: "var(--fg-3)", fontSize: 12 }}>
          顶层方块直接展示子分类的嵌套排版;单击有子项的方块即可下钻聚焦到该子树。
        </p>
        <CfTreemap nodes={nodes} width={640} height={320} onDrill={(p: { pathNames: string[] }) => setFocus(p.pathNames)}
        />
        <p style={{ marginTop: 8, fontSize: 12 }}>
          <CfTag tone="info" size="sm">焦点</CfTag>
          {focus.length ? focus.join(' / ') : '全部'}
        </p>
    </>
  );
}
import { useState } from 'react';
import { CfTag, CfTreemap } from '@chufix-design/react';

export default function Demo() {
  const nodes = [
    {
      name: '亚太',
      children: [
        { name: '中国', value: 4280 },
        { name: '日本', value: 1820 },
        { name: '东南亚', value: 1240 },
        { name: '澳洲', value: 760 },
      ],
    },
    {
      name: '欧洲',
      children: [
        { name: '英国', value: 1420 },
        { name: '德国', value: 1380 },
        { name: '法国', value: 980 },
        { name: '北欧', value: 820 },
      ],
    },
    {
      name: '北美',
      children: [
        { name: '美国', value: 3920 },
        { name: '加拿大', value: 1080 },
      ],
    },
    {
      name: '其他',
      children: [
        { name: '南美', value: 620 },
        { name: '中东', value: 540 },
        { name: '非洲', value: 380 },
      ],
    },
  ];

  const [focus, setFocus] = useState<string[]>([]);
  return (
    <>
      <p style={{ margin: "0 0 8px", color: "var(--fg-3)", fontSize: 12 }}>
          顶层方块直接展示子分类的嵌套排版;单击有子项的方块即可下钻聚焦到该子树。
        </p>
        <CfTreemap nodes={nodes} width={640} height={320} onDrill={(p: { pathNames: string[] }) => setFocus(p.pathNames)}
        />
        <p style={{ marginTop: 8, fontSize: 12 }}>
          <CfTag tone="info" size="sm">焦点</CfTag>
          {focus.length ? focus.join(' / ') : '全部'}
        </p>
    </>
  );
}

API

属性类型默认值说明
nodesTreemapNode[]{ name, value?, colorIndex?, children? }[]
widthnumber480SVG 宽度
heightnumber240SVG 高度
showLabelsbooleantrue在矩形内显示 name / value(足够大时)
childPaddingnumber4父矩形内子节点四周留白
headerHeightnumber16父矩形顶部留给标题的高度
drillablebooleantrue点击有子节点的顶层矩形钻取
showBreadcrumbbooleantrue钻取时显示面包屑 + ↑上一层按钮
layout'squarify' | 'slice-and-dice''squarify'排版算法,默认 squarify 优化长宽比
ariaLabelstring透传给根 <svg>aria-label

Events

Vue 事件React 回调载荷类型说明
item-enteronItemEnterTreemapInteractionPayload鼠标进入某矩形时触发
item-leaveonItemLeaveTreemapInteractionPayload鼠标离开矩形时触发
drillonDrillTreemapDrillPayload钻取后的新焦点 + 完整路径

类型

interface TreemapNode {
  name: string;
  value?: number;
  colorIndex?: number;
  /** 嵌套子节点;父节点的值会被自动计算为子节点之和。 */
  children?: TreemapNode[];
}

interface TreemapInteractionPayload {
  node: TreemapNode;
  /** 当前焦点的直接子节点中的索引。 */
  dataIndex: number;
  /** 0 = 顶层矩形;1+ = 嵌套子矩形。 */
  depth: number;
  /** 从原始 root 到该节点的 name 路径。 */
  pathNames: string[];
  nativeEvent?: PointerEvent;
}

interface TreemapDrillPayload {
  /** 当前焦点,回到顶层时为 null。 */
  node: TreemapNode | null;
  pathNames: string[];
}

反馈与讨论

Treemap 矩形树图 的讨论

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