---
title: "鱼骨布局 Fishbone"
description: "鱼骨布局 Fishbone"
language: "zh"
canonical: "https://g6.antv.antgroup.com/zh/manual/layout/Fishbone/"
version: "5.1.1"
---

## 概述

鱼骨布局是一种专门用于表示层次结构数据的图形布局方式。它通过模拟鱼骨的形状，将数据节点按照层次结构排列，使得数据的层次关系更加清晰直观。鱼骨布局特别适用于需要展示因果关系、层次结构或分类信息的数据集。

## 使用场景

- 需要展示层次结构数据，如组织结构、分类体系
- 需要展示问题分析过程，如故障分析、质量分析
- 需要展示决策过程，如决策树、影响因素分析

## 在线体验



```ts
import { createGraph } from '/demo-runtime.ts';

createGraph(
  {
    data: {
      nodes: [
        {
          id: 'Quality',
          depth: 0,
          children: ['Machine', 'Method', 'Material', 'Man Power', 'Measurement', 'Milieu'],
        },
        {
          id: 'Machine',
          depth: 1,
          children: ['Mill', 'Mixer', 'Metal Lathe'],
        },
        {
          id: 'Mill',
          depth: 2,
        },
        {
          id: 'Mixer',
          depth: 2,
        },
        {
          id: 'Metal Lathe',
          depth: 2,
          children: ['Milling'],
        },
        {
          id: 'Milling',
          depth: 3,
        },
        {
          id: 'Method',
          depth: 1,
        },
        {
          id: 'Material',
          depth: 1,
          children: ['Masonite', 'Marscapone', 'Meat'],
        },
        {
          id: 'Masonite',
          depth: 2,
          children: ['spearMint', 'pepperMint', 'test1'],
        },
        {
          id: 'spearMint',
          depth: 3,
        },
        {
          id: 'pepperMint',
          depth: 3,
          children: ['test3'],
        },
        {
          id: 'test3',
          depth: 4,
        },
        {
          id: 'test1',
          depth: 3,
          children: ['test4'],
        },
        {
          id: 'test4',
          depth: 4,
        },
        {
          id: 'Marscapone',
          depth: 2,
          children: ['Malty', 'Minty'],
        },
        {
          id: 'Malty',
          depth: 3,
        },
        {
          id: 'Minty',
          depth: 3,
        },
        {
          id: 'Meat',
          depth: 2,
          children: ['Mutton'],
        },
        {
          id: 'Mutton',
          depth: 3,
        },
        {
          id: 'Man Power',
          depth: 1,
          children: ['Manager', "Master's Student", 'Magician', 'Miner', 'Magister', 'Massage Artist'],
        },
        {
          id: 'Manager',
          depth: 2,
        },
        {
          id: "Master's Student",
          depth: 2,
        },
        {
          id: 'Magician',
          depth: 2,
        },
        {
          id: 'Miner',
          depth: 2,
        },
        {
          id: 'Magister',
          depth: 2,
          children: ['Malpractice'],
        },
        {
          id: 'Malpractice',
          depth: 3,
        },
        {
          id: 'Massage Artist',
          depth: 2,
          children: ['Masseur', 'Masseuse'],
        },
        {
          id: 'Masseur',
          depth: 3,
        },
        {
          id: 'Masseuse',
          depth: 3,
        },
        {
          id: 'Measurement',
          depth: 1,
          children: ['Malleability'],
        },
        {
          id: 'Malleability',
          depth: 2,
        },
        {
          id: 'Milieu',
          depth: 1,
          children: ['Marine'],
        },
        {
          id: 'Marine',
          depth: 2,
        },
      ],
      edges: [
        {
          source: 'Quality',
          target: 'Machine',
        },
        {
          source: 'Quality',
          target: 'Method',
        },
        {
          source: 'Quality',
          target: 'Material',
        },
        {
          source: 'Quality',
          target: 'Man Power',
        },
        {
          source: 'Quality',
          target: 'Measurement',
        },
        {
          source: 'Quality',
          target: 'Milieu',
        },
        {
          source: 'Machine',
          target: 'Mill',
        },
        {
          source: 'Machine',
          target: 'Mixer',
        },
        {
          source: 'Machine',
          target: 'Metal Lathe',
        },
        {
          source: 'Metal Lathe',
          target: 'Milling',
        },
        {
          source: 'Material',
          target: 'Masonite',
        },
        {
          source: 'Material',
          target: 'Marscapone',
        },
        {
          source: 'Material',
          target: 'Meat',
        },
        {
          source: 'Masonite',
          target: 'spearMint',
        },
        {
          source: 'Masonite',
          target: 'pepperMint',
        },
        {
          source: 'Masonite',
          target: 'test1',
        },
        {
          source: 'pepperMint',
          target: 'test3',
        },
        {
          source: 'test1',
          target: 'test4',
        },
        {
          source: 'Marscapone',
          target: 'Malty',
        },
        {
          source: 'Marscapone',
          target: 'Minty',
        },
        {
          source: 'Meat',
          target: 'Mutton',
        },
        {
          source: 'Man Power',
          target: 'Manager',
        },
        {
          source: 'Man Power',
          target: "Master's Student",
        },
        {
          source: 'Man Power',
          target: 'Magician',
        },
        {
          source: 'Man Power',
          target: 'Miner',
        },
        {
          source: 'Man Power',
          target: 'Magister',
        },
        {
          source: 'Man Power',
          target: 'Massage Artist',
        },
        {
          source: 'Magister',
          target: 'Malpractice',
        },
        {
          source: 'Massage Artist',
          target: 'Masseur',
        },
        {
          source: 'Massage Artist',
          target: 'Masseuse',
        },
        {
          source: 'Measurement',
          target: 'Malleability',
        },
        {
          source: 'Milieu',
          target: 'Marine',
        },
      ],
    },
    node: {
      type: 'rect',
      style: {
        size: [32, 32],
        // fill: () => randomColor(),
        label: false,
        labelFill: '#262626',
        labelFontFamily: 'Gill Sans',
        labelMaxLines: 2,
        labelMaxWidth: '100%',
        labelPlacement: 'center',
        labelText: (d) => d.id,
        labelWordWrap: true,
      },
    },
    edge: {
      type: 'polyline',
      style: {
        lineWidth: 3,
      },
    },
    behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],
    autoFit: 'view',
    layout: {
      type: 'fishbone',
      direction: 'RL',
      hGap: 50,
      vGap: 50,
      getRibSep: () => 60,
    },
  },
  { width: 600, height: 400 },
  (gui, graph) => {
    const options = {
      type: 'fishbone',
      direction: 'RL',
      hGap: 50,
      vGap: 50,
      getRibSep: 60,
    };

    const optionFolder = gui.addFolder('Fishbone Layout Options');
    optionFolder.add(options, 'type').disable(true);
    optionFolder.add(options, 'direction', ['RL', 'LR']);
    optionFolder.add(options, 'hGap', 20, 100, 10);
    optionFolder.add(options, 'vGap', 20, 100, 10);
    optionFolder.add(options, 'getRibSep', 30, 100, 10);

    optionFolder.onChange(async ({ property, value }) => {
      graph.setLayout(
        Object.assign({}, graph.getLayout(), {
          [property]: property === 'getRibSep' ? () => value : value,
        }),
      );
      await graph.layout();
      // 调整 direction 后部分node可能会溢出屏幕，重新执行下fitView
      if (property === 'direction') {
        graph.fitView();
      }
    });
  },
);
```



## 基本用法

```js
const graph = new Graph({
  layout: {
    type: 'fishbone',
    direction: 'LR',
    hGap: 50,
    vGap: 50,
    getRibSep: () => 60,
  },
});
```

## 配置项

| 属性                   | 描述                                                       | 类型                                                                                                                                   | 默认值   | 必选 |
| ---------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---- |
| type                   | 布局类型                                                   | `fishbone`                                                                                                                             | -        | ✓    |
| direction              | 排布方向，`RL` 从右到左，鱼头在右；`LR` 从左到右，鱼头在左 | `RL` \| `LR`                                                                                                                           | `RL`     |      |
| hGap                   | 水平间距                                                   | number                                                                                                                                 | -        |      |
| vGap                   | 垂直间距                                                   | number                                                                                                                                 | -        |      |
| getRibSep              | 获取鱼骨间距                                               | (node: NodeData) => number                                                                                                             | () => 60 |      |
| width                  | 布局宽度                                                   | number                                                                                                                                 | -        |      |
| height                 | 布局高度                                                   | number                                                                                                                                 | -        |      |
| nodeSize               | 节点大小                                                   | number \| [number, number] \| [number, number, number] \| ((node: NodeData) => number \| [number, number] \| [number, number, number]) | -        |      |
| isLayoutInvisibleNodes | 不可见节点是否参与布局，当 preLayout 为 true 时生效        | boolean                                                                                                                                | -        |      |
| nodeFilter             | 参与该布局的节点                                           | (node: NodeData) => boolean                                                                                                            | -        |      |
| preLayout              | 使用前布局，在初始化元素前计算布局，不适用于流水线布局     | boolean                                                                                                                                | -        |      |

## 代码示例

### 基础用法

最简单的配置方式：

```js
import { Graph, treeToGraphData } from '@antv/g6';

const graph = new Graph({
  layout: {
    type: 'fishbone',
  },
  autoFit: 'view',
  data: treeToGraphData({
    nodes: [
      { id: 'root', data: { label: 'Root' } },
      { id: 'child1', data: { label: 'Child 1' } },
      { id: 'child2', data: { label: 'Child 2' } },
      { id: 'child3', data: { label: 'Child 3' } },
    ],
    edges: [
      { id: 'e1', source: 'root', target: 'child1' },
      { id: 'e2', source: 'root', target: 'child2' },
      { id: 'e3', source: 'root', target: 'child3' },
    ],
  }),
  edge: {
    type: 'polyline',
    style: {
      lineWidth: 3,
    },
  },
  behaviors: ['drag-canvas'],
});
```

效果如下：



```ts

import { Graph } from '@antv/g6';

const graph = new Graph({
  container: 'container',
  width: 600,
  height: 400,
  layout: {
    type: 'fishbone',
  },
  autoFit: 'view',
  data: {
    nodes: [
      {
        id: 'Quality',
        depth: 0,
        children: ['Machine', 'Method', 'Material', 'Man Power', 'Measurement', 'Milieu'],
      },
      {
        id: 'Machine',
        depth: 1,
        children: ['Mill', 'Mixer', 'Metal Lathe'],
      },
      {
        id: 'Mill',
        depth: 2,
      },
      {
        id: 'Mixer',
        depth: 2,
      },
      {
        id: 'Metal Lathe',
        depth: 2,
        children: ['Milling'],
      },
      {
        id: 'Milling',
        depth: 3,
      },
      {
        id: 'Method',
        depth: 1,
      },
      {
        id: 'Material',
        depth: 1,
        children: ['Masonite', 'Marscapone', 'Meat'],
      },
      {
        id: 'Masonite',
        depth: 2,
        children: ['spearMint', 'pepperMint', 'test1'],
      },
      {
        id: 'spearMint',
        depth: 3,
      },
      {
        id: 'pepperMint',
        depth: 3,
        children: ['test3'],
      },
      {
        id: 'test3',
        depth: 4,
      },
      {
        id: 'test1',
        depth: 3,
        children: ['test4'],
      },
      {
        id: 'test4',
        depth: 4,
      },
      {
        id: 'Marscapone',
        depth: 2,
        children: ['Malty', 'Minty'],
      },
      {
        id: 'Malty',
        depth: 3,
      },
      {
        id: 'Minty',
        depth: 3,
      },
      {
        id: 'Meat',
        depth: 2,
        children: ['Mutton'],
      },
      {
        id: 'Mutton',
        depth: 3,
      },
      {
        id: 'Man Power',
        depth: 1,
        children: ['Manager', "Master's Student", 'Magician', 'Miner', 'Magister', 'Massage Artist'],
      },
      {
        id: 'Manager',
        depth: 2,
      },
      {
        id: "Master's Student",
        depth: 2,
      },
      {
        id: 'Magician',
        depth: 2,
      },
      {
        id: 'Miner',
        depth: 2,
      },
      {
        id: 'Magister',
        depth: 2,
        children: ['Malpractice'],
      },
      {
        id: 'Malpractice',
        depth: 3,
      },
      {
        id: 'Massage Artist',
        depth: 2,
        children: ['Masseur', 'Masseuse'],
      },
      {
        id: 'Masseur',
        depth: 3,
      },
      {
        id: 'Masseuse',
        depth: 3,
      },
      {
        id: 'Measurement',
        depth: 1,
        children: ['Malleability'],
      },
      {
        id: 'Malleability',
        depth: 2,
      },
      {
        id: 'Milieu',
        depth: 1,
        children: ['Marine'],
      },
      {
        id: 'Marine',
        depth: 2,
      },
    ],
    edges: [
      {
        source: 'Quality',
        target: 'Machine',
      },
      {
        source: 'Quality',
        target: 'Method',
      },
      {
        source: 'Quality',
        target: 'Material',
      },
      {
        source: 'Quality',
        target: 'Man Power',
      },
      {
        source: 'Quality',
        target: 'Measurement',
      },
      {
        source: 'Quality',
        target: 'Milieu',
      },
      {
        source: 'Machine',
        target: 'Mill',
      },
      {
        source: 'Machine',
        target: 'Mixer',
      },
      {
        source: 'Machine',
        target: 'Metal Lathe',
      },
      {
        source: 'Metal Lathe',
        target: 'Milling',
      },
      {
        source: 'Material',
        target: 'Masonite',
      },
      {
        source: 'Material',
        target: 'Marscapone',
      },
      {
        source: 'Material',
        target: 'Meat',
      },
      {
        source: 'Masonite',
        target: 'spearMint',
      },
      {
        source: 'Masonite',
        target: 'pepperMint',
      },
      {
        source: 'Masonite',
        target: 'test1',
      },
      {
        source: 'pepperMint',
        target: 'test3',
      },
      {
        source: 'test1',
        target: 'test4',
      },
      {
        source: 'Marscapone',
        target: 'Malty',
      },
      {
        source: 'Marscapone',
        target: 'Minty',
      },
      {
        source: 'Meat',
        target: 'Mutton',
      },
      {
        source: 'Man Power',
        target: 'Manager',
      },
      {
        source: 'Man Power',
        target: "Master's Student",
      },
      {
        source: 'Man Power',
        target: 'Magician',
      },
      {
        source: 'Man Power',
        target: 'Miner',
      },
      {
        source: 'Man Power',
        target: 'Magister',
      },
      {
        source: 'Man Power',
        target: 'Massage Artist',
      },
      {
        source: 'Magister',
        target: 'Malpractice',
      },
      {
        source: 'Massage Artist',
        target: 'Masseur',
      },
      {
        source: 'Massage Artist',
        target: 'Masseuse',
      },
      {
        source: 'Measurement',
        target: 'Malleability',
      },
      {
        source: 'Milieu',
        target: 'Marine',
      },
    ],
  },
  edge: {
    type: 'polyline',
    style: {
      lineWidth: 3,
    },
  },
  behaviors: ['drag-canvas'],
});

graph.render();
```


## 实际案例



```ts

import { Graph, treeToGraphData } from '@antv/g6';

const data = {
  id: 'Quality',
  children: [
    {
      id: 'Machine',
      children: [{ id: 'Mill' }, { id: 'Mixer' }, { id: 'Metal Lathe', children: [{ id: 'Milling' }] }],
    },
    { id: 'Method' },
    {
      id: 'Material',
      children: [
        {
          id: 'Masonite',
          children: [
            { id: 'spearMint' },
            { id: 'pepperMint', children: [{ id: 'test3' }] },
            { id: 'test1', children: [{ id: 'test4' }] },
          ],
        },
        {
          id: 'Marscapone',
          children: [{ id: 'Malty' }, { id: 'Minty' }],
        },
        { id: 'Meat', children: [{ id: 'Mutton' }] },
      ],
    },
    {
      id: 'Man Power',
      children: [
        { id: 'Manager' },
        { id: "Master's Student" },
        { id: 'Magician' },
        { id: 'Miner' },
        { id: 'Magister', children: [{ id: 'Malpractice' }] },
        {
          id: 'Massage Artist',
          children: [{ id: 'Masseur' }, { id: 'Masseuse' }],
        },
      ],
    },
    {
      id: 'Measurement',
      children: [{ id: 'Malleability' }],
    },
    {
      id: 'Milieu',
      children: [{ id: 'Marine' }],
    },
  ],
};

export const layoutFishbone = async (context) => {
  const graph = new Graph({
    ...context,
    container: 'container',
    autoFit: 'view',
    data: treeToGraphData(data),
    node: {
      type: 'rect',
      style: {
        size: [32, 32],
        // fill: () => randomColor(),
        label: false,
        labelFill: '#262626',
        labelFontFamily: 'Gill Sans',
        labelMaxLines: 2,
        labelMaxWidth: '100%',
        labelPlacement: 'center',
        labelText: (d) => d.id,
        labelWordWrap: true,
      },
    },
    edge: {
      type: 'polyline',
      style: {
        lineWidth: 3,
      },
    },
    layout: {
      type: 'fishbone',
      vGap: 48,
      hGap: 48,
      direction: 'RL',
    },
    behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],
    animation: false,
  });

  await graph.render();

  layoutFishbone.form = (panel) => {
    const config = {
      type: 'fishbone',
      direction: 'RL',
    };

    return [
      panel
        .add(config, 'direction', ['LR', 'RL'])
        .name('Direction')
        .onChange((value) => {
          graph.setLayout((prev) => ({ ...prev, direction: value }));
          graph.render();
        }),
    ];
  };

  return graph;
};

layoutFishbone();
```


- [Fishbone布局](/zh/examples/layout/fishbone/basic/)
### demo-runtime.ts

```ts
import { Graph } from '@antv/g6';
import GUI from 'lil-gui';

export function addPanel(renderPanel: (gui: GUI) => void) {
  const gui = new GUI({ container: document.body });
  gui.title('Control');
  Object.assign(gui.domElement.style, { position: 'absolute', top: '0', right: '0', zIndex: '10' });
  renderPanel(gui);
  addEventListener('pagehide', () => gui.destroy(), { once: true });
}

export async function createGraph(
  options: ConstructorParameters<typeof Graph>[0],
  size: { width?: number; height?: number } = {},
  renderPanel?: (gui: GUI, graph: Graph) => void,
) {
  const container = document.createElement('div');
  Object.assign(container.style, {
    width: '100%',
    maxWidth: `${size.width || 600}px`,
    height: `${size.height || 400}px`,
  });
  document.getElementById('container')!.append(container);
  const graph = new Graph({ ...size, ...options, width: container.clientWidth, container, autoResize: true });
  addEventListener('pagehide', () => graph.destroy(), { once: true });
  await graph.render();
  if (renderPanel) addPanel((gui) => renderPanel(gui, graph));
  return container;
}
```
