♾ A Graph Visualization Framework in JavaScript.

RAW Rules
298 Discovered Rules ~600,323 Estimated Tokens

Cursor Rule: translation

.cursor/rules/translation.mdc cursor-rule ~872 tokens
Raw
Description: 翻译
---
description: 翻译
globs:
alwaysApply: false
---
# Translation Guidelines for site/docs

When translating files under the `site/docs` directory, please adhere to the following guidelines:

1. **Consistency in Terminology**: Ensure that terminology is consistent throughout the document. Use a glossary if available to maintain uniformity in terms.

   **Glossary**:

   - 画布 (Canvas)
   - 元素 (Element)
   - 节点 (Node)
   - 边 (Edge)
   - 组合 (Combo)
   - 交互 (Behavior)
   - 布局 (Layout)
   - 插件 (Plugin)
   - 动画 (Animation)
   - 数据处理 (Transform)
   - 色板 (Palette)
   - 配置项 (Option)
   - 图数据 (Graph Data)
   - 树图 (Tree Graph)
   - 属性 (Property)
   - 描述 (Description)
   - 类型 (Type)
   - 默认值 (Default Value)
   - 必选 (Required)

2. **Adjust Hyperlinks**: Review and adjust hyperlinks to ensure they point to the correct translated sections or documents. Verify that all links are functional and correctly formatted.

   - **Internal Links**: In the English version, all internal links should have a `/en` prefix, while the Chinese version should not have any prefix. Ensure this prefix is added to all internal links in English documents to avoid any oversight.
   - **Anchor Points**: For anchor points following a `#`, if they contain Chinese characters, they should be adjusted to match the corresponding title in the English version rather than being directly translated.
   - **External Links**: Convert external links appropriately to ensure they align with the language and context of the document.

3. **Direct Writing to Translated Documents**: Translations should be stored in corresponding `.en.md` or `.zh.md` files within the same directory. Ensure that the translated content is placed in the correct location within the document.

   - When translating from Chinese to English, create or update the `.en.md` file in the same directory.
   - When translating from English to Chinese, create or update the `.zh.md` file in the same directory.

4. **Support for Partial Content Translation**: Allow for the selection and translation of specific sections of content. Translated sections should be inserted into the appropriate location within the document, maintaining the logical flow and structure.

   - **Full Document Translation**: If the entire document is selected for translation, replace the entire content with the translated version.
   - **Partial Content Translation**: If only specific sections are selected, find the appropriate place to replace or insert the translated content, ensuring the document's logical flow and structure are maintained.

5. **Contextual Translation**: Avoid literal translations. Ensure that the translation fits the English context and conveys the intended meaning accurately.

6. **Direct Modification**: Translations should be directly modified in the corresponding `.en.md` or `.zh.md` files without returning the translated content separately. Ensure that the changes are saved in the correct file and location.

7. **Preserve Metadata Order**: Do not modify the `order` attribute in the page metadata during translation. This ensures that the document order remains consistent across different language versions.

8. **Add '/en' Prefix to Internal Links**: Ensure that all internal links in English documentation have the '/en' prefix to maintain consistency and correct navigation.

By following these guidelines, translations will be more accurate and consistent, facilitating easier review and integration into the documentation.
\n```\n\nVisit [G6 CDN Example](https://codesandbox.io/p/sandbox/using-g6-from-cdn-xt9ty6) to view the complete example code.\n\n\n\n---\ntitle: 安装\norder: 1\n---\n\n## npm\n\n```bash\n# npm\nnpm install @antv/g6 --save\n\n# pnpm\npnpm add @antv/g6\n\n# yarn\nyarn add @antv/g6\n```\n\n在代码中引入 G6:\n\n```js\nimport { Graph } from '@antv/g6';\n```\n\n访问 [G6 npm 示例](https://codesandbox.io/p/sandbox/using-g6-from-npm-d9spnr) 查看完整示例代码。\n\n## CDN\n\n`@antv/g6` 在多个 CDN 上提供:\n\n- unpkg: https://unpkg.com/@antv/g6@latest/dist/g6.min.js\n- jsDelivr: https://cdn.jsdelivr.net/npm/@antv/g6@5/dist/g6.min.js\n- npmmirror: https://registry.npmmirror.com/@antv/g6/5/files/dist/g6.min.js\n\n使用 `script` 标签引入 G6:\n\n```html\n\n```\n\n访问 [G6 CDN 示例](https://codesandbox.io/p/sandbox/using-g6-from-cdn-xt9ty6) 查看完整示例代码。\n\n\n\n---\ntitle: angular\norder: 2\n---\n\nRefer to the example below, you can use G6 in Angular, and you can also view the [Live Example](https://stackblitz.com/edit/g6-in-angular?file=src%2Fmain.ts)。\n\n\n\n\n\n---\ntitle: 在 Angular 中使用\norder: 2\n---\n\n参考下面的示例,你可以在 Angular 中使用 G6,也可以查看 [在线示例](https://stackblitz.com/edit/g6-in-angular?file=src%2Fmain.ts)。\n\n\n\n\n\n---\ntitle: react\norder: 0\n---\n\n## Non-Strict Mode\n\nRefer to the example below, you can use G6 in React, and you can also view the [Live Example](https://stackblitz.com/edit/g6-in-react?file=src/App.tsx) 。\n\n\n\n## Strict Mode\n\nIn strict mode, React intentionally mounts, unmounts, and remounts components in development. Create the Graph instance inside an effect, keep it in a ref, and destroy it in the cleanup callback so the first development-only mount does not leave a stale graph behind. The following complete example also shows how to register and render a React node.\n\n\n\n\n\n---\ntitle: 在 React 中使用\norder: 0\n---\n\n:::info{title=建议}\n如果你需要更完善的 React 与 G6 集成解决方案,可以使用 AntV 官方封装库 [`@antv/graphin`](https://github.com/antvis/graphin)。\n:::\n\n## 非严格模式\n\n参考下面的示例,你可以在 React 中使用 G6,也可以查看 [在线示例](https://stackblitz.com/edit/g6-in-react?file=src/App.tsx) 。\n\n\n\n## 严格模式\n\n在严格模式下,React 会在开发环境中有意执行挂载、卸载、再挂载。请把 Graph 实例放在 effect 里创建,用 ref 保存,并在清理函数中销毁,这样第一次开发态挂载不会留下旧实例。下面的完整示例同时演示了如何注册和渲染 React 节点。\n\n\n\n\n\n---\ntitle: vue\norder: 1\n---\n\n:::warning\nPlease do not pass Vue reactive data directly to the G6 instance, which may cause G6 to fail to render correctly, or even cause the page to crash.\n:::\n\nRefer to the example below, you can use G6 in Vue, and you can also view the [Live Example](https://stackblitz.com/edit/g6-in-vue?file=src/App.vue)。\n\n\n\n\n\n---\ntitle: 在 Vue 中使用\norder: 1\n---\n\n:::warning{title=注意}\n请不要将 Vue 响应式数据直接传递给 G6 实例,这可能会导致 G6 无法正确渲染,甚至导致页面崩溃。\n:::\n\n参考下面的示例,你可以在 Vue 中使用 G6,也可以查看 [在线示例](https://stackblitz.com/edit/g6-in-vue?file=src/App.vue)。\n\n\n\n\n\n---\ntitle: Quick Start\norder: 0\n---\n\n## Online Experience with G6\n\nVisit [Chart Examples](/en/examples) to experience G6 online without any environment setup.\n\n## Creating a Simple Graph\n\nIn this example, we will create a simple graph using G6 based on an HTML page.\n\nCopy the following code into an HTML file and then open this file in a browser:\n\n```html\n\n
\n\n\n\n\n\n```\n\nYou will get a graph as shown below:\n\n\n\nLet's analyze the following code snippet:\n\n1. First, we create a `div` element to serve as the container for the graph:\n\n```html\n
\n```\n\n2. Then, include the G6's JS file:\n\n```html\n\n```\n\n3. Use the `fetch` method to obtain the graph's data:\n\n```js\nfetch('https://assets.antv.antgroup.com/g6/graph.json').then((res) => res.json());\n```\n\n4. Finally, create an instance of the graph, pass in the configuration object, and call the `render` method to render the graph:\n\n```js\nconst { Graph } = G6;\n\nconst graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data,\n node: {\n style: {\n size: 10,\n },\n palette: {\n field: 'group',\n color: 'tableau',\n },\n },\n layout: {\n type: 'd3-force',\n manyBody: {},\n x: {},\n y: {},\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\nIf you are using frameworks such as React, Vue, Angular, etc., you can refer to:\n\n- [Using G6 in React](./integration/react)\n- [Using G6 in Vue](./integration/vue)\n- [Using G6 in Angular](./integration/angular)\n\n\n\n---\ntitle: 快速开始\norder: 0\n---\n\n## 在线体验 G6\n\n访问 [图表示例](/examples) 无需任何环境配置即可在线体验 G6。\n\n## 创建一个简单的图\n\n在本例子中,我们将基于 HTML 页面使用 G6 创建一个简单的图。\n\n将下面的代码复制到一个 HTML 文件中,然后在浏览器中打开这个文件:\n\n```html\n\n
\n\n\n\n\n\n```\n\n会得到如下所示的图:\n\n\n\n下面分析一下这段代码:\n\n1. 首先我们创建一个 `div` 元素作为图的容器:\n\n```html\n
\n```\n\n2. 然后引入 G6 的 JS 文件:\n\n```html\n\n```\n\n3. 使用 `fetch` 方法获取图的数据:\n\n```js\nfetch('https://assets.antv.antgroup.com/g6/graph.json').then((res) => res.json());\n```\n\n4. 最后创建一个图实例,传入配置对象,并调用 `render` 方法渲染图:\n\n```js\nconst { Graph } = G6;\n\nconst graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data,\n node: {\n style: {\n size: 10,\n },\n palette: {\n field: 'group',\n color: 'tableau',\n },\n },\n layout: {\n type: 'd3-force',\n manyBody: {},\n x: {},\n y: {},\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n如果你使用 React、Vue、Angular 等框架,可以查看:\n\n- [在 React 中使用 G6](./integration/react)\n- [在 Vue 中使用 G6](./integration/vue)\n- [在 Angular 中使用 G6](./integration/angular)\n\n\n\n---\ntitle: Step-by-step guide\norder: 3\n---\n\nThis tutorial will guide you through the development of a G6 chart from scratch, and along the way, you will learn and understand the main concepts of G6.\n\n## Create Application\n\nWe will use Vite to create a simple front-end application.\n\n### Initialization\n\nFirst, create an empty directory:\n\n```bash\nmkdir g6-tutorial\n\ncd g6-tutorial\n```\n\nInitialize the project:\n\n```bash\nnpm init -y\n```\n\nInstall G6:\n\n```bash\nnpm install @antv/g6 --save\n```\n\nVite is a new type of front-end build tool that is based on ESModule and can quickly start up projects.\n\nInstall Vite:\n\n```bash\nnpm install vite --save-dev\n```\n\nAdd a start script to the `package.json`:\n\n```json\n{\n \"scripts\": {\n \"dev\": \"vite\"\n }\n}\n```\n\n### Create Files\n\nCreate the `index.html` and `main.ts` files with the following content:\n\n**index.html**:\n\n```html\n\n\n \n @antv/g6 Tutorial\n \n \n
\n \n \n\n```\n\n**main.ts**:\n\n```typescript\nalert('Hello, G6!');\n```\n\n### Start project\n\n```bash\nnpm run dev\n```\n\nOpen a web browser and visit the address output in the terminal (typically: http://127.0.0.1:5173/), and you will see a pop-up displaying \"Hello, G6!\".\n\n## Creating a Simple Graph\n\nNext, we will create a simple chart using G6.\n\n### Preparing the Data\n\nG6 uses JSON-formatted data to describe the graph, which usually includes nodes and edges. We will use the following prepared data:\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', style: { x: 50, y: 50 } },\n { id: 'node-2', style: { x: 150, y: 50 } },\n ],\n edges: [{ source: 'node-1', target: 'node-2' }],\n};\n```\n\nThe data includes two nodes and one edge. The `id` attribute for nodes is mandatory, and the position of each node is set in the `style`. The `source` and `target` attributes of the edge represent the `id` of the starting node and the ending node, respectively.\n\n### Creating and Drawing the Graph\n\nCreate an instance of the Graph, pass in a configuration object that includes the container and data, and then call the `render` method to draw the Graph:\n\n```typescript\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: {\n nodes: [\n { id: 'node-1', style: { x: 50, y: 50 } },\n { id: 'node-2', style: { x: 150, y: 50 } },\n ],\n edges: [{ source: 'node-1', target: 'node-2' }],\n },\n});\n\ngraph.render();\n```\n\nAs shown below, you can see that the chart has been successfully drawn:\n\n\n\n### Element\n\nNext, we will introduce how to configure the style and types of elements in the canvas.\n\nG6 provides various mechanisms to configure element styles, which can be done in the data itself or within the chart instance. In the previous example, we configured the position of the nodes in the data. Next, we will configure the styles of nodes and edges in the graph configuration options:\n\n\n\n```js\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: {\n nodes: [\n { id: 'node-1', style: { x: 50, y: 50 } },\n { id: 'node-2', style: { x: 150, y: 50 } },\n ],\n edges: [{ source: 'node-1', target: 'node-2' }],\n },\n node: {\n style: {\n fill: 'pink',\n },\n },\n edge: {\n style: {\n stroke: 'lightgreen',\n },\n },\n});\n\ngraph.render();\n```\n\nAs the code shows, we have configured the fill color of the nodes to be pink and the stroke color of the edges to be light green within the chart instance. You can see the effect in the example below:\n\n\n\nThe key parts are the `node.style` and `edge.style` options, which are used to configure the styles of nodes and edges, respectively.\n\n> In the subsequent code examples, we will only display the parts of the options. For the complete code of this project, please refer to the [Complete Example](https://codesandbox.io/s/g6-tutorial).\n\nNext, we will demonstrate more types of nodes by setting the node type:\n\n```js\n{\n node: {\n type: (datum) => datum.id === 'node-1' ? 'circle' : 'rect',\n style: {\n fill: 'pink',\n size: 20\n }\n }\n}\n```\n\nIn the code above, we set the `type` attribute of the node, which can be a string or a function. When `type` is a function, the argument of the function is the current node's data object, and the return value is the type of the node.\n\n> Similarly, each attribute under the `style` style of an element can also be a function, with the argument being the current element's data object.\n\n> You can even set the entire `style` property as a function, allowing you to dynamically set the element's style based on the data object.\n\nThe circular node (`circle`) is the default node type in G6. Here, we set the type of the first node to a circle and the type of the second node to a rectangle.\n\nAt the same time, we also set the size of the nodes to 20, so the first node is a circle with a radius of 10, and the second node is a square with a side length of 20.\n\n> If you want to set the size of the rectangular node to 20x10, you can set `size` to an array `[20, 10]`.\n\nYou can see the effect in the example below:\n\n\n\n### Behaviors\n\nThe chart provided in the previous example is static. Next, we will add some interactive behaviors.\n\nG6 offers a variety of interactive behaviors. We will add a few commonly used Behaviors to allow users to drag, zoom the canvas, and drag nodes.\n\n```js\n{\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'];\n}\n```\n\nTry dragging nodes and the canvas in the example below, and use the scroll wheel to zoom in and out on the canvas:\n\n\n\n### Layout\n\nIn the previous example, we manually set the positions of the nodes. However, this can become very difficult when there are many nodes.\n\nLayout algorithms can automatically adjust the positions of nodes based on certain rules. G6 provides a variety of layout algorithms, such as tree layout, force-directed layout, and so on.\n\nFirst, generate a set of data that does not include position information:\n\n```js\nconst data = {\n nodes: Array.from({ length: 10 }).map((_, i) => ({ id: `node-${i}` })),\n edges: Array.from({ length: 9 }).map((_, i) => ({ source: `node-0`, target: `node-${i + 1}` })),\n};\n```\n\nBy default, if a node does not have position information, G6 will place the node at the top-left corner, that is, at the coordinates `(0, 0)`.\n\nNext, we will use the `d3-force` layout algorithm, which is a force-directed layout algorithm that can simulate the forces of attraction and repulsion between nodes, allowing the nodes to automatically adjust to suitable positions.\n\n```js\n{\n layout: {\n type: 'd3-force',\n },\n}\n```\n\nView the example below, and you can see that the nodes have automatically adjusted to suitable positions:\n\n
\nComplete Code\n\n```js\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: {\n nodes: Array.from({ length: 10 }).map((_, i) => ({ id: `node-${i}` })),\n edges: Array.from({ length: 9 }).map((_, i) => ({ source: `node-0`, target: `node-${i + 1}` })),\n },\n node: {\n style: {\n size: 20,\n fill: 'pink',\n },\n },\n edge: {\n style: {\n stroke: 'lightgreen',\n },\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n layout: {\n type: 'd3-force',\n },\n});\n\ngraph.render();\n```\n\n
\n\n\n\n### Palette\n\nSimilarly, when there are many nodes, manually setting the color of each node can become difficult. G6 provides a palette mechanism that makes it easy to assign colors to elements.\n\nPalettes typically assign colors to elements based on a specific field in the data, such as the type of node, the weight of an edge, etc.\n\nBelow, we add a `category` field in the data:\n\n```js\nconst data = {\n nodes: Array.from({ length: 10 }).map((_, i) => ({\n id: `node-${i}`,\n data: { category: i === 0 ? 'central' : 'around' },\n })),\n edges: Array.from({ length: 9 }).map((_, i) => ({ source: `node-0`, target: `node-${i + 1}` })),\n};\n```\n\nThen, use the `tableau` palette to set the colors for the nodes, where the `field` attribute specifies the field in the data, and the `color` attribute specifies the name of the palette.\n\n```js\n{\n node: {\n palette: {\n field: 'category',\n color: 'tableau',\n }\n }\n}\n```\n\n> It is important to note that the `fill` style in `node.style` should be removed, as its priority is higher than the colors assigned by the palette.\n\n\n\n### Plugins\n\nThe plugin mechanism is an important feature of G6, which allows you to extend the functionality of G6 through plugins. G6 provides a wealth of built-in plugins, such as `tooltip`, `legend`, etc., and also supports user-defined plugins.\n\nBelow, we will use the `grid-line` plugin to add grid lines to the canvas:\n\n```js\n{\n plugins: ['grid-line'],\n}\n```\n\nYou can see that grid lines have been added to the canvas:\n\n\n\nThe plugin configuration mentioned above used a shorthand form. Most plugins support the passing of additional parameters. For example, the `grid-line` plugin allows you to configure the `follow` property to specify whether the grid lines should follow the canvas when it is dragged.\n\n```js\n{\n plugins: [{ type: 'grid-line', follow: true }];\n}\n```\n\nTry dragging the canvas in the example below, and you will see that the grid lines move along with the canvas:\n\n\n\n## Summary\n\nIn this tutorial, we created a G6 chart from scratch and became acquainted with the main concepts of G6. We learned how to create a simple chart, how to configure the style and types of elements, how to add interactive behaviors, how to use layout algorithms, how to use palettes, and how to use plugins.\n\nFor a more detailed introduction to the concepts of G6, you can refer to [Core Concepts](/en/manual/graph/graph).\n\nDetailed explanations of options such as elements, layouts, and plugins can be found in the [API](/en/api/data).\n\n\n\n---\ntitle: 详细教程\norder: 3\n---\n\n本教程将引导你从头开始完成一个 G6 图表开发,并在过程中了解和学习 G6 的主要概念。\n\n## 创建应用\n\n我们将使用 Vite 来创建一个简单的前端应用。\n\n### 初始化\n\n首先创建一个空目录:\n\n```bash\nmkdir g6-tutorial\n\ncd g6-tutorial\n```\n\n初始化项目:\n\n```bash\nnpm init -y\n```\n\n安装 G6:\n\n```bash\nnpm install @antv/g6 --save\n```\n\nVite 是一个新型的前端构建工具,它基于 ESModule,可以快速启动项目。\n\n安装 Vite:\n\n```bash\nnpm install vite --save-dev\n```\n\n在 `package.json` 中添加启动脚本:\n\n```json\n{\n \"scripts\": {\n \"dev\": \"vite\"\n }\n}\n```\n\n### 创建文件\n\n创建 `index.html` 和 `main.ts` 文件,内容如下:\n\n**index.html**:\n\n```html\n\n\n \n @antv/g6 Tutorial\n \n \n
\n \n \n\n```\n\n**main.ts**:\n\n```typescript\nalert('Hello, G6!');\n```\n\n### 启动项目\n\n```bash\nnpm run dev\n```\n\n打开浏览器访问终端中输出的地址(通常为:http://127.0.0.1:5173/ ),你将看到一个弹窗显示 \"Hello, G6!\"。\n\n## 创建一个简单的图表\n\n接下来,我们将使用 G6 创建一个简单的图表。\n\n### 准备数据\n\nG6 使用 JSON 格式的数据来描述图,通常包括节点和边。我们将使用下面准备的数据:\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', style: { x: 50, y: 50 } },\n { id: 'node-2', style: { x: 150, y: 50 } },\n ],\n edges: [{ source: 'node-1', target: 'node-2' }],\n};\n```\n\n数据中包括两个节点和一条边,节点的 `id` 属性是必须的,并在 `style` 设置了每个节点的位置。边的 `source` 和 `target` 属性分别表示边的起始节点 `id` 和结束节点 `id`。\n\n### 创建并绘制图表\n\n创建一个图表实例,传入一个配置对象,包括容器和数据,然后调用 `render` 方法渲染图表:\n\n```typescript\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: {\n nodes: [\n { id: 'node-1', style: { x: 50, y: 50 } },\n { id: 'node-2', style: { x: 150, y: 50 } },\n ],\n edges: [{ source: 'node-1', target: 'node-2' }],\n },\n});\n\ngraph.render();\n```\n\n如下所示,可以看到图表已经顺利绘制出来:\n\n\n\n### 元素\n\n接下来将介绍如何配置画布中的元素样式和种类。\n\nG6 提供了多种机制来配置元素样式,可以在数据中进行配置,也可以在图表实例中进行配置。前面的示例中,我们在数据中配置了节点的位置,接下来我们在图配置项中配置节点和边的样式:\n\n\n\n```js\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: {\n nodes: [\n { id: 'node-1', style: { x: 50, y: 50 } },\n { id: 'node-2', style: { x: 150, y: 50 } },\n ],\n edges: [{ source: 'node-1', target: 'node-2' }],\n },\n node: {\n style: {\n fill: 'pink',\n },\n },\n edge: {\n style: {\n stroke: 'lightgreen',\n },\n },\n});\n\ngraph.render();\n```\n\n正如代码所示,我们在图表实例中配置了节点的填充颜色为粉色,边的描边颜色为浅绿色。你可以在下面的示例中看到效果:\n\n\n\n其中的关键部分是 `node.style` 和 `edge.style` 配置项,分别用来配置节点和边的样式。\n\n> 在后续的代码示例中,我们仅展示配置项的部分代码,本项目的完整代码请查看 [完整示例](https://codesandbox.io/s/g6-tutorial)。\n\n下面我们将通过设置节点的类型来展示更多的节点种类:\n\n```js\n{\n node: {\n type: (datum) => datum.id === 'node-1' ? 'circle' : 'rect',\n style: {\n fill: 'pink',\n size: 20\n }\n }\n}\n```\n\n上面的代码中,我们设置了节点的 `type` 属性,其值可以是一个字符串,也可以是一个函数。当 `type` 是一个函数时,函数的参数是当前节点的数据对象,函数的返回值是节点的类型。\n\n> 同样的,元素中 `style` 样式下的每个属性都可以是一个函数,函数的参数是当前元素的数据对象。\n\n> 你甚至可以将整个 `style` 属性设置为一个函数,这样你可以根据数据对象动态设置元素的样式。\n\n圆形节点(`circle`)是 G6 的默认节点类型,这里我们将第一个节点的类型设置为圆形,第二个节点的类型设置为矩形。\n\n同时我们还将节点的大小设置为 20,因此第一个节点是一个半径为 10 的圆形,第二个节点是一个边长为 20 的正方形。\n\n> 如果你想将矩形节点的大小设置为 20x10,可以将 `size` 设置为一个数组 `[20, 10]`。\n\n你可以在下面的示例中看到效果:\n\n\n\n### 交互\n\n在上面的例子中提供的图表是静态的,接下来我们将添加一些交互行为。\n\nG6 提供了多种交互行为,我们添加几个常用的交互,使得用户可以拖拽、缩放画布,拖拽节点。\n\n```js\n{\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'];\n}\n```\n\n尝试在下面的示例中拖拽节点和画布,并使用滚轮缩放画布:\n\n\n\n### 布局\n\n在上面的示例中,我们手动设置了节点的位置。但当节点数量较多时,这会变得非常困难。\n\n布局算法可以基于一定的规则自动调整节点的位置,G6 提供了多种布局算法,例如树形布局、力导向布局等。\n\n首先生成一组不包括位置信息的数据:\n\n```js\nconst data = {\n nodes: Array.from({ length: 10 }).map((_, i) => ({ id: `node-${i}` })),\n edges: Array.from({ length: 9 }).map((_, i) => ({ source: `node-0`, target: `node-${i + 1}` })),\n};\n```\n\n默认情况下,如果节点没有位置信息,G6 会将节点放置在左上角,即 `(0, 0)`。\n\n接下来我们使用 `d3-force` 布局算法,它是一种力导向布局算法,可以模拟节点之间的引力和斥力,使得节点自动调整到合适的位置。\n\n```js\n{\n layout: {\n type: 'd3-force',\n },\n}\n```\n\n查看下面的示例,可以看到节点已经自动调整到合适的位置:\n\n
\n完整代码\n\n```js\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: {\n nodes: Array.from({ length: 10 }).map((_, i) => ({ id: `node-${i}` })),\n edges: Array.from({ length: 9 }).map((_, i) => ({ source: `node-0`, target: `node-${i + 1}` })),\n },\n node: {\n style: {\n size: 20,\n fill: 'pink',\n },\n },\n edge: {\n style: {\n stroke: 'lightgreen',\n },\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n layout: {\n type: 'd3-force',\n },\n});\n\ngraph.render();\n```\n\n
\n\n\n\n### 色板\n\n同样的,当节点数量较多时,手动设置节点的颜色也会变得困难。G6 提供了色板机制,可以便捷地为元素设置颜色。\n\n色板通常会基于数据的某个字段为元素设置颜色,例如节点的类型、边的权重等。\n\n下面我们在数据中的添加 `category` 字段:\n\n```js\nconst data = {\n nodes: Array.from({ length: 10 }).map((_, i) => ({\n id: `node-${i}`,\n data: { category: i === 0 ? 'central' : 'around' },\n })),\n edges: Array.from({ length: 9 }).map((_, i) => ({ source: `node-0`, target: `node-${i + 1}` })),\n};\n```\n\n然后使用 `tableau` 色板为节点设置颜色,`field` 属性指定了数据中的字段,`color` 属性指定了色板的名称。\n\n```js\n{\n node: {\n palette: {\n field: 'category',\n color: 'tableau',\n }\n }\n}\n```\n\n> 需要注意将 `node.style` 中的 `fill` 样式移除,因为其优先级高于色板分配的颜色。\n\n\n\n### 插件\n\n插件机制是 G6 的一个重要特性,可以通过插件扩展 G6 的功能。G6 提供了丰富的内置插件,例如 `tooltip`、`legend` 等,也支持用户自定义插件。\n\n下面我们将使用 `grid-line` 插件为画布添加网格线:\n\n```js\n{\n plugins: ['grid-line'],\n}\n```\n\n可以看到画布已经添加了网格线:\n\n\n\n上面的插件配置项中使用了简写形式,大部分的插件都支持传递额外的参数,例如 `grid-line` 插件可以配置 `follow` 属性来指定拖拽画布时网格线是否跟随画布移动。\n\n```js\n{\n plugins: [{ type: 'grid-line', follow: true }];\n}\n```\n\n尝试在下面的示例中拖拽画布,可以看到网格线跟随画布移动:\n\n\n\n## 小结\n\n在本教程中,我们从头开始创建了一个 G6 图表,并了解了 G6 的主要概念。我们学习了如何创建一个简单的图表,如何配置元素的样式和种类,如何添加交互行为,如何使用布局算法,如何使用色板,如何使用插件。\n\n关于 G6 更加详细的概念介绍可以在 [核心概念](/manual/graph/graph) 中查看。\n\n图的 API 详细说明可以在 [API](/api/graph) 中查看。\n\n\n\n---\ntitle: extension\norder: 9\n---\n\n## Concept\n\nExtension is an important concept in G6, it is a general term for all expandable parts in G6, including the following types:\n\n- Animation\n- Behavior\n- Element\n - Node\n - Edge\n - Combo\n- Layout\n- Palette\n- Plugin\n- Theme\n- Transform\n\n## Register Extension\n\nG6 provides the `register` function for registering extensions, for example:\n\n```typescript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { CustomNode } from './my-custom-node';\n\n// # Registering Nodes\nregister(ExtensionCategory.NODE, 'custom-node', CustomNode);\n```\n\nThe first parameter of the `register` function is the type of the extension, the second parameter is the name of the extension, and the third parameter is the implementation of the extension(refer to the custom-related sections in the documentation for each extension type).\n\nDifferent types of extensions **can** use the same extension name, but when registering extensions of the same type, only the first registration will take effect.\n\n```typescript\n// ✅\nregister(ExtensionCategory.NODE, 'custom-name', CustomNode);\nregister(ExtensionCategory.COMBO, 'custom-name', CustomCombo);\n\n// ❌\nregister(ExtensionCategory.NODE, 'custom-name', CustomNode);\nregister(ExtensionCategory.NODE, 'custom-name', CustomNode);\n```\n\n## Use Extension\n\nThe configuration location for different types of extensions varies, but all are used by specifying the name that was used during registration, for example:\n\n- Using node extensions: `options.node.type`\n- Using edge extensions: `options.edge.type`\n- Using combo extensions: `options.combo.type`\n- Using behavior extensions: `options.behaviors`\n- Using layout extensions: `options.layout.type`\n- Using plugin extensions: `options.plugins`\n- Using theme extensions: `options.theme`\n- Using data transform extensions: `options.transform`\n- Using palette extensions: `options.node.palette`, `options.edge.palette`, etc.\n- Using animation extensions: `options.node.animate`, `options.edge.animate`, etc.\n\n## Get Extension\n\nG6 provides the `getExtension` and `getExtensions` methods to obtain a single extension and all extensions of a specified type, respectively, for example:\n\n```typescript\nimport { getExtension, getExtensions, ExtensionCategory } from '@antv/g6';\n\n// To get the implementation of the node extension registered with the name 'custom-node'\ngetExtension(ExtensionCategory.NODE, 'custom-node');\n\n// Retrieve all registered node extension implementations\ngetExtensions(ExtensionCategory.NODE);\n```\n\n\n\n---\ntitle: Extension - 扩展\norder: 9\n---\n\n## 概念\n\n扩展 (Extension) 是 G6 中的一个重要概念,它是 G6 中所有可扩展部分的统称,包括以下几种:\n\n- 动画 (Animation)\n- 交互 (Behavior)\n- 元素 (Element)\n- 节点 (Node)\n - 边 (Edge)\n - 组合 (Combo)\n- 布局 (Layout)\n- 色板 (Palette)\n- 插件 (Plugin)\n- 主题 (Theme)\n- 数据转换 (Transform)\n\n## 注册扩展\n\nG6 提供了 `register` 函数用于注册扩展,例如:\n\n```typescript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { CustomNode } from './my-custom-node';\n\n// 注册节点\nregister(ExtensionCategory.NODE, 'custom-node', CustomNode);\n```\n\n`register` 函数的第一个参数是扩展的类型,第二个参数是扩展的名称,第三个参数是扩展的实现(扩展实现参考各扩展类型所在文档的自定义相关章节)。\n\n不同的扩展类型之间**可以**使用相同的扩展名称,但同一类型的扩展重复注册时仅会在第一次注册时生效。\n\n\n\n```typescript\n// ✅\nregister(ExtensionCategory.NODE, 'custom-name', CustomNode);\nregister(ExtensionCategory.COMBO, 'custom-name', CustomCombo);\n\n// ❌\nregister(ExtensionCategory.NODE, 'custom-name', CustomNode);\nregister(ExtensionCategory.NODE, 'custom-name', CustomNode);\n```\n\n## 使用扩展\n\n不同的扩展类型的配置位置有所不同,但都是通过指定注册时所使用的名称来使用扩展,例如:\n\n- 使用节点扩展:`options.node.type`\n- 使用边扩展:`options.edge.type`\n- 使用组合扩展:`options.combo.type`\n- 使用交互扩展:`options.behaviors`\n- 使用布局扩展:`options.layout.type`\n- 使用插件扩展:`options.plugins`\n- 使用主题扩展:`options.theme`\n- 使用数据转换扩展:`options.transform`\n- 使用色板扩展:`options.node.palette` `options.edge.palette` 等\n- 使用动画扩展:`options.node.animate` `options.edge.animate` 等\n\n## 获取扩展\n\nG6 提供了 `getExtension` 和 `getExtensions` 方法分别用于获取指定扩展类型下的单个扩展和所有扩展,例如:\n\n```typescript\nimport { getExtension, getExtensions, ExtensionCategory } from '@antv/g6';\n\n// 获取注册的名称为 'custom-node' 的节点扩展实现\ngetExtension(ExtensionCategory.NODE, 'custom-node');\n\n// 获取所有注册的节点扩展实现\ngetExtensions(ExtensionCategory.NODE);\n```\n\n\n\n---\n\ntitle: Built-in Extensions\norder: 4\n\nThe G6 built-in extensions and registered types are as follows:\n\n## Animations\n\n| Extension | Registration Type |\n| ------------- | ----------------- |\n| ComboCollapse | 'combo-collapse' |\n| ComboExpand | 'combo-expand' |\n| NodeCollapse | 'node-collapse' |\n| NodeExpand | 'node-expand' |\n| PathIn | 'path-in' |\n| PathOut | 'path-out' |\n| Fade | 'fade' |\n| Translate | 'translate' |\n\nUsage:\n\nIn `GraphOptions.[node|edge|combo].animation.[stage]`, for example:\n\n```ts\nconst graph = new Graph({\n // ... other options\n node: {\n animation: {\n update: 'translate', // Only use translation animation in the update stage\n },\n },\n});\n```\n\n## Behaviors\n\n| Extension | Registration Type | Description |\n| ------------------------- | ----------------------------- | ---------------------------------------------- |\n| BrushSelect | 'brush-select' | / |\n| ClickSelect | 'click-select' | / |\n| CollapseExpand | 'collapse-expand' | / |\n| CreateEdge | 'create-edge' | / |\n| DragCanvas | 'drag-canvas' | / |\n| DragElementForce | 'drag-element-force' | Drag element when use d3-force layout |\n| DragElement | 'drag-element' | / |\n| FixElementSize | 'fix-element-size' | Keep the size of element during zooming canvas |\n| FocusElement | 'focus-element' | / |\n| HoverActivate | 'hover-activate' | / |\n| LassoSelect | 'lasso-select' | / |\n| OptimizeViewportTransform | 'optimize-viewport-transform' | Hide elements during manipulate the canvas |\n| ScrollCanvas | 'scroll-canvas' | / |\n| ZoomCanvas | 'zoom-canvas' | / |\n\nUsage:\n\nIn `GraphOptions.behaviors`, for example:\n\n```ts\nconst graph = new Graph({\n // ... other options\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n});\n```\n\n## Elements\n\n### Nodes\n\n| Extension | Registration Type |\n| --------- | ----------------- |\n| circle | Circle |\n| diamond | Diamond |\n| ellipse | Ellipse |\n| hexagon | Hexagon |\n| html | HTML |\n| image | Image |\n| rect | Rect |\n| star | Star |\n| donut | Donut |\n| triangle | Triangle |\n\nUsage:\n\n1. In `GraphOptions.data.nodes[number].type`;\n2. In `GraphOptions.node.type`;\n\n```ts\nconst graph = new Graph({\n // ... other options\n data: {\n nodes: [{ id: 'node-1', type: 'circle' }],\n },\n node: {\n type: 'circle',\n },\n});\n```\n\n### Edges\n\n| Extension | Registration Type | Description |\n| --------------- | ------------------ | ----------------------------- |\n| Cubic | 'cubic' | Cubic Bezier Curve |\n| Line | 'line' | / |\n| Polyline | 'polyline' | / |\n| Quadratic | 'quadratic' | Quadratic Bezier Curve |\n| CubicHorizontal | 'cubic-horizontal' | Horizontal Cubic Bezier Curve |\n| CubicVertical | 'cubic-vertical' | Vertical Cubic Bezier Curve |\n| CubicRadial | 'cubic-radial' | Radial Cubic Bezier Curve |\n\nUsage(like `Nodes`):\n\n1. In `GraphOptions.data.edges[number].type`;\n2. In `GraphOptions.edge.type`;\n\n### Combos\n\n| Extension | Registration Type |\n| ----------- | ----------------- |\n| CircleCombo | 'circle' |\n| RectCombo | 'rect' |\n\nUsage(like `Nodes`):\n\n1. In `GraphOptions.data.combos[number].type`;\n2. In `GraphOptions.combo.type`;\n\n## Layouts\n\n| Extension | Registration Type | Description |\n| ------------------- | ----------------- | ------------------------------- |\n| AntVDagreLayout | 'antv-dagre' | / |\n| ComboCombinedLayout | 'combo-combined' | / |\n| CompactBoxLayout | 'compact-box' | / |\n| ForceAtlas2Layout | 'force-atlas2' | / |\n| CircularLayout | 'circular' | / |\n| ConcentricLayout | 'concentric' | / |\n| D3ForceLayout | 'd3-force' | / |\n| DagreLayout | 'dagre' | / |\n| DendrogramLayout | 'dendrogram' | / |\n| ForceLayout | 'force' | / |\n| FruchtermanLayout | 'fruchterman' | / |\n| GridLayout | 'grid' | / |\n| IndentedLayout | 'indented' | / |\n| MDSLayout | 'mds' | Multidimensional Scaling Layout |\n| MindmapLayout | 'mindmap' | / |\n| RadialLayout | 'radial' | / |\n| RandomLayout | 'random' | / |\n\nUsage:\n\nIn `GraphOptions.layout`, for example:\n\n```ts\nconst graph = new Graph({\n // ... other options\n layout: {\n type: 'force',\n },\n});\n```\n\n## Palettes\n\n\n\nUsage:\n\nIn `GraphOptions.[node|edge|combo].palette`, for example:\n\n```ts\nconst graph = new Graph({\n // ... other options\n node: {\n palette: 'tableau',\n },\n});\n```\n\n## Themes\n\n| Registration Type |\n| ----------------- |\n| dark |\n| light |\n\nUsage:\n\nIn `GraphOptions.theme`, for example:\n\n```ts\nconst graph = new Graph({\n // ... other options\n theme: 'dark',\n});\n```\n\n## Plugins\n\n| Extension | Registration Type |\n| -------------- | ------------------ |\n| BubbleSets | 'bubble-sets' |\n| EdgeFilterLens | 'edge-filter-lens' |\n| GridLine | 'grid-line' |\n| Background | 'background' |\n| Contextmenu | 'contextmenu' |\n| Fisheye | 'fisheye' |\n| Fullscreen | 'fullscreen' |\n| History | 'history' |\n| Hull | 'hull' |\n| Legend | 'legend' |\n| Minimap | 'minimap' |\n| Snapline | 'snapline' |\n| Timebar | 'timebar' |\n| Toolbar | 'toolbar' |\n| Tooltip | 'tooltip' |\n| Watermark | 'watermark' |\n\nUsage:\n\nIn `GraphOptions.plugins`, for example:\n\n```ts\nconst graph = new Graph({\n // ... other options\n plugins: ['minimap', 'contextmenu'],\n});\n```\n\n## Transforms\n\n| Extension | Registration Type | Description |\n| -------------------- | ------------------------ | ----------- |\n| ProcessParallelEdges | 'process-parallel-edges' | / |\n| PlaceRadialLabels | 'place-radial-labels' | 径向标签 |\n\nUsage:\n\nIn `GraphOptions.transform`, for example:\n\n```ts\nconst graph = new Graph({\n // ... other options\n transform: ['process-parallel-edges', 'place-radial-labels'],\n});\n```\n\n## Shapes\n\n| Registration Type |\n| ----------------- |\n| circle |\n| ellipse |\n| group |\n| html |\n| image |\n| line |\n| path |\n| polygon |\n| polyline |\n| rect |\n| text |\n| label |\n| badge |\n\nUsage:\n\nIn the [upsert](http://localhost:8000/en/manual/custom-extension/element#methods) method of the element class when customizing the shape, pass the second parameter:\n\n```ts\nthis.upsert('shape-key', 'text', { text: 'label', fontSize: 16 }, this);\n```\n\n\n\n---\ntitle: 内置扩展\norder: 4\n---\n\nG6 内置扩展及注册扩展类型如下:\n\n## 动画\n\n| 扩展 | 注册类型 | 描述 |\n| ------------- | ---------------- | -------- |\n| ComboCollapse | 'combo-collapse' | 组合收起 |\n| ComboExpand | 'combo-expand' | 组合展开 |\n| NodeCollapse | 'node-collapse' | 节点收起 |\n| NodeExpand | 'node-expand' | 节点展开 |\n| PathIn | 'path-in' | 路径进入 |\n| PathOut | 'path-out' | 路径退出 |\n| Fade | 'fade' | 渐变 |\n| Translate | 'translate' | 平移 |\n\n配置方式:\n\n在 `GraphOptions.[node|edge|combo].animation.[stage]` 中使用,示例:\n\n```ts\nconst graph = new Graph({\n // ... 其他配置\n node: {\n animation: {\n update: 'translate', // 更新阶段仅使用平移动画\n },\n },\n});\n```\n\n## 交互\n\n| 扩展 | 注册类型 | 描述 |\n| ------------------------- | ----------------------------- | ---------------------- |\n| BrushSelect | 'brush-select' | 框选 |\n| ClickSelect | 'click-select' | 点击选中 |\n| CollapseExpand | 'collapse-expand' | 展开/收起元素 |\n| CreateEdge | 'create-edge' | 创建边 |\n| DragCanvas | 'drag-canvas' | 拖拽画布 |\n| DragElementForce | 'drag-element-force' | 力导向拖拽元素 |\n| DragElement | 'drag-element' | 拖拽元素 |\n| FixElementSize | 'fix-element-size' | 缩放画布时固定元素大小 |\n| FocusElement | 'focus-element' | 聚焦元素 |\n| HoverActivate | 'hover-activate' | 悬停激活 |\n| LassoSelect | 'lasso-select' | 套索选择 |\n| OptimizeViewportTransform | 'optimize-viewport-transform' | 操作画布时隐藏元素 |\n| ScrollCanvas | 'scroll-canvas' | 滚动画布 |\n| ZoomCanvas | 'zoom-canvas' | 缩放画布 |\n\n配置方式:\n\n在 `GraphOptions.behaviors` 中配置,示例:\n\n```ts\nconst graph = new Graph({\n // ... 其他配置\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n});\n```\n\n## 元素\n\n### 节点\n\n| 扩展 | 注册类型 | 描述 |\n| -------- | -------- | ---------- |\n| circle | Circle | 圆形节点 |\n| diamond | Diamond | 菱形节点 |\n| ellipse | Ellipse | 椭圆节点 |\n| hexagon | Hexagon | 六边形节点 |\n| html | HTML | HTML节点 |\n| image | Image | 图片节点 |\n| rect | Rect | 矩形节点 |\n| star | Star | 星形节点 |\n| donut | Donut | 甜甜圈节点 |\n| triangle | Triangle | 三角形节点 |\n\n配置方式:\n\n1. 在 `GraphOptions.data.nodes[number].type` 中配置;\n2. 在 `GraphOptions.node.type` 中配置;\n\n```ts\nconst graph = new Graph({\n // ... 其他配置\n data: {\n nodes: [{ id: 'node-1', type: 'circle' }],\n },\n node: {\n type: 'circle',\n },\n});\n```\n\n### 边\n\n| 扩展 | 注册类型 | 描述 |\n| --------------- | ------------------ | ------------------ |\n| Cubic | 'cubic' | 三次贝塞尔曲线 |\n| Line | 'line' | 直线 |\n| Polyline | 'polyline' | 折线 |\n| Quadratic | 'quadratic' | 二次贝塞尔曲线 |\n| CubicHorizontal | 'cubic-horizontal' | 水平三次贝塞尔曲线 |\n| CubicVertical | 'cubic-vertical' | 垂直三次贝塞尔曲线 |\n| CubicRadial | 'cubic-radial' | 径向三次贝塞尔曲线 |\n\n配置方式同 `节点`:\n\n1. 在 `GraphOptions.data.edges[number].type` 中配置;\n2. 在 `GraphOptions.edge.type` 中配置;\n\n### 组合\n\n| 扩展 | 注册类型 | 描述 |\n| ----------- | -------- | -------- |\n| CircleCombo | 'circle' | 圆形组合 |\n| RectCombo | 'rect' | 矩形组合 |\n\n配置方式同 `节点`:\n\n1. 在 `GraphOptions.data.combos[number].type` 中配置;\n2. 在 `GraphOptions.combo.type` 中配置;\n\n## 布局\n\n| 扩展 | 注册类型 | 描述 |\n| ------------------- | ---------------- | ---------------------- |\n| AntVDagreLayout | 'antv-dagre' | AntV Dagre 布局 |\n| ComboCombinedLayout | 'combo-combined' | 组合布局 |\n| CompactBoxLayout | 'compact-box' | 紧凑树 |\n| ForceAtlas2Layout | 'force-atlas2' | ForceAlas2 力导向布局 |\n| CircularLayout | 'circular' | 环形布局 |\n| ConcentricLayout | 'concentric' | 同心圆布局 |\n| D3ForceLayout | 'd3-force' | D3 力导向布局 |\n| DagreLayout | 'dagre' | Dagre 布局 |\n| DendrogramLayout | 'dendrogram' | 生态树 |\n| ForceLayout | 'force' | 力导向布局 |\n| FruchtermanLayout | 'fruchterman' | Fruchterman 力导向布局 |\n| GridLayout | 'grid' | 网格布局 |\n| IndentedLayout | 'indented' | 缩进树 |\n| MDSLayout | 'mds' | 高维数据降维布局 |\n| MindmapLayout | 'mindmap' | 脑图树 |\n| RadialLayout | 'radial' | 径向布局 |\n| RandomLayout | 'random' | 随机布局 |\n\n配置方式:\n\n在 `GraphOptions.layout` 中配置,示例:\n\n```ts\nconst graph = new Graph({\n // ... 其他配置\n layout: {\n type: 'force',\n },\n});\n```\n\n## 色板\n\n\n\n配置方式:\n\n在 `GraphOptions.[node|edge|combo].palette` 中配置,示例:\n\n```ts\nconst graph = new Graph({\n // ... 其他配置\n node: {\n palette: 'tableau',\n },\n});\n```\n\n## 主题\n\n| 注册类型 | 描述 |\n| -------- | -------- |\n| dark | 深色主题 |\n| light | 浅色主题 |\n\n配置方式:\n\n在 `GraphOptions.theme` 中配置,示例:\n\n```ts\nconst graph = new Graph({\n // ... 其他配置\n theme: 'dark',\n});\n```\n\n## 插件\n\n| 扩展 | 注册类型 | 描述 |\n| -------------- | ------------------ | ---------- |\n| BubbleSets | 'bubble-sets' | 气泡集 |\n| EdgeFilterLens | 'edge-filter-lens' | 边过滤镜 |\n| GridLine | 'grid-line' | 网格线 |\n| Background | 'background' | 背景 |\n| Contextmenu | 'contextmenu' | 上下文菜单 |\n| Fisheye | 'fisheye' | 鱼眼放大镜 |\n| Fullscreen | 'fullscreen' | 全屏展示 |\n| History | 'history' | 历史记录 |\n| Hull | 'hull' | 轮廓包围 |\n| Legend | 'legend' | 图例 |\n| Minimap | 'minimap' | 小地图 |\n| Snapline | 'snapline' | 对齐线 |\n| Timebar | 'timebar' | 时间条 |\n| Toolbar | 'toolbar' | 工具栏 |\n| Tooltip | 'tooltip' | 提示框 |\n| Watermark | 'watermark' | 水印 |\n\n配置方式:\n\n在 `GraphOptions.plugins` 中配置,示例:\n\n```ts\nconst graph = new Graph({\n // ... 其他配置\n plugins: ['minimap', 'contextmenu'],\n});\n```\n\n## 数据转换\n\n| 扩展 | 注册类型 | 描述 |\n| -------------------- | ------------------------ | -------- |\n| ProcessParallelEdges | 'process-parallel-edges' | 平行边 |\n| PlaceRadialLabels | 'place-radial-labels' | 径向标签 |\n\n配置方式:\n\n在 `GraphOptions.transforms` 中配置,示例:\n\n```ts\nconst graph = new Graph({\n // ... 其他配置\n transform: ['process-parallel-edges', 'place-radial-labels'],\n});\n```\n\n## 图形\n\n| 注册类型 | 描述 |\n| -------- | ------ |\n| circle | 圆形 |\n| ellipse | 椭圆 |\n| group | 分组 |\n| html | HTML |\n| image | 图片 |\n| line | 直线 |\n| path | 路径 |\n| polygon | 多边形 |\n| polyline | 折线 |\n| rect | 矩形 |\n| text | 文本 |\n| label | 标签 |\n| badge | 徽标 |\n\n使用方式:\n\n自定义图形时,元素类成员方法 [upsert](/manual/element/node/custom-node) 方法第二个参数传入:\n\n```ts\nthis.upsert('shape-key', 'text', { text: 'label', fontSize: 16 }, this);\n```\n\n\n\n---\ntitle: Graph\norder: 0\n---\n\n## Overview\n\n### Definition of Graph\n\nIn Chinese, the character \"图\" (Graph) can often be used to represent many different concepts, such as image,shape,and chart or diagram,etc.\n\nIn Graph Theory, a graph is a mathematical structure used to model pairwise relationships between objects, which we typically represent with nodes (or Vertex) and edges (or Link) to denote the objects and the relationships between them.\n\nThe \"graph\" in G6:\n\n- Conceptually, it is the \"graph\" from Graph Theory, a data structure composed of nodes and edges.\n- Visually, a \"graph\" is a figure composed of a set of graphical elements representing nodes and edges.\n- In terms of code implementation, a \"graph\" is a class capable of transforming data into a graphical display.\n\n### Types of Graph\n\nGraph Theory categorizes graphs into many different types based on their structure and properties, such as:\n\n- Directed Graph and Undirected Graph\n- Weighted Graph and Unweighted Graph\n- Simple Graph and Multigraph\n- Cyclic Graph and Acyclic Graph\n- Connected Graph and Disconnected Graph\n- Complete Graph and Non-Complete Graph\n- Sparse Graph and Dense Graph\n- ...\n\nIn G6, we provide a universal graph representation capable of depicting the various types of graphs mentioned above, for example:\n\n- Directed Graph and Undirected Graph: Defined by the start and end points of the edges.\n- Weighted Graph and Unweighted Graph: Defined by the `weight` data on the edges.\n- Simple Graph and Multigraph: Defined by the uniqueness of the edges.\n- ...\n\n### Use Scenarios\n\nGraphs are a very versatile data structure that can be used to represent a variety of scenarios, such as:\n\n- Social Networks\n- Knowledge Graphs\n- Traffic Networks\n- Power Grids\n\nIn G6, we provide a rich expression capability for graphs that can meet the needs of different scenarios. We also offer a wealth of interactive and animated effects to make the graphs more vivid and intuitive.\n\n## Use G6 Graph\n\nTo create a Graph with G6, you first need to import the `@antv/g6` library, and then instantiate the Graph class.\n\n> For installation instructions, refer to: [Getting Started - Installation](/en/manual/getting-started/installation)\n\nThe Graph class accepts an instantiation argument object, known as **options** (Options, in visualization theory it is referred to as: `Specification`), which is used to configure the graph's data, element styles, layout, interactions, etc.\n\n```typescript\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 800,\n height: 600,\n // ... other other options\n});\n```\n\n:::warning{title=note}\nThe instantiation process only configures the basic information of the graph. To render the graph onto the page, you still need to call the `render` method.\n:::\n\n- To learn how to quickly create a graph, please refer to [Quick Start](/en/manual/getting-started/quick-start).\n- For more detailed information about the configuration options, please refer to [Options](/en/manual/graph/option).\n- To gain an in-depth understanding of the concepts within the configuration options, please read the rest of the content in this section.\n\n\n\n---\ntitle: Graph - 图\norder: 0\n---\n\n## 什么是 Graph\n\n中文字“图”在大家的传统认知里指的是图画、图像,而图论与可视化中的“图”—— Graph 则有着更精确的定位:主体(objects)与关系(relationships)的组成。它甚至不局限于视觉,主体与关系的数据也可以称为图。\n\n> —— 摘自 [AntV 专栏](https://zhuanlan.zhihu.com/aiux-antv) 文章:[Graph Visualization · 知多少 之 《HelloWorld 图可视化》](https://zhuanlan.zhihu.com/p/83685690)。\n\n在 G6 中,Graph 对象是图的载体,它包含了图上的所有元素(节点、边等),同时挂载了图的相关操作(如交互监听、元素操作、渲染等)。\n\nGraph 对象的完整生命周期包括:\n\n1. **创建**: 通过 `new Graph(options)` 实例化\n2. **初始化**: 在创建时进行内部初始化\n3. **渲染**: 调用 `graph.render()` 进行首次渲染\n4. **更新**: 通过各种 API 更新图的数据和配置\n5. **销毁**: 调用 `graph.destroy()` 销毁实例并释放资源\n\n## 使用 G6 Graph\n\n要使用 G6 创建 Graph,首先需要引入 `@antv/g6` 库,然后实例化 Graph 类。\n\n> 安装教程参考:[开始使用 - 安装](/manual/getting-started/installation)\n\nGraph 类接收一个实例化参数对象,称之为**配置项**(Options,在可视化理论中将其称为:`Specification`),用于配置图的数据、元素样式、布局、交互等。\n\n```typescript\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container', // 通过 DOM ID 指定容器\n width: 800, // 画布宽高(若容器已设尺寸可省略)\n height: 600,\n data: {\n // 初始数据\n nodes: [{ id: 'start', data: { label: 'Hello G6!' } }],\n },\n});\n```\n\n:::warning{title=注意}\n实例化过程仅是配置图的基本信息,要将图渲染到页面上,还需要调用 `render` 方法\n:::\n\n## 图配置项\n\n通过下表速查图的配置项,更多类型定义说明和详细用法请参考 [API - 图配置项](/manual/graph/option)。\n\n| 属性 | 类型 | 默认值 | 描述 |\n| ---------------- | ---------------------------------- | ----------- | ------------------------------------------------------------- |\n| container | string \\| HTMLElement \\| Canvas | - | 图容器,可以是 DOM 元素 ID、DOM 元素实例或 Canvas 实例 |\n| width | number | 容器宽度 | 画布宽度(像素) |\n| height | number | 容器高度 | 画布高度(像素) |\n| autoFit | 'view' \\| 'center' \\| object | - | 自动适配策略,'view'(适应视图)或'center'(居中) |\n| autoResize | boolean | false | 是否在窗口大小变化时自动调整画布大小 |\n| background | string | - | 画布背景色,也作为导出图片时的背景色 |\n| canvas | CanvasConfig | - | 画布配置 |\n| cursor | Cursor | `'default'` | 指针样式 |\n| devicePixelRatio | number | 2 | 设备像素比 |\n| padding | number \\| number[] | - | 画布内边距,在自适应时会根据内边距进行适配 |\n| renderer | (layer: string) => IRenderer | - | 手动指定渲染器 |\n| rotation | number | 0 | 旋转角度(弧度) |\n| zoom | number | 1 | 缩放比例 |\n| zoomRange | [number, number] | [0.01, 10] | 缩放比例的限制范围 |\n| x | number | - | 视口 x 坐标 |\n| y | number | - | 视口 y 坐标 |\n| data | GraphData | - | 图数据,详见 [数据](/manual/data) |\n| node | NodeOptions | - | 节点全局配置,详见 [节点](/manual/element/node/overview) |\n| edge | EdgeOptions | - | 边全局配置,详见 [边](/manual/element/edge/overview) |\n| combo | ComboOptions | - | 组合全局配置,详见 [组合](/manual/element/combo/overview) |\n| animation | boolean \\| AnimationEffectTiming | - | 全局动画配置,详见 [动画](/manual/animation/animation) |\n| theme | string \\| false | `'light'` | 主题配置,支持 `'light'`、`'dark'` 或自定义主题名 |\n| layout | LayoutOptions \\| LayoutOptions[] | - | 布局配置,详见 [布局](/manual/layout/overview) |\n| behaviors | (string \\| CustomBehaviorOption)[] | - | 交互行为配置,详见 [交互](/manual/behavior/overview) |\n| plugins | (string \\| CustomPluginOption)[] | - | 插件配置,详见 [插件](/manual/plugin/overview) |\n| transforms | TransformOptions | - | 数据转换器配置,详见 [数据转换器](/manual/transform/overview) |\n\n## 图属性\n\n图实例提供了一些只读属性,用于获取图的状态信息:\n\n| 属性 | 类型 | 描述 |\n| --------- | ------- | ------------------------ |\n| destroyed | boolean | 当前图实例是否已被销毁 |\n| rendered | boolean | 当前图实例是否已完成渲染 |\n\n## 参考示例\n\n### 完整的创建和配置示例\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 200,\n width: 300,\n height: 200,\n padding: 30,\n autoResize: true,\n\n // 视口配置\n zoom: 0.8,\n autoFit: 'view',\n padding: 20,\n\n // 主题配置\n theme: 'dark',\n\n // 节点配置\n node: {\n style: {\n fill: '#7FFFD4',\n stroke: '#5CACEE',\n lineWidth: 2,\n },\n },\n\n // 边配置\n edge: {\n style: {\n stroke: '#A4D3EE',\n lineWidth: 1.5,\n endArrow: true,\n },\n },\n\n // 布局配置\n layout: {\n type: 'force',\n preventOverlap: true,\n linkDistance: 100,\n },\n\n // 交互行为\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n\n // 初始数据\n data: {\n nodes: [\n { id: 'node1', data: { label: '节点1' } },\n { id: 'node2', data: { label: '节点2' } },\n ],\n edges: [{ source: 'node1', target: 'node2', data: { label: '关系' } }],\n },\n});\n\ngraph.render();\n```\n\n```typescript\nimport { Graph } from '@antv/g6';\n\n// 创建图实例\nconst graph = new Graph({\n // 基础配置\n container: 'container',\n width: 300,\n height: 200,\n padding: 30,\n autoResize: true,\n\n // 视口配置\n zoom: 0.8,\n autoFit: 'view',\n padding: 20,\n\n // 主题配置\n theme: 'dark',\n\n // 节点配置\n node: {\n style: {\n fill: '#7FFFD4',\n stroke: '#5CACEE',\n lineWidth: 2,\n },\n },\n\n // 边配置\n edge: {\n style: {\n stroke: '#A4D3EE',\n lineWidth: 1.5,\n endArrow: true,\n },\n },\n\n // 布局配置\n layout: {\n type: 'force',\n preventOverlap: true,\n linkDistance: 100,\n },\n\n // 交互行为\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n\n // 初始数据\n data: {\n nodes: [\n { id: 'node1', data: { label: '节点1' } },\n { id: 'node2', data: { label: '节点2' } },\n ],\n edges: [{ source: 'node1', target: 'node2', data: { label: '关系' } }],\n },\n});\n\n// 渲染图\ngraph.render();\n```\n\n- 要了解如何快速创建一个图,请参考[快速上手](/manual/getting-started/quick-start)。\n- 要深入了解配置项中个部分的概念,请阅读本章节的其他内容。\n\n\n\n---\ntitle: Options\norder: 0\n---\n\n## autoFit\n\n> _{ type: 'view'; options?: [FitViewOptions](#fitviewoptions); animation?: [ViewportAnimationEffectTiming](#viewportanimationeffecttiming); } \\| { type: 'center'; animation?: [ViewportAnimationEffectTiming](#viewportanimationeffecttiming); } \\| 'view' \\| 'center'_\n\nWhether to automatically fit the canvas. ⚠️ **Note**: Each time `render` is executed, it will adapt according to `autoFit`.\n\nTwo basic adaptation modes:\n\n- `'view'` - Automatically scale to ensure all content is visible within the view\n- `'center'` - Center the content without changing the zoom level\n\nMore precise adaptation control can be achieved through object form:\n\n```javascript\nconst graph = new Graph({\n autoFit: {\n type: 'view', // Adaptation type: 'view' or 'center'\n options: {\n // Only applicable to 'view' type\n when: 'overflow', // When to adapt: 'overflow' (only when content overflows) or 'always' (always adapt)\n direction: 'x', // Adaptation direction: 'x', 'y', or 'both'\n },\n animation: {\n // Adaptation animation effect\n duration: 1000, // Animation duration (milliseconds)\n easing: 'ease-in-out', // Animation easing function\n },\n },\n});\n```\n\n#### FitViewOptions\n\n| Property | Description | Type | Default | Required |\n| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | ---------- | -------- |\n| when | Adaptation occurs under the following conditions
- `'overflow'` adapt only when content overflows
- `'always'` always adapt | `'overflow'` \\| `'always'` | `'always'` | |\n| direction | Adapt only in the specified direction
- `'x'` adapt only in x direction
- `'y'` adapt only in y direction
- `'both'` adapt in both x and y directions | `'x'` \\| `'y'` \\| `'both'` | `'both'` | |\n\n#### ViewportAnimationEffectTiming\n\n```typescript\ntype ViewportAnimationEffectTiming =\n | boolean // true to enable default animation, false to disable animation\n | {\n easing?: string; // Animation easing function: 'ease-in-out', 'ease-in', 'ease-out', 'linear'\n duration?: number; // Animation duration (milliseconds)\n };\n```\n\n## autoResize\n\n> _boolean_ **Default:** `false`\n\nWhether to automatically resize the canvas.\n\nImplemented based on the `window.onresize` event. When the browser window size changes, the canvas will automatically resize to fit the container.\n\n## background\n\n> _string_\n\nCanvas background color.\n\nThis color is used as the background color when exporting images. Any valid CSS color value can be used, such as hexadecimal, RGB, RGBA, etc.\n\n## canvas\n\n> [CanvasConfig](#canvasconfig)\n\nCanvas configuration. Related configuration items under GraphOptions (such as `container`, `width`, `height`, `devicePixelRatio`, `background`, `cursor`) are shortcut configuration items and will be converted to canvas configuration items.\n\n#### CanvasConfig\n\n| Property | Description | Type | Default | Required |\n| ---------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------- | -------- |\n| container | Canvas container | string \\| HTMLElement | - | |\n| devicePixelRatio | Device pixel ratio | number | - | |\n| width | Canvas width | number | - | |\n| height | Canvas height | number | - | |\n| cursor | Cursor style, same as [GraphOptions.cursor](#cursor) | string | - | |\n| background | Canvas background color | string | - | |\n| renderer | Renderer, same as [GraphOptions.renderer](#renderer) | (layer: `'background'` \\| `'main'` \\| `'label'` \\| `'transient'`) => IRenderer | - | |\n| enableMultiLayer | Whether to enable multi-layer. Non-dynamic parameter, effective only during initialization | boolean | - | |\n\n## container\n\n> _string \\|_ _HTMLElement_ _\\|_ Canvas\n\nCanvas container, can be one of the following three assignments:\n\n- ID string of the DOM element, such as `'container'`\n- HTML element object, such as `document.getElementById('container')`\n- Canvas instance, such as `new Canvas(options)`, where `options` is of type [CanvasConfig](#canvasconfig).\n\n## cursor\n\n> string\n\nCursor style, controls the cursor shape when hovering over the canvas. Any valid CSS cursor value can be used.\n\nSupported values include: `'auto'`, `'default'`, `'none'`, `'context-menu'`, `'help'`, `'pointer'`, `'progress'`, `'wait'`, `'cell'`, `'crosshair'`, `'text'`, `'vertical-text'`, `'alias'`, `'copy'`, `'move'`, `'no-drop'`, `'not-allowed'`, `'grab'`, `'grabbing'`, `'all-scroll'`, `'col-resize'`, `'row-resize'`, `'n-resize'`, `'e-resize'`, `'s-resize'`, `'w-resize'`, `'ne-resize'`, `'nw-resize'`, `'se-resize'`, `'sw-resize'`, `'ew-resize'`, `'ns-resize'`, `'nesw-resize'`, `'nwse-resize'`, `'zoom-in'`, `'zoom-out'.\n\nCursor values are referenced from [MDN - cursor](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor).\n\n## devicePixelRatio\n\n> _number_\n\nDevice pixel ratio.\n\nUsed for high-definition screens, the default is [window.devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio).\n\n## width\n\n> _number_\n\nCanvas width. If not set, the container width will be automatically obtained.\n\n## height\n\n> _number_\n\nCanvas height. If not set, the container height will be automatically obtained.\n\n## renderer\n\n> _(layer: 'background' \\| 'main' \\| 'label' \\| 'transient') =>_ _IRenderer_\n\nManually specify the renderer\n\nG6 uses a layered rendering approach, divided into four layers: `background`, `main`, `label`, `transient`. Users can set the renderer for each layer of the canvas through this configuration item.\n\n**Example**: Use SVG renderer for rendering\n\n```javascript\nimport { Renderer as SVGRenderer } from '@antv/g-svg';\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n renderer: () => new SVGRenderer(),\n});\n```\n\n## padding\n\n> _number \\| number[]_\n\nCanvas padding\n\nUsually, during adaptation, it will be adapted according to the padding. It can be a single value (same padding on all sides) or an array form (specify the padding for top, right, bottom, left in order).\n\n**Example:**\n\n```javascript\n// Single value\nconst graph1 = new Graph({\n padding: 20, // 20 pixels of padding on all sides\n});\n\n// Array form\nconst graph2 = new Graph({\n padding: [20, 40, 20, 40], // Padding for top, right, bottom, left\n});\n```\n\n## rotation\n\n> _number_ **Default:** `0`\n\nRotation angle (in radians)\n\n## x\n\n> _number_\n\nViewport x coordinate, sets the initial horizontal position of the viewport.\n\n## y\n\n> _number_\n\nViewport y coordinate, sets the initial vertical position of the viewport.\n\n## zoom\n\n> _number_ **Default:** `1`\n\nSets the initial zoom level of the viewport, 1 means 100% (original size).\n\n## zoomRange\n\n> _[number, number]_ **Default:** `[0.01, 10]`\n\nZoom range, limits the minimum and maximum scale that users can zoom.\n\n## animation\n\n> _boolean \\| [AnimationEffectTiming](#animationeffecttiming)_\n\nEnable or disable global animation\n\nWhen configured as an animation option, animation will be enabled, and this animation configuration will be used as the base configuration for global animation.\n\n#### AnimationEffectTiming\n\n| Property | Description | Type | Default | Required |\n| ---------- | ------------------------------ | ------------------------------------------------------------------- | ----------- | -------- |\n| delay | Animation delay time | number | - | |\n| direction | Animation direction | `'alternate'` \\| `'alternate-reverse'` \\| `'normal'` \\| `'reverse'` | `'forward'` | |\n| duration | Animation duration | number | - | |\n| easing | Animation easing function | string | - | |\n| fill | Fill mode after animation ends | `'auto'` \\| `'backwards'` \\| `'both'` \\| `'forwards'` \\| `'none'` | `'none'` | |\n| iterations | Animation iteration count | number | - | |\n\n**Example:**\n\n```javascript\n// Simple enable\nconst graph1 = new Graph({\n animation: true,\n});\n\n// Detailed configuration\nconst graph2 = new Graph({\n animation: {\n duration: 500, // Animation duration (milliseconds)\n easing: 'ease-in-out', // Easing function\n },\n});\n```\n\n## data\n\n> [GraphData](#graphdata)\n\nData.\n\n#### GraphData\n\n| Property | Description | Type | Default | Required |\n| -------- | ----------- | ------------------------- | ------- | -------- |\n| nodes | Node data | [NodeData](#nodedata)[] | - | ✓ |\n| edges | Edge data | [EdgeData](#edgedata)[] | - | ✓ |\n| combos | Combo data | [ComboData](#combodata)[] | - | ✓ |\n\n#### NodeData\n\n| Property | Description | Type | Default | Required |\n| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------- | -------- |\n| id | Unique identifier for the node, used to distinguish different nodes | string | - | ✓ |\n| type | Node type, built-in node type name or custom node name | string | - | |\n| data | Node data, used to store custom data for the node, such as node name, description, etc. Can be accessed in style mapping through callback functions | object | - | |\n| style | Node style, including visual attributes such as position, size, color, etc. | object | - | |\n| states | Initial state of the node, such as selected, activated, hovered, etc. | string[] | - | |\n| combo | ID of the combo to which it belongs, used to organize the hierarchical relationship of nodes, if none, it is null | string \\| null | - | |\n| children | Collection of child node IDs, used only in tree graph scenarios | string[] | - | |\n\n#### EdgeData\n\n| Property | Description | Type | Default | Required |\n| -------- | -------------------------------------------------------------------------------------------------------------- | -------- | ------- | -------- |\n| source | Starting node ID of the edge | string | - | ✓ |\n| target | Target node ID of the edge | string | - | ✓ |\n| id | Unique identifier for the edge | string | - | |\n| type | Edge type, built-in edge type name or custom edge name | string | - | |\n| data | Edge data, used to store custom data for the edge, can be accessed in style mapping through callback functions | object | - | |\n| style | Edge style, including visual attributes such as line color, width, arrow, etc. | object | - | |\n| states | Initial state of the edge | string[] | - | |\n\n#### ComboData\n\n| Property | Description | Type | Default | Required |\n| -------- | ---------------------------------------------------------------------------------------------------------------- | -------------- | ------- | -------- |\n| id | Unique identifier for the combo | string | - | ✓ |\n| type | Combo type, built-in combo type name or custom combo name | string | - | |\n| data | Combo data, used to store custom data for the combo, can be accessed in style mapping through callback functions | object | - | |\n| style | Combo style | object | - | |\n| states | Initial state of the combo | string[] | - | |\n| combo | Parent combo ID. If there is no parent combo, it is null | string \\| null | - | |\n\n**Example:**\n\n```javascript\nconst graph = new Graph({\n data: {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 } },\n { id: 'node2', style: { x: 200, y: 200 } },\n ],\n edges: [{ id: 'edge1', source: 'node1', target: 'node2' }],\n combos: [{ id: 'combo1', style: { x: 150, y: 150 } }],\n },\n});\n```\n\n- Read [Data](/en/manual/data) to learn more about graph data, including but not limited to data formats, how to manipulate data, etc.\n\n## node\n\n> [NodeOptions](#nodeoptions)\n\nNode configuration options.\n\n#### NodeOptions\n\n| Property | Description | Type | Default | Required |\n| --------- | -------------------------------------------------------------------------------- | -------------------------------------------------------- | -------- | -------- |\n| type | Node type, built-in node type name or custom node name | [Type](/en/manual/element/node/base-node#type) | `circle` | |\n| style | Node style, including color, size, etc. | [Style](/en/manual/element/node/base-node#style) | - | |\n| state | Define the style of the node in different states | [State](/en/manual/element/node/base-node#state) | - | |\n| palette | Define the color palette of the node, used to map colors based on different data | [Palette](/en/manual/element/node/base-node#palette) | - | |\n| animation | Define the animation effect of the node | [Animation](/en/manual/element/node/base-node#animation) | - | |\n\nSee [Node](/en/manual/element/node/base-node) for details\n\n**Example:**\n\n```javascript\nconst graph = new Graph({\n node: {\n type: 'circle', // Node type\n style: {\n fill: '#e6f7ff', // Fill color\n stroke: '#91d5ff', // Border color\n lineWidth: 1, // Border width\n r: 20, // Radius\n labelText: (d) => d.id, // Label text\n },\n // Node state style\n state: {\n hover: {\n lineWidth: 2,\n stroke: '#69c0ff',\n },\n selected: {\n fill: '#bae7ff',\n stroke: '#1890ff',\n lineWidth: 2,\n },\n },\n },\n});\n```\n\n## edge\n\n> [EdgeOptions](#edgeoptions)\n\nEdge configuration options\n\n#### EdgeOptions\n\n| Property | Description | Type | Default | Required |\n| --------- | -------------------------------------------------------------------------------- | -------------------------------------------------------- | ------- | -------- |\n| type | Edge type, built-in edge type name or custom edge name | [Type](/en/manual/element/edge/base-edge#type) | `line` | |\n| style | Edge style, including color, size, etc. | [Style](/en/manual/element/edge/base-edge#style) | - | |\n| state | Define the style of the edge in different states | [State](/en/manual/element/edge/base-edge#state) | - | |\n| palette | Define the color palette of the edge, used to map colors based on different data | [Palette](/en/manual/element/edge/base-edge#palette) | - | |\n| animation | Define the animation effect of the edge | [Animation](/en/manual/element/edge/base-edge#animation) | - | |\n\nSee [Edge](/en/manual/element/edge/base-edge) for details\n\n**Example:**\n\n```javascript\nconst graph = new Graph({\n edge: {\n type: 'polyline', // Edge type\n style: {\n stroke: '#91d5ff', // Edge color\n lineWidth: 2, // Edge width\n endArrow: true, // Whether there is an arrow\n },\n // Edge state style\n state: {\n selected: {\n stroke: '#1890ff',\n lineWidth: 3,\n },\n },\n },\n});\n```\n\n## combo\n\n> [ComboOptions](#combooptions)\n\nCombo configuration options\n\n| Property | Description | Type | Default | Required |\n| --------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------- | -------- | -------- |\n| type | Combo type, built-in combo type name or custom combo name | [Type](/en/manual/element/combo/base-combo#type) | `circle` | |\n| style | Combo style, including color, size, etc. | [Style](/en/manual/element/combo/base-combo#style) | - | |\n| state | Define the style of the combo in different states | [State](/en/manual/element/combo/base-combo#state) | - | |\n| palette | Define the color palette of the combo, used to map colors based on different data | [Palette](/en/manual/element/combo/base-combo#palette) | - | |\n| animation | Define the animation effect of the combo | [Animation](/en/manual/element/combo/base-combo#animation) | - | |\n\nSee [Combo](/en/manual/element/combo/base-combo) for details\n\n**Example:**\n\n```javascript\nconst graph = new Graph({\n combo: {\n type: 'circle', // Combo type\n style: {\n fill: '#f0f0f0', // Background color\n stroke: '#d9d9d9', // Border color\n lineWidth: 1, // Border width\n },\n // Combo state style\n state: {\n selected: {\n stroke: '#1890ff',\n lineWidth: 2,\n },\n },\n },\n});\n```\n\n## layout\n\n> _CustomLayoutOptions \\| CustomLayoutOptions[]_\n\nLayout configuration options, can be an object (normal layout) or an array (pipeline layout).\n\n**Example**:\n\n```javascript\nconst graph = new Graph({\n container: 'container',\n layout: {\n type: 'force', // Force-directed layout\n preventOverlap: true, // Prevent node overlap\n nodeStrength: -50, // Repulsion between nodes\n edgeStrength: 0.5, // Elastic coefficient of edges\n iterations: 200, // Number of iterations\n animation: true, // Enable layout animation\n },\n});\n```\n\n## theme\n\n> _false \\| 'light' \\| 'dark' \\| string_\n\nSet the theme of the chart, can be the built-in `'light'`, `'dark'` theme, or the name of a custom theme. Set to `false` to use no theme.\n\n## behaviors\n\n> _(string \\| [CustomExtensionOptions](#customextensionoptions) \\| ((this:Graph) =>CustomExtensionOptions))[]_\n\nConfigure the interaction behaviors of the chart, can be a string (using default configuration), an object (custom configuration), or a function (dynamic configuration, the graph instance can be accessed within the function).\n\n**Example:**\n\n```javascript\nconst graph = new Graph({\n behaviors: [\n 'drag-canvas', // Enable canvas dragging with default configuration\n 'zoom-canvas', // Enable canvas zooming with default configuration\n {\n type: 'drag-element', // Custom configuration for dragging elements\n key: 'drag-node-only',\n enable: (event) => event.targetType === 'node', // Only allow dragging nodes\n },\n function () {\n console.log(this); // Output graph instance\n return {\n type: 'hover-activate',\n };\n },\n ],\n});\n```\n\n- View [Interaction Overview](/en/manual/behavior/overview) to learn more about interaction principles\n- Browse [Built-in Interactions](/en/manual/behavior/auto-adapt-label) to get a list of all built-in interactions and their configuration options\n\n## plugins\n\n> _(string \\| [CustomExtensionOptions](#customextensionoptions) \\| ((this:Graph) =>CustomExtensionOptions))[]_\n\nSet the plugins of the chart, can be a string (using default configuration), an object (custom configuration), or a function (dynamic configuration, the graph instance can be accessed within the function).\n\n**Example:**\n\n```javascript\nconst graph = new Graph({\n container: 'container',\n plugins: [\n 'minimap', // Enable minimap with default configuration\n {\n type: 'grid', // Enable grid background\n key: 'grid-plugin',\n line: {\n stroke: '#d9d9d9',\n lineWidth: 1,\n },\n },\n {\n type: 'toolbar', // Enable toolbar\n key: 'graph-toolbar',\n position: 'top-right', // Position\n },\n ],\n});\n```\n\n- View [Plugin Overview](/en/manual/plugin/overview) to learn more about plugin principles\n- Browse [Built-in Plugins](/en/manual/plugin/background) to get a list of all built-in plugins and their configuration options\n\n## transforms\n\n> _(string \\| [CustomExtensionOptions](#customextensionoptions) \\| ((this:Graph) =>CustomExtensionOptions))[]_\n\nConfigure data processing, used to process data before rendering, does not affect the original data. Can be a string (using default configuration), an object (custom configuration), or a function (dynamic configuration, the graph instance can be accessed within the function).\n\n**Example:**\n\n```javascript\nconst graph = new Graph({\n transforms: [\n 'process-parallel-edges', // Process parallel edges with default configuration\n {\n type: 'map-node-size', // Map node size based on node data\n field: 'value', // Use the value of the field\n max: 50, // Maximum radius\n min: 20, // Minimum radius\n },\n ],\n});\n```\n\n- View [Data Processing Overview](/en/manual/transform/overview) to learn more about data processing principles\n- Browse [Built-in Data Processing](/en/manual/transform/map-node-size) to get a list of all built-in data processing and their configuration options\n\n#### CustomExtensionOptions\n\n```typescript\ninterface CustomExtensionOption extends Record {\n /** Extension type */\n type: string;\n /** Extension key, i.e., unique identifier */\n key?: string;\n}\n```\n\n\n\n---\ntitle: Options 配置项\norder: 0\n---\n\n## autoFit\n\n> _{ type: 'view'; options?: [FitViewOptions](#fitviewoptions); animation?: [ViewportAnimationEffectTiming](#viewportanimationeffecttiming); } \\| { type: 'center'; animation?: [ViewportAnimationEffectTiming](#viewportanimationeffecttiming); } \\| 'view' \\| 'center'_\n\n是否自动适应画布。⚠️ **注意**:每次执行 `render` 时,都会根据 `autoFit` 进行自适应。\n\n两种基本自适应模式:\n\n- `'view'` - 自动缩放,确保所有内容都在视图内可见\n- `'center'` - 内容居中显示,但不改变缩放比例\n\n还可通过对象形式实现更精细的自适应控制:\n\n```javascript\nconst graph = new Graph({\n autoFit: {\n type: 'view', // 自适应类型:'view' 或 'center'\n options: {\n // 仅适用于 'view' 类型\n when: 'overflow', // 何时适配:'overflow'(仅当内容溢出时) 或 'always'(总是适配)\n direction: 'x', // 适配方向:'x'、'y' 或 'both'\n },\n animation: {\n // 自适应动画效果\n duration: 1000, // 动画持续时间(毫秒)\n easing: 'ease-in-out', // 动画缓动函数\n },\n },\n});\n```\n\n#### FitViewOptions\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------- | -------------------------------------------------------------------------------------------------------------- | -------------------------- | ---------- | ---- |\n| when | 在以下情况下进行适配
- `'overflow'` 仅当图内容超出视口时进行适配
- `'always'` 总是进行适配 | `'overflow`' \\| `'always'` | `'always'` | |\n| direction | 仅对指定方向进行适配
- `'x'` 仅适配 x 方向
- `'y'` 仅适配 y 方向
- `'both'` 适配 x 和 y 方向 | `'x`' \\| `'y`' \\| `'both'` | `'both'` | |\n\n#### ViewportAnimationEffectTiming\n\n```typescript\ntype ViewportAnimationEffectTiming =\n | boolean // true 启用默认动画,false 禁用动画\n | {\n easing?: string; // 动画缓动函数:'ease-in-out'、'ease-in'、'ease-out'、'linear'\n duration?: number; // 动画持续时间(毫秒)\n };\n```\n\n## autoResize\n\n> _boolean_ **默认值:** `false`\n\n是否自动调整画布大小。\n\n基于 `window.onresize` 事件实现。当浏览器窗口大小变化时,画布将自动调整大小以适应容器。\n\n## background\n\n> _string_\n\n画布背景色。\n\n该颜色作为导出图片时的背景色。可以使用任何有效的 CSS 颜色值,如十六进制、RGB、RGBA 等。\n\n## canvas\n\n> [CanvasConfig](#canvasconfig)\n\n画布配置。GraphOptions 下相关配置项(如 `container`、`width`、`height`、`devicePixelRatio`、`background`、`cursor`)为快捷配置项,会被转换为 canvas 配置项。\n\n#### CanvasConfig\n\n| 属性 | 描述 | 类型 | 默认值 | 必填 |\n| ---------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------ | ------ | ---- |\n| container | 画布容器 | string \\| HTMLElement | - | |\n| devicePixelRatio | 设备像素比 | number | - | |\n| width | 画布宽度 | number | - | |\n| height | 画布高度 | number | - | |\n| cursor | 指针样式,与 [GraphOptions.cursor](#cursor) 配置相同 | string | - | |\n| background | 画布背景色 | string | - | |\n| renderer | 渲染器,与 [GraphOptions.renderer](#renderer) 配置相同 | (layer: `'background'` \\| `'main'` \\| `'label'` \\| `'transient'`) => IRenderer | - | |\n| enableMultiLayer | 是否启用多图层。非动态参数,仅在初始化时生效 | boolean | - | |\n\n## container\n\n> _string \\|_ _HTMLElement_ _\\|_ Canvas\n\n画布容器,可以是以下三种赋值之一:\n\n- DOM 元素的 ID 字符串,如 `'container'`\n- HTML 元素对象,如 `document.getElementById('container')`\n- Canvas 实例,如 `new Canvas(options)`,其中 `options` 为 [CanvasConfig](#canvasconfig) 类型。\n\n## cursor\n\n> string\n\n指针样式,控制鼠标悬停在画布上时的光标形状。可以使用任何有效的 CSS cursor 值。\n\n支持的值有: `'auto'`、`'default'`、`'none'`、`'context-menu'`、`'help'`、`'pointer'`、`'progress'`、`'wait'`、`'cell'`、`'crosshair'`、`'text'`、`'vertical-text'`、`'alias'`、`'copy'`、`'move'`、`'no-drop'`、`'not-allowed'`、`'grab'`、`'grabbing'`、`'all-scroll'`、`'col-resize'`、`'row-resize'`、`'n-resize'`、`'e-resize'`、`'s-resize'`、`'w-resize'`、`'ne-resize'`、`'nw-resize'`、`'se-resize'`、`'sw-resize'`、`'ew-resize'`、`'ns-resize'`、`'nesw-resize'`、`'nwse-resize'`、`'zoom-in'`、`'zoom-out'`。\n\n这里的 Cursor 值参考 [MDN - cursor](https://developer.mozilla.org/zh-CN/docs/Web/CSS/cursor)。\n\n## devicePixelRatio\n\n> _number_\n\n设备像素比。\n\n用于高清屏的设备像素比,默认为 [window.devicePixelRatio](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/devicePixelRatio)。\n\n## width\n\n> _number_\n\n画布宽度。如果未设置,则会自动获取容器宽度。\n\n## height\n\n> _number_\n\n画布高度。如果未设置,则会自动获取容器高度。\n\n## renderer\n\n> _(layer: 'background' \\| 'main' \\| 'label' \\| 'transient') =>_ _IRenderer_\n\n手动指定渲染器\n\nG6 采用了分层渲染的方式,分为 `background`、`main`、`label`、`transient` 四层,用户可以通过该配置项分别设置每层画布的渲染器。\n\n**示例**: 使用 SVG 渲染器进行渲染\n\n```javascript\nimport { Renderer as SVGRenderer } from '@antv/g-svg';\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n renderer: () => new SVGRenderer(),\n});\n```\n\n## padding\n\n> _number \\| number[]_\n\n画布内边距\n\n通常在自适应时,会根据内边距进行适配。可以是单个数值(四边相同)或者数组形式(按顺序指定上、右、下、左的内边距)。\n\n**示例:**\n\n```javascript\n// 单个数值\nconst graph1 = new Graph({\n padding: 20, // 四边均为 20 像素的内边距\n});\n\n// 数组形式\nconst graph2 = new Graph({\n padding: [20, 40, 20, 40], // 上、右、下、左的内边距\n});\n```\n\n## rotation\n\n> _number_ **默认值:** `0`\n\n旋转角度(以弧度为单位)\n\n## x\n\n> _number_\n\n视口 x 坐标,设置视口的初始水平位置。\n\n## y\n\n> _number_\n\n视口 y 坐标,设置视口的初始垂直位置。\n\n## zoom\n\n> _number_ **默认值:** `1`\n\n设置视口的初始缩放级别,1 表示 100%(原始大小)。\n\n## zoomRange\n\n> _[number, number]_ **默认值:** `[0.01, 10]`\n\n缩放范围,限制用户可以缩放的最小和最大比例。\n\n## animation\n\n> _boolean \\| [AnimationEffectTiming](#animationeffecttiming)_\n\n启用或关闭全局动画\n\n为动画配置项时,会启用动画,并将该动画配置作为全局动画的基础配置。\n\n#### AnimationEffectTiming\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ---------- | -------------------- | ------------------------------------------------------------------- | ----------- | ---- |\n| delay | 动画延迟时间 | number | - | |\n| direction | 动画方向 | `'alternate'` \\| `'alternate-reverse'` \\| `'normal'` \\| `'reverse'` | `'forward'` | |\n| duration | 动画持续时间 | number | - | |\n| easing | 动画缓动函数 | string | - | |\n| fill | 动画结束后的填充模式 | `'auto'` \\| `'backwards'` \\| `'both'` \\| `'forwards'` \\| `'none'` | `'none'` | |\n| iterations | 动画迭代次数 | number | - | |\n\n**示例:**\n\n```javascript\n// 简单启用\nconst graph1 = new Graph({\n animation: true,\n});\n\n// 详细配置\nconst graph2 = new Graph({\n animation: {\n duration: 500, // 动画持续时间(毫秒)\n easing: 'ease-in-out', // 缓动函数\n },\n});\n```\n\n## data\n\n> [GraphData](#graphdata)\n\n数据。\n\n#### GraphData\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------ | -------- | ------------------------- | ------ | ---- |\n| nodes | 节点数据 | [NodeData](#nodedata)[] | - | ✓ |\n| edges | 边数据 | [EdgeData](#edgedata)[] | - | ✓ |\n| combos | 组合数据 | [ComboData](#combodata)[] | - | ✓ |\n\n#### NodeData\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------- | -------------------------------------------------------------------------------------------- | -------------- | ------ | ---- |\n| id | 节点的唯一标识符,用于区分不同的节点 | string | - | ✓ |\n| type | 节点类型,内置节点类型名称或者自定义节点的名称 | string | - | |\n| data | 节点数据,用于存储节点的自定义数据,例如节点的名称、描述等。可以在样式映射中通过回调函数获取 | object | - | |\n| style | 节点样式,包括位置、大小、颜色等视觉属性 | object | - | |\n| states | 节点初始状态,如选中、激活、悬停等 | string[] | - | |\n| combo | 所属的组合 ID,用于组织节点的层级关系,如果没有则为 null | string \\| null | - | |\n| children | 子节点 ID 集合,仅在树图场景下使用 | string[] | - | |\n\n#### EdgeData\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------ | ---------------------------------------------------------------- | -------- | ------ | ---- |\n| source | 边起始节点 ID | string | - | ✓ |\n| target | 边目标节点 ID | string | - | ✓ |\n| id | 边的唯一标识符 | string | - | |\n| type | 边类型,内置边类型名称或者自定义边的名称 | string | - | |\n| data | 边数据,用于存储边的自定义数据,可以在样式映射中通过回调函数获取 | object | - | |\n| style | 边样式,包括线条颜色、宽度、箭头等视觉属性 | object | - | |\n| states | 边初始状态 | string[] | - | |\n\n#### ComboData\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------ | -------------------------------------------------------------------- | -------------- | ------ | ---- |\n| id | 组合的唯一标识符 | string | - | ✓ |\n| type | 组合类型,内置组合类型名称或者自定义组合名称 | string | - | |\n| data | 组合数据,用于存储组合的自定义数据,可以在样式映射中通过回调函数获取 | object | - | |\n| style | 组合样式 | object | - | |\n| states | 组合初始状态 | string[] | - | |\n| combo | 组合的父组合 ID。如果没有父组合,则为 null | string \\| null | - | |\n\n**示例:**\n\n```javascript\nconst graph = new Graph({\n data: {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 } },\n { id: 'node2', style: { x: 200, y: 200 } },\n ],\n edges: [{ id: 'edge1', source: 'node1', target: 'node2' }],\n combos: [{ id: 'combo1', style: { x: 150, y: 150 } }],\n },\n});\n```\n\n- 阅读 [数据](/manual/data) 深入了解图数据,包括不限于数据格式、如何操作数据等。\n\n## node\n\n> [NodeOptions](#nodeoptions)\n\n节点配置项。\n\n#### NodeOptions\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------- | -------------------------------------------- | ----------------------------------------------------- | -------- | ---- |\n| type | 节点类型,内置节点类型名称或自定义节点的名称 | [Type](/manual/element/node/base-node#type) | `circle` | |\n| style | 节点样式,包括颜色、大小等 | [Style](/manual/element/node/base-node#style) | - | |\n| state | 定义节点在不同状态下的样式 | [State](/manual/element/node/base-node#state) | - | |\n| palette | 定义节点的色板,用于根据不同数据映射颜色 | [Palette](/manual/element/node/base-node#palette) | - | |\n| animation | 定义节点的动画效果 | [Animation](/manual/element/node/base-node#animation) | - | |\n\n详见 [Node](/manual/element/node/base-node)\n\n**示例:**\n\n```javascript\nconst graph = new Graph({\n node: {\n type: 'circle', // 节点类型\n style: {\n fill: '#e6f7ff', // 填充色\n stroke: '#91d5ff', // 边框色\n lineWidth: 1, // 边框宽度\n r: 20, // 半径\n labelText: (d) => d.id, // 标签文本\n },\n // 节点状态样式\n state: {\n hover: {\n lineWidth: 2,\n stroke: '#69c0ff',\n },\n selected: {\n fill: '#bae7ff',\n stroke: '#1890ff',\n lineWidth: 2,\n },\n },\n },\n});\n```\n\n## edge\n\n> [EdgeOptions](#edgeoptions)\n\n边配置项\n\n#### EdgeOptions\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------- | -------------------------------------- | ----------------------------------------------------- | ------ | ---- |\n| type | 边类型,内置边类型名称或自定义边的名称 | [Type](/manual/element/edge/base-edge#type) | `line` | |\n| style | 边样式,包括颜色、大小等 | [Style](/manual/element/edge/base-edge#style) | - | |\n| state | 定义边在不同状态下的样式 | [State](/manual/element/edge/base-edge#state) | - | |\n| palette | 定义边的色板,用于根据不同数据映射颜色 | [Palette](/manual/element/edge/base-edge#palette) | - | |\n| animation | 定义边的动画效果 | [Animation](/manual/element/edge/base-edge#animation) | - | |\n\n详见 [Edge](/manual/element/edge/base-edge)\n\n**示例:**\n\n```javascript\nconst graph = new Graph({\n edge: {\n type: 'polyline', // 边类型\n style: {\n stroke: '#91d5ff', // 边的颜色\n lineWidth: 2, // 边的宽度\n endArrow: true, // 是否有箭头\n },\n // 边的状态样式\n state: {\n selected: {\n stroke: '#1890ff',\n lineWidth: 3,\n },\n },\n },\n});\n```\n\n## combo\n\n> [ComboOptions](#combooptions)\n\n组合配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------- | -------------------------------------------- | ------------------------------------------------------- | -------- | ---- |\n| type | 组合类型,内置组合类型名称或自定义组合的名称 | [Type](/manual/element/combo/base-combo#type) | `circle` | |\n| style | 组合样式,包括颜色、大小等 | [Style](/manual/element/combo/base-combo#style) | - | |\n| state | 定义组合在不同状态下的样式 | [State](/manual/element/combo/base-combo#state) | - | |\n| palette | 定义组合的色板,用于根据不同数据映射颜色 | [Palette](/manual/element/combo/base-combo#palette) | - | |\n| animation | 定义组合的动画效果 | [Animation](/manual/element/combo/base-combo#animation) | - | |\n\n详见 [Combo](/manual/element/combo/base-combo)\n\n**示例:**\n\n```javascript\nconst graph = new Graph({\n combo: {\n type: 'circle', // 组合类型\n style: {\n fill: '#f0f0f0', // 背景色\n stroke: '#d9d9d9', // 边框色\n lineWidth: 1, // 边框宽度\n },\n // 组合状态样式\n state: {\n selected: {\n stroke: '#1890ff',\n lineWidth: 2,\n },\n },\n },\n});\n```\n\n## layout\n\n> _CustomLayoutOptions \\| CustomLayoutOptions[]_\n\n布局配置项,可以是对象(普通布局)或数组(流水线布局)。\n\n**示例**:\n\n```javascript\nconst graph = new Graph({\n container: 'container',\n layout: {\n type: 'force', // 力导向布局\n preventOverlap: true, // 防止节点重叠\n nodeStrength: -50, // 节点之间的斥力\n edgeStrength: 0.5, // 边的弹性系数\n iterations: 200, // 迭代次数\n animation: true, // 启用布局动画\n },\n});\n```\n\n## theme\n\n> _false \\| 'light' \\| 'dark' \\| string_\n\n设置图表的主题,可以是内置的 `'light'`、`'dark'` 主题,也可以是自定义主题的名称。设为 `false` 则不使用任何主题。\n\n## behaviors\n\n> _(string \\| [CustomExtensionOptions](#customextensionoptions) \\| ((this:Graph) =>CustomExtensionOptions))[]_\n\n配置图表的交互行为,可以是字符串(使用默认配置)、对象(自定义配置)或函数(动态配置、函数内可访问图实例)。\n\n**示例:**\n\n```javascript\nconst graph = new Graph({\n behaviors: [\n 'drag-canvas', // 使用默认配置启用画布拖拽\n 'zoom-canvas', // 使用默认配置启用画布缩放\n {\n type: 'drag-element', // 自定义配置拖拽元素\n key: 'drag-node-only',\n enable: (event) => event.targetType === 'node', // 只允许拖拽节点\n },\n function () {\n console.log(this); // 输出 graph 实例\n return {\n type: 'hover-activate',\n };\n },\n ],\n});\n```\n\n- 查看 [交互总览](/manual/behavior/overview) 深入了解交互原理\n- 浏览 [内置交互](/manual/behavior/auto-adapt-label) 获取所有内置交互列表及其配置选项\n\n## plugins\n\n> _(string \\| [CustomExtensionOptions](#customextensionoptions) \\| ((this:Graph) =>CustomExtensionOptions))[]_\n\n设置图表的插件,可以是字符串(使用默认配置)、对象(自定义配置)或函数(动态配置、函数内可访问图实例)。\n\n**示例:**\n\n```javascript\nconst graph = new Graph({\n container: 'container',\n plugins: [\n 'minimap', // 启用小地图,使用默认配置\n {\n type: 'grid', // 启用网格背景\n key: 'grid-plugin',\n line: {\n stroke: '#d9d9d9',\n lineWidth: 1,\n },\n },\n {\n type: 'toolbar', // 启用工具栏\n key: 'graph-toolbar',\n position: 'top-right', // 位置\n },\n ],\n});\n```\n\n- 查看 [插件总览](/manual/plugin/overview) 深入了解插件原理\n- 浏览 [内置插件](/manual/plugin/background) 获取所有内置插件列表及其配置项\n\n## transforms\n\n> _(string \\| [CustomExtensionOptions](#customextensionoptions) \\| ((this:Graph) =>CustomExtensionOptions))[]_\n\n配置数据处理,用于在渲染前对数据进行处理,不会影响原始数据。可以是字符串(使用默认配置)、对象(自定义配置)或函数(动态配置、函数内可访问图实例)。\n\n**示例:**\n\n```javascript\nconst graph = new Graph({\n transforms: [\n 'process-parallel-edges', // 处理平行边,使用默认配置\n {\n type: 'map-node-size', // 根据节点数据映射节点大小\n field: 'value', // 使用 value 字段的值\n max: 50, // 最大半径\n min: 20, // 最小半径\n },\n ],\n});\n```\n\n- 查看 [数据处理总览](/manual/transform/overview) 深入了解数据处理原理\n- 浏览 [内置数据处理](/manual/transform/map-node-size) 获取所有内置数据处理列表及其配置项\n\n#### CustomExtensionOptions\n\n```typescript\ninterface CustomExtensionOption extends Record {\n /** 拓展类型 */\n type: string;\n /** 拓展 key,即唯一标识 */\n key?: string;\n}\n```\n\n\n\n---\ntitle: Introduction\norder: 0\n---\n\n![](https://user-images.githubusercontent.com/6113694/45008751-ea465300-b036-11e8-8e2a-166cbb338ce2.png)\n\n[![Build Status](https://github.com/antvis/g6/workflows/build/badge.svg?branch=v5)](https://github.com/antvis//actions)\n[![Coverage Status](https://img.shields.io/coveralls/github/antvis/G6/v5.svg)](https://coveralls.io/github/antvis/G6?branch=v5)\n![typescript](https://img.shields.io/badge/language-typescript-red.svg)\n![MIT](https://img.shields.io/badge/license-MIT-000000.svg)\n[![npm package](https://img.shields.io/npm/v/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6)\n[![NPM downloads](http://img.shields.io/npm/dm/@antv/g6.svg)](https://npmjs.org/package/@antv/g6)\n[![Percentage of issues still open](http://isitmaintained.com/badge/open/antvis/g6.svg)](http://isitmaintained.com/project/antvis/g6 'Percentage of issues still open')\n\n

AntV G6

\n\n

\n\n

G6 is a graph visualization engine. It provides capabilities for graph drawing, layout, analysis, interaction, animation, and other aspects of graph visualization. It aims to offer developers a set of tools that are easy to use, professionally reliable, and highly customizable for graph visualization development.

\n\n

Rich Elements

\n\n

Built-in 10+ elements to meet the needs of common scenarios.

\n\n

Flexible Interactions

\n\n

Built-in 10+ interactions that can be freely combined according to different scenarios.

\n\n

High-Performance Layout Algorithms

\n\n

High-performance layouts, built-in 10+ common graph layouts, with support for WebGPU and WASM computational acceleration.

\n\n

Multicolor Themes for Various Scenarios

\n\n

\n\n

3D Scenes

\n\n

\n\n

\n\n

Supports 3D elements and layouts to create immersive graph visualization scenarios.

\n\n

High Customizability

\n\n

Elements, layouts, interactions, and plugins are all customizable, enabling every creative idea you have.

\n\n

Start Your G6 Journey in Just 3 Minutes

\n\n

Click to enter 👉 Quick Start

\n\n
\n\n\n\n\n\n\n\n\n\n\n\n\n
\n\n\n\n---\ntitle: 简介\norder: 0\nsidebar: false\n---\n\n![](https://user-images.githubusercontent.com/6113694/45008751-ea465300-b036-11e8-8e2a-166cbb338ce2.png)\n\n[![Build Status](https://github.com/antvis/g6/workflows/build/badge.svg?branch=v5)](https://github.com/antvis//actions)\n[![Coverage Status](https://img.shields.io/coveralls/github/antvis/G6/v5.svg)](https://coveralls.io/github/antvis/G6?branch=v5)\n![typescript](https://img.shields.io/badge/language-typescript-red.svg)\n![MIT](https://img.shields.io/badge/license-MIT-000000.svg)\n[![npm package](https://img.shields.io/npm/v/@antv/g6.svg)](https://www.npmjs.com/package/@antv/g6)\n[![NPM downloads](http://img.shields.io/npm/dm/@antv/g6.svg)](https://npmjs.org/package/@antv/g6)\n[![Percentage of issues still open](http://isitmaintained.com/badge/open/antvis/g6.svg)](http://isitmaintained.com/project/antvis/g6 'Percentage of issues still open')\n\n

AntV G6

\n\n

\n\n

G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、交互、动画等图可视化能力。旨在为开发者提供一套简单易用、专业可靠、可高度定制的图可视化开发工具

\n\n

丰富元素

\n\n

内置 10+ 元素,满足常规场景需求

\n\n

灵活交互

\n\n

内置 10+ 交互,并可根据不同场景自由组合

\n\n

高性能布局算法

\n\n

高性能布局,内置 10+ 常用的图布局,支持 WebGPU、 WASM 计算加速

\n\n

多色主题,适用多种场景

\n\n

\n\n

3D 场景

\n\n

\n\n

\n\n

支持 3D 元素、布局,打造沉浸式的图可视化场景

\n\n

高可定制性

\n\n

元素、布局、交互、插件统统可定制,实现你的每一个创意

\n\n

仅需 3 分钟,开启 G6 之旅

\n\n

点击进入👉 快速开始

\n\n
\n\n\n\n\n\n\n\n\n\n\n\n\n
\n\n\n\n---\ntitle: AntvDagre Layout\norder: 2\n---\n\n## Overview\n\nAntvDagre builds upon the original [dagre](https://github.com/dagrejs/dagre/wiki) layout and adds more useful options, such as `nodeOrder`, `edgeLabelSpace`, and more. The `dagre` layout itself is a hierarchical layout suitable for directed acyclic graphs (DAGs), which can automatically handle node direction and spacing, and supports both horizontal and vertical layouts. See more Dagre layout [examples](/en/examples#layout-dagre), [source code](https://github.com/dagrejs/dagre/blob/master/lib/layout.js), and [official documentation](https://github.com/dagrejs/dagre/wiki).\n\nDagre Layout\n\n## Configuration\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'antv-dagre',\n rankdir: 'TB',\n align: 'UL',\n nodesep: 50,\n ranksep: 50,\n controlPoints: false,\n },\n});\n```\n\n## Options\n\n> For more native `dagre` options, refer to the [official documentation](https://github.com/dagrejs/dagre/wiki#configuring-the-layout). Here, only some core and new options are listed.\n\n\"Dagre\n\n| Property | Description | Type | Default | Required |\n| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ------- | -------- |\n| type | Layout type | `antv-dagre` | - | ✓ |\n| rankdir | Layout direction, options | `TB` \\| `BT` \\| `LR` \\| `RL` | `TB` | |\n| align | Node alignment, options | `UL` \\| `UR` \\| `DL` \\| `DR` | `UL` | |\n| nodesep | Node spacing (px). For `TB` or `BT`, it's horizontal spacing; for `LR` or `RL`, it's vertical spacing. | number | 50 | |\n| nodesepFunc | Callback for node spacing (px), allows different spacing for different nodes. For `TB` or `BT`, it's horizontal spacing; for `LR` or `RL`, it's vertical spacing. Takes precedence over `nodesep`. | (d?: Node) => number | | |\n| ranksep | Rank spacing (px). For `TB` or `BT`, it's vertical spacing between adjacent ranks; for `LR` or `RL`, it's horizontal spacing. | number | 50 | |\n| ranksepFunc | Callback for rank spacing (px), allows different spacing for different ranks. For `TB` or `BT`, it's vertical spacing; for `LR` or `RL`, it's horizontal spacing. Takes precedence over `ranksep`. | (d?: Node) => number | | |\n| ranker | Algorithm for assigning ranks to nodes: `longest-path`, `tight-tree`, or `network-simplex` | `network-simplex` \\| `tight-tree` \\| `longest-path` | - | |\n| nodeSize | Specify node size for all nodes or each node, used for occupying space and spacing calculation | Size \\| ((nodeData: Node) => Size) | - | |\n| controlPoints | Whether to keep edge control points. Only effective when using built-in polyline edges (`type: 'polyline-edge'`) or any edge that consumes `style.controlPoints` as control points | boolean | false | |\n| begin | Top-left alignment position of the layout | [number, number] \\| [number, number, number] | - | |\n| sortByCombo | Whether to sort nodes in the same rank based on `parentId` in each node's data to avoid Combo overlap | boolean | false | |\n| edgeLabelSpace | Whether to reserve space for edge labels | boolean | true | |\n| nodeOrder | Reference array for node order in the same rank, storing node ids | string[] | - | |\n| radial | Whether to use radial layout based on `dagre` | boolean | - | |\n| focusNode | Focus node, only effective when `radial` is true | ID \\| Node \\| null | - | |\n| preset | Reference node positions for layout calculation, usually used to keep transitions smooth when switching data | NodeData[] | - | |\n\n> Note: In G6, the control points computed by `antv-dagre` are written into `edge.style.controlPoints`.\n\n### align\n\n> _DagreAlign_ **Default:** `UL`\n\nNode alignment: U = upper, D = down, L = left, R = right\n\n- `UL`: align to upper left\n- `UR`: align to upper right\n- `DL`: align to lower left\n- `DR`: align to lower right\n\n### rankdir\n\n> _DagreRankdir_ **Default:** `TB`\n\nLayout direction. T = top, B = bottom, L = left, R = right\n\n- `TB`: top to bottom\n- `BT`: bottom to top\n- `LR`: left to right\n- `RL`: right to left\n\n### ranker\n\n> _`network-simplex` \\| `tight-tree` \\| `longest-path`_\n\nLayout mode\n\n### ranksep\n\n> _number_ **Default:** 50\n\nRank spacing (px)\n\nFor 'TB' or 'BT', it's vertical spacing; for 'LR' or 'RL', it's horizontal spacing. `ranksepFunc` has higher priority.\n\n### ranksepFunc\n\n> _(d?: Node) => number_\n\nCallback for rank spacing (px)\n\nFor 'TB' or 'BT', it's vertical spacing; for 'LR' or 'RL', it's horizontal spacing. Takes precedence over nodesep if set.\n\n### nodesep\n\n> _number_ **Default:** 50\n\nNode spacing (px)\n\nFor 'TB' or 'BT', it's horizontal spacing; for 'LR' or 'RL', it's vertical spacing. `nodesepFunc` has higher priority.\n\n### nodesepFunc\n\n> _(d?: Node) => number_\n\nCallback for node spacing (px), allows different spacing for different nodes\n\nFor 'TB' or 'BT', it's horizontal spacing; for 'LR' or 'RL', it's vertical spacing. Takes precedence over nodesep if set.\n\n### begin\n\n> _[number, number] \\| [number, number, number]_ **Default:** undefined\n\nTop-left alignment position of the layout\n\n### controlPoints\n\n> _boolean_ **Default:** false\n\nWhether to keep edge control points. Only effective when using built-in polyline edges (`type: 'polyline-edge'`) or any edge that uses `style.controlPoints` as control points. Adds `style.controlPoints` to edge data.\n\n### edgeLabelSpace\n\n> _boolean_ **Default:** true\n\nWhether to reserve space for edge labels\n\nThis affects whether a dummy node is added in the middle of the edge.\n\n### focusNode\n\n> _ID \\| Node \\| null_\n\nFocus node, only effective when `radial` is true\n\n- ID: node id\n- Node: node instance\n- null: cancel focus\n\n### nodeOrder\n\n> _string[]_ **Default:** undefined\n\nReference array for node order in the same rank, stores node ids\n\nIf not specified, dagre's default order is used.\n\n### nodeSize\n\n> _Size \\| ((nodeData: Node) => Size)_ **Default:** undefined\n\nSpecify node size for all or each node.\n\nUsed for collision detection to prevent node overlap\n\n### preset\n\n> _OutNode[]_ **Default:** undefined\n\nReference node positions for layout calculation\n\nUsually for smooth transitions when switching data. In G6, if updating data, the existing layout result is used as input.\n\n### radial\n\n> _boolean_\n\nWhether to use radial layout based on dagre\n\n### sortByCombo\n\n> _boolean_ **Default:** false\n\nWhether to sort nodes in the same rank by their parentId to prevent Combo overlap\n\nRecommended to enable when using Combo\n\n## Suitable Scenarios\n\n- **Flowcharts**: Suitable for displaying flowcharts, node direction and spacing are automatically handled\n- **Dependency Graphs**: Display dependencies between packages or modules\n- **Task Scheduling Graphs**: Show dependencies and execution order between tasks\n\n## Related Documentation\n\n> The following documents can help you better understand the Dagre layout\n\n- [Graph Layout Algorithms: In-depth Dagre Layout](https://mp.weixin.qq.com/s/EdyTfFUH7fyMefNSBXI2nA)\n- [In-depth Dagre Layout Algorithm](https://www.yuque.com/antv/g6-blog/xxp5nl)\n\n\n\n---\ntitle: AntV Dagre 布局 AntvDagre\norder: 2\n---\n\n## 概述\n\nAntvDagre 在原先[dagre](https://github.com/dagrejs/dagre/wiki)布局的基础上增加了更多有用的设置项,比如`nodeOrder`、`edgeLabelSpace`等等。 `dagre`布局本身一种层次化布局,适用于有向无环图(DAG)的布局场景,能够自动处理节点之间的方向和间距,支持水平和垂直布局。参考更多 Dagre 布局[样例](/examples#layout-dagre)或[源码](https://github.com/dagrejs/dagre/blob/master/lib/layout.js)以及[官方文档](https://github.com/dagrejs/dagre/wiki)。\n\nDagre布局\n\n## 配置方式\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'antv-dagre',\n rankdir: 'TB',\n align: 'UL',\n nodesep: 50,\n ranksep: 50,\n controlPoints: false,\n },\n});\n```\n\n## 配置项\n\n> 更多`dagre`原生配置项可参考[官方文档](https://github.com/dagrejs/dagre/wiki#configuring-the-layout),这里仅列出部分核心配置和新增的配置\n\n\"Dagre\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ------ | ---- |\n| type | 布局类型 | `antv-dagre` | - | ✓ |\n| rankdir | 布局方向,可选值 | `TB` \\| `BT` \\| `LR` \\| `RL` | `TB` | |\n| align | 节点对齐方式,可选值 | `UL` \\| `UR` \\| `DL` \\| `DR` | `UL` | |\n| nodesep | 节点间距(px)。在 rankdir 为 `TB` 或 `BT` 时是节点的水平间距;在 rankdir 为 `LR` 或 `RL` 时代表节点的竖直方向间距 | number | 50 | |\n| nodesepFunc | 节点间距(px)的回调函数,优先级高于 `nodesep` | (d?: Node) => number | - | |\n| ranksep | 层间距(px)。在 rankdir 为 `TB` 或 `BT` 时是竖直方向相邻层间距;在 rankdir 为 `LR` 或 `RL` 时代表水平方向相邻层间距 | number | 50 | |\n| ranksepFunc | 层间距(px)的回调函数,优先级高于 `ranksep` | (d?: Node) => number | - | |\n| ranker | 为每个节点分配等级的算法,共支持三种算法:`longest-path`、`tight-tree`、`network-simplex` | `network-simplex` \\| `tight-tree` \\| `longest-path` | - | |\n| nodeSize | 统一指定或为每个节点指定节点大小,用于占位与间距计算 | Size \\| ((nodeData: Node) => Size) | - | |\n| controlPoints | 是否保留边的控制点,仅在边配置中使用了内置折线(type: 'polyline-edge')时,或任何将自定义消费了 `style.controlPoints` 字段作为控制点位置的边时生效 | boolean | false | |\n| begin | 布局的左上角对齐位置 | [number, number] \\| [number, number, number] | - | |\n| sortByCombo | 同一层节点是否根据每个节点数据中的 `parentId` 进行排序,以防止 Combo 重叠 | boolean | false | |\n| edgeLabelSpace | 是否为边的 label 预留位置 | boolean | true | |\n| nodeOrder | 同层节点顺序的参考数组,存放节点 id 值 | string[] | - | |\n| radial | 是否基于 `dagre` 进行辐射布局 | boolean | - | |\n| focusNode | 关注的节点,仅在 `radial` 为 true 时生效 | ID \\| Node \\| null | - | |\n| preset | 布局计算时参考的节点位置,一般用于切换数据时保证重新布局的连续性 | NodeData[] | - | |\n\n> 补充:在 G6 中,`antv-dagre` 计算得到的控制点会被写入边的 `style.controlPoints`。\n\n### align\n\n> _DagreAlign_ **Default:** `UL`\n\n节点对齐方式 U:upper(上);D:down(下);L:left(左);R:right(右)\n\n- `UL`:对齐到左上角\n- `UR`:对齐到右上角\n- `DL`:对齐到左下角\n- `DR`:对齐到右下角\n\n### rankdir\n\n> _DagreRankdir_ **Default:** `TB`\n\n布局的方向。T:top(上);B:bottom(下);L:left(左);R:right(右)\n\n- `TB`:从上至下布局\n- `BT`:从下至上布局\n- `LR`:从左至右布局\n- `RL`:从右至左布局\n\n### ranker\n\n> _`network-simplex` \\| `tight-tree` \\| `longest-path`_\n\n布局的模式\n\n### ranksep\n\n> _number_ **Default:** 50\n\n层间距(px)\n\n在 rankdir 为 'TB' 或 'BT' 时是竖直方向相邻层间距;在 rankdir 为 'LR' 或 'RL' 时代表水平方向相邻层间距。ranksepFunc 拥有更高的优先级\n\n### ranksepFunc\n\n> _(d?: Node) => number_\n\n层间距(px)的回调函数\n\n在 rankdir 为 'TB' 或 'BT' 时是竖直方向相邻层间距;在 rankdir 为 'LR' 或 'RL' 时代表水平方向相邻层间距。优先级高于 nodesep,即若设置了 nodesepFunc,则 nodesep 不生效\n\n### nodesep\n\n> _number_ **Default:** 50\n\n节点间距(px)\n\n在 rankdir 为 'TB' 或 'BT' 时是节点的水平间距;在 rankdir 为 'LR' 或 'RL' 时代表节点的竖直方向间距。nodesepFunc 拥有更高的优先级\n\n### nodesepFunc\n\n> _(d?: Node) => number_\n\n节点间距(px)的回调函数,通过该参数可以对不同节点设置不同的节点间距\n\n在 rankdir 为 'TB' 或 'BT' 时是节点的水平间距;在 rankdir 为 'LR' 或 'RL' 时代表节点的竖直方向间距。优先级高于 nodesep,即若设置了 nodesepFunc,则 nodesep 不生效\n\n### begin\n\n> _[number, number] \\| [number, number, number]_ **Default:** undefined\n\n布局的左上角对齐位置\n\n### controlPoints\n\n> _boolean_ **Default:** false\n\n是否保留边的控制点,仅在边配置中使用了内置折线(type: 'polyline-edge') 时,或任何将自定义消费了 `style.controlPoints` 字段作为控制点位置的边时生效。本质上就是给边数据增加了 `style.controlPoints`\n\n### edgeLabelSpace\n\n> _boolean_ **Default:** true\n\n是否为边的label留位置\n\n这会影响是否在边中间添加dummy node\n\n### focusNode\n\n> _ID \\| Node \\| null_\n\n关注的节点,注意,仅在`radial` 为 true 时生效\n\n- ID: 节点 id\n- Node: 节点实例\n- null: 取消关注\n\n### nodeOrder\n\n> _string[]_ **Default:** undefined\n\n同层节点顺序的参考数组,存放节点 id 值\n\n若未指定,则将按照 dagre 本身机制排列同层节点顺序\n\n### nodeSize\n\n> _Size \\| ((nodeData: Node) => Size)_ **Default:** undefined\n\n统一指定或为每个节点指定节点大小。\n\n用于防止节点重叠时的碰撞检测\n\n### preset\n\n> _OutNode[]_ **Default:** undefined\n\n布局计算时参考的节点位置\n\n一般用于切换数据时保证重新布局的连续性。在 G6 中,若是更新数据,则将自动使用已存在的布局结果数据作为输入\n\n### radial\n\n> _boolean_\n\n是否基于 dagre 进行辐射布局\n\n### sortByCombo\n\n> _boolean_ **Default:** false\n\n同一层节点是否根据每个节点数据中的 parentId 进行排序,以防止 Combo 重叠\n\n建议在有 Combo 的情况下配置\n\n## 布局适用场景\n\n- **流程图**:适合展示流程图,节点之间的方向和间距会自动处理;\n- **依赖关系图**:展示软件包或模块之间的依赖关系;\n- **任务调度图**:展示任务之间的依赖关系和执行顺序。\n\n## 相关文档\n\n> 以下文档可以帮助你更好地理解 Dagre 布局\n\n- [图布局算法|详解 Dagre 布局](https://mp.weixin.qq.com/s/EdyTfFUH7fyMefNSBXI2nA)\n- [深入解读Dagre布局算法](https://www.yuque.com/antv/g6-blog/xxp5nl)\n\n\n\n---\ntitle: Common Layout Configuration Options\norder: 1\n---\n\nThis article introduces the common attribute configurations for built-in layouts.\n\n## General Configuration\n\n| Property | Description | Type | Default | Required |\n| ---------------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------- | -------- |\n| type | Layout type, name of built-in or custom layout | [Type](#Type) | - | ✓ |\n| isLayoutInvisibleNodes | Whether invisible nodes participate in the layout (takes effect when preLayout is true) | boolean | false | |\n| nodeFilter | Nodes participating in the layout | (node: NodeData) => boolean | () => true | |\n| comboFilter | Combos participating in the layout | (combo: ComboData) => boolean | () => true | |\n| preLayout | Use pre-layout, calculate layout before initializing elements | boolean | false | |\n| enableWorker | Whether to run the layout in a WebWorker | boolean | - | |\n| iterations | Number of iterations for iterative layout | number | - | |\n| animation | Whether to enable layout animation | boolean | false | |\n| width | Width of the layout area, defaults to the current container width | number | - | |\n| height | Height of the layout area, defaults to the current container height | number | - | |\n| center | Layout center point | [number, number] \\| [number, number, number] | - | |\n| node | Node field mapping, used to map business fields to layout fields | (datum) => ({ id?, x?, y?, z?, parentId?, isCombo? }) | - | |\n| edge | Edge field mapping, used to map business fields to layout fields | (datum) => ({ id?, source?, target? }) | - | |\n\nAdditional notes:\n\n- `width` / `height` / `center` are common layout fields uniformly supported by `@antvis/layout`.\n- `node` / `edge` are used to adapt non-standard business fields such as custom `id` / `source` / `target`.\n- `iterations` is the step count used by the G6 runtime to drive iterative layouts, and is not the same as some layouts' internal algorithm parameters.\n\n### Type\n\nSpecifies the layout type, either the name of a built-in layout type or a custom layout.\n\n```js {4}\nconst graph = new Graph({\n // Other configurations...\n layout: {\n type: 'antv-dagre',\n },\n});\n```\n\nOptional values include:\n\n- `antv-dagre`: [Custom layout based on dagre](/en/manual/layout/antv-dagre-layout)\n- `circular`: [Circular layout](/en/manual/layout/circular-layout)\n- `combo-combined`: [Layout suitable for combinations](/en/manual/layout/combo-combined-layout)\n- `concentric`: [Concentric layout](/en/manual/layout/concentric-layout)\n- `d3-force`: [Force-directed layout based on D3](/en/manual/layout/d3-force-layout)\n- `d3-force-3d`: [3D Force-directed layout](/en/manual/layout/d3-force3-d-layout)\n- `dagre`: [Dagre layout](/en/manual/layout/dagre-layout)\n- `fishbone`: [Fishbone layout](/en/manual/layout/fishbone)\n- `force`: [Force-directed layout](/en/manual/layout/force-layout)\n- `force-atlas2`: [ForceAtlas2 layout](/en/manual/layout/force-atlas2-layout)\n- `fruchterman`: [Fruchterman layout](/en/manual/layout/fruchterman-layout)\n- `grid`: [Grid layout](/en/manual/layout/grid-layout)\n- `mds`: [MDS layout for high-dimensional data](/en/manual/layout/mds-layout)\n- `radial`: [Radial layout](/en/manual/layout/radial-layout)\n- `random`: [Random layout](/en/manual/layout/random-layout)\n- `snake`: [Snake layout](/en/manual/layout/snake)\n- `compact-box`: [Compact box tree layout](/en/manual/layout/compact-box-layout)\n- `dendrogram`: [Dendrogram layout](/en/manual/layout/dendrogram-layout)\n- `mindmap`: [Mindmap layout](/en/manual/layout/mindmap-layout)\n- `indented`: [Indented tree layout](/en/manual/layout/indented-layout)\n\n\n\n---\ntitle: 布局通用配置项\norder: 1\n---\n\n本文介绍内置布局通用属性配置。\n\n## 通用配置\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ---------------------- | ----------------------------------------------------- | ----------------------------------------------------- | ---------- | ---- |\n| type | 布局类型,内置布局或自定义布局的名称 | [Type](#Type) | - | ✓ |\n| isLayoutInvisibleNodes | 不可见节点是否参与布局(当 preLayout 为 true 时生效) | boolean | false | |\n| nodeFilter | 参与该布局的节点 | (node: NodeData) => boolean | () => true | |\n| comboFilter | 参与该布局的combo元素 | (combo: ComboData) => boolean | () => true | |\n| preLayout | 使用前布局,在初始化元素前计算布局 | boolean | false | |\n| enableWorker | 是否在 WebWorker 中运行布局 | boolean | - | |\n| iterations | 迭代布局的迭代次数 | number | - | |\n| animation | 是否启用布局动画 | boolean | false | |\n| width | 布局区域宽度,默认使用当前容器宽度 | number | - | |\n| height | 布局区域高度,默认使用当前容器高度 | number | - | |\n| center | 布局中心点 | [number, number] \\| [number, number, number] | - | |\n| node | 节点字段映射,用于把业务字段映射为布局字段 | (datum) => ({ id?, x?, y?, z?, parentId?, isCombo? }) | - | |\n| edge | 边字段映射,用于把业务字段映射为布局字段 | (datum) => ({ id?, source?, target? }) | - | |\n\n补充说明:\n\n- `width` / `height` / `center` 是 `@antvis/layout` 统一支持的通用布局字段。\n- `node` / `edge` 用于适配非标准 `id/source/target` 业务数据。\n- `iterations` 是 G6 运行时用于驱动迭代布局的步数,不等同于某些布局内部自己的算法参数。\n\n### Type\n\n指定布局类型,内置布局类型名称或自定义布局的名称。\n\n```js {4}\nconst graph = new Graph({\n // 其他配置...\n layout: {\n type: 'antv-dagre',\n },\n});\n```\n\n可选值有:\n\n- `antv-dagre`:[基于 dagre 定制的布局](/manual/layout/antv-dagre-layout)\n- `circular`:[环形布局](/manual/layout/circular-layout)\n- `combo-combined`:[适用于存在组合的布局](/manual/layout/combo-combined-layout)\n- `concentric`:[同心圆布局](/manual/layout/concentric-layout)\n- `d3-force`[基于 D3 的力导向布局](/manual/layout/d3-force-layout)\n- `d3-force-3d`:[3D力导向布局](/manual/layout/d3-force3-d-layout)\n- `dagre`:[dagre 布局](/manual/layout/dagre-layout)\n- `fishbone`:[鱼骨布局](/manual/layout/fishbone)\n- `force`:[力导向布局](/manual/layout/force-layout)\n- `force-atlas2`:[ForceAtlas2 布局](/manual/layout/force-atlas2-layout)\n- `fruchterman`:[Fruchterman 布局](/manual/layout/fruchterman-layout)\n- `grid`:[网格布局](/manual/layout/grid-layout)\n- `mds`:[高维数据降维算法布局](/manual/layout/mds-layout)\n- `radial`:[径向布局](/manual/layout/radial-layout)\n- `random`:[随机布局](/manual/layout/random-layout)\n- `snake`:[蛇形布局](/manual/layout/snake)\n- `compact-box`:[紧凑树布局](/manual/layout/compact-box-layout)\n- `dendrogram`:[树状布局](/manual/layout/dendrogram-layout)\n- `mindmap`:[思维导图布局](/manual/layout/mindmap-layout)\n- `indented`:[缩进树布局](/manual/layout/indented-layout)\n\n\n\n---\ntitle: Circular Layout\norder: 3\n---\n\n## Overview\n\nCircular layout arranges nodes evenly or at intervals on a circle, and also supports spiral layouts by configuring different startRadius and endRadius. See more circular layout [examples](en/examples#layout-circular) or [source code](https://github.com/antvis/layout/blob/v5/packages/layout/src/circular.ts).\n\n## Usage Scenarios\n\n**Circular layout**:\n\n- Suitable for networks with equal relationships and no hierarchical structure\n\n**Spiral layout**:\n\n- Suitable for implicit hierarchies or time series graphs (such as organizational charts, propagation networks)\n\n## Basic Usage\n\nOther settings use the default configuration (layout width and height default to the entire canvas container)\n\n```js\nconst graph = new Graph({\n // other configurations\n layout: {\n type: 'circular',\n },\n});\n```\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| ----------- | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | ----------------------------------------- | -------- |\n| type | Layout type | circular | - | ✓ |\n| angleRatio | How many 2\\*PI between the first and last node | number | 1 | |\n| center | Center of the layout | [number, number]\\|[number, number, number] | [`layout width` / 2, `layout height` / 2] | |\n| clockwise | Whether to arrange clockwise | boolean | true | |\n| divisions | Number of segments on the ring (segments will be evenly distributed, effective when endRadius - startRadius != 0) | number | 1 | |\n| nodeSize | Node size (diameter), used for collision detection | Size \\| ((nodeData: Node) => Size) | 10 | |\n| nodeSpacing | Minimum spacing between rings, used to adjust radius | number \\| ((nodeData: Node) => number) | 10 | |\n| ordering | Node ordering on the ring, [see details](#ordering) | `topology` \\| `topology-directed` \\| `degree` | - | |\n| radius | Circle radius, if set, spiral layout configs `startRadius` and `endRadius` are ignored, [see details](#radius) | number | - | |\n| startAngle | Start angle of the layout | number | 0 | |\n| endAngle | End angle of the layout | number | 2 \\* Math.PI | |\n| startRadius | Start radius for spiral layout, [usage](#spiral-layout) | number | - | |\n| endRadius | End radius for spiral layout | number | - | |\n| width | Layout width | number | canvas width | |\n| height | Layout height | number | canvas height | |\n\n### ordering\n\nNode ordering on the ring\n\n- `topology`: topological order\n- `topology-directed`: topological order (directed graph)\n- `degree`: order by degree\n\nIf not set (`null`), the order in the array is used directly\n\n### radius\n\nIf radius, startRadius, and endRadius are not set, the default is `Math.min(layout width, layout height) / 2`, i.e., fills the entire layout area\n\n## Code Examples\n\n### Basic Circular Layout\n\n```javascript\nconst graph = new Graph({\n // other configurations\n layout: {\n type: 'circular',\n },\n});\n```\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/circular.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data,\n node: {\n style: {\n labelText: (d) => d.id,\n labelFill: '#fff',\n labelPlacement: 'center',\n },\n },\n layout: {\n type: 'circular',\n },\n behaviors: ['drag-canvas', 'drag-element'],\n });\n\n graph.render();\n });\n```\n\n### Spiral Layout\n\n```javascript\nconst graph = new Graph({\n // other configurations\n layout: {\n type: 'circular',\n startRadius: 10,\n endRadius: 300,\n },\n});\n```\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/circular.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'center',\n data,\n node: {\n style: {\n labelText: (d) => d.id,\n labelFill: '#fff',\n labelPlacement: 'center',\n },\n },\n layout: {\n type: 'circular',\n startRadius: 10,\n endRadius: 300,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: 环形布局 Circular\norder: 3\n---\n\n## 概述\n\n环形布局是一种把节点均匀或者按间隔放置在圆上的布局,也支持通过配置 startRadius 和 endRadius 为不一样的值实现螺旋状布局。参考更多环形布局[样例](/examples#layout-circular)或[源码](https://github.com/antvis/layout/blob/v5/packages/layout/src/circular.ts)。\n\n## 使用场景\n\n**环形布局**:\n\n- 适用于平等关系网络、无层级结构的图\n\n**螺旋状布局**:\n\n- 适用于隐式层级或时间序列图(如组织架构、传播网络)\n\n## 基本用法\n\n其余均使用默认配置(布局宽高默认是整个画布容器)\n\n```js\nconst graph = new Graph({\n // 其他配置\n layout: {\n type: 'circular',\n },\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ----------- | ----------------------------------------------------------------------------------- | --------------------------------------------- | -------------------------------- | ---- |\n| type | 布局类型 | circular | - | ✓ |\n| angleRatio | 从第一个节点到最后节点之间相隔多少个 2\\*PI | number | 1 | |\n| center | 布局的中心 | [number, number]\\|[number, number, number] | [`布局宽度` / 2, `布局高度` / 2] | |\n| clockwise | 是否顺时针排列 | boolean | true | |\n| divisions | 节点在环上的分段数(几个段将均匀分布,在 endRadius - startRadius != 0 时生效) | number | 1 | |\n| nodeSize | 节点大小(直径)。用于防止节点重叠时的碰撞检测 | Size \\| ((nodeData: Node) => Size) | 10 | |\n| nodeSpacing | 环与环之间最小间距,用于调整半径 | number \\| ((nodeData: Node) => number) | 10 | |\n| ordering | 节点在环上排序的依据,[说明](#ordering) | `topology` \\| `topology-directed` \\| `degree` | - | |\n| radius | 圆的半径,设置了则螺旋状布局的配置`startRadius`、`endRadius`不生效,[说明](#radius) | number | - | |\n| startAngle | 布局的开始角度 | number | 0 | |\n| endAngle | 布局的结束角度 | number | 2 \\* Math.PI | |\n| startRadius | 螺旋状布局的开始半径,[用法](#螺旋状布局) | number | - | |\n| endRadius | 螺旋状布局的结束半径 | number | - | |\n| width | 布局的宽度 | number | 画布宽度 | |\n| height | 布局的高度 | number | 画布高度 | |\n\n### ordering\n\n节点在环上排序的依据\n\n- `topology`: 按照拓扑排序\n- `topology-directed`: 按照拓扑排序(有向图)\n- `degree`: 按照度数大小排序\n\n不配置(`null`)则直接使用数组中的顺序\n\n### radius\n\n如果radius、startRadius、endRadius都没配置,则默认为最终计算出来的`Math.min(布局宽度, 布局高度) / 2`,即布满整个布局区域\n\n## 代码示例\n\n### 基础环形布局\n\n```javascript\nconst graph = new Graph({\n // 其他配置\n layout: {\n type: 'circular',\n },\n});\n```\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/circular.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data,\n node: {\n style: {\n labelText: (d) => d.id,\n labelFill: '#fff',\n labelPlacement: 'center',\n },\n },\n layout: {\n type: 'circular',\n },\n behaviors: ['drag-canvas', 'drag-element'],\n });\n\n graph.render();\n });\n```\n\n### 螺旋状布局\n\n```javascript\nconst graph = new Graph({\n // 其他配置\n layout: {\n type: 'circular',\n startRadius: 10,\n endRadius: 300,\n },\n});\n```\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/circular.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'center',\n data,\n node: {\n style: {\n labelText: (d) => d.id,\n labelFill: '#fff',\n labelPlacement: 'center',\n },\n },\n layout: {\n type: 'circular',\n startRadius: 10,\n endRadius: 300,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: ComboCombined Layout\norder: 4\n---\n\n## Overview\n\nComboCombined composite layout is suitable for graph data with composite group structures. It supports flexible configuration of the layout for elements inside combos as well as the layout between the outermost combos and nodes. By default, the internal elements use the Concentric layout, and the outer layout uses the gForce force-directed layout, balancing layout effect and overall stability. See more ComboCombined layout [examples](/en/examples#layout-combo-layout) and [source code](https://github.com/antvis/layout/blob/v5/packages/layout/src/combo-combined.ts).\n\n## Usage Scenarios\n\n- User profile analysis: Analyze user behavior and product relationships, use user interest circles as combos, display specific products and behavior tags as internal nodes, and help operators identify user consumption paths.\n- Supply chain management graph: Divide suppliers, manufacturers, warehouses, and distributors into combos by role or region, display resources, personnel, or equipment as internal nodes, and clearly show the internal structure of each link in the supply chain.\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------ | -------- |\n| type | Layout type | `combo-combined` | - | ✓ |\n| center | Layout center | [`PointTuple`](https://github.com/antvis/layout/blob/v5/packages/layout/src/types.ts#L829) | Graph center | |\n| layout | Layout configuration. Can be fixed, or returned dynamically based on `comboId` | `string` \\| `object` \\| `(comboId?: string) => string \\| object` | - | |\n| nodeSize | Node size (diameter), used for collision detection | `number` \\| `number[]` \\| (d?: [NodeData](/en/manual/data#节点数据nodedata)) => number | - | |\n| nodeSpacing | Spacing between nodes | `number` \\| (d?: [NodeData](/en/manual/data#节点数据nodedata)) => number | - | |\n| comboSpacing | Spacing between combos | `number` \\| (d?: unknown) => number | - | |\n| comboPadding | Padding value inside the combo, used only for force calculation, not for rendering. It is recommended to set the same value as the visual padding. | `((d?: unknown) => number)` \\| `number` \\| `number[]` \\| `undefined` | - | |\n\n### layout\n\n> _`string | object | (comboId?: string) => string | object`_\n\nIn `5.1`, it is recommended to use a single `layout` field to choose layouts for different levels, instead of configuring `innerLayout` and `outerLayout` separately.\n\n**Example**:\n\n```ts\nnew Graph({\n layout: {\n type: 'combo-combined',\n layout: (comboId) => (comboId ? { type: 'grid' } : { type: 'force' }),\n },\n});\n```\n\n## Example Code\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/combo.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n data,\n layout: {\n type: 'combo-combined',\n comboPadding: 2,\n },\n node: {\n style: {\n size: 20,\n labelText: (d) => d.id,\n },\n palette: {\n type: 'group',\n field: (d) => d.combo,\n },\n },\n edge: {\n style: (model) => {\n const { size, color } = model.data;\n return {\n stroke: color || '#99ADD1',\n lineWidth: size || 1,\n };\n },\n },\n behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas'],\n autoFit: 'view',\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: 复合布局 ComboCombined\norder: 4\n---\n\n## 概述\n\nComboCombined 复合布局适用于复合分组结构的图数据展示场景,支持灵活配置 Combo 内部元素的布局以及最外层 Combo 和节点之间的布局。 默认情况内部元素采用 Concentric 同心圆布局,外部布局采用 gForce 力导向布局,兼顾布局效果与整体稳定性。参考更多 ComboCombined 复合布局[样例](/examples#layout-combo-layout)和[源码](https://github.com/antvis/layout/blob/v5/packages/layout/src/combo-combined.ts)\n\n## 使用场景\n\n- 用户画像分析: 分析用户行为与商品关系,将用户兴趣圈层作为 Combo,内部节点展示具体商品和行为标签,帮助运营人员识别用户消费路径。\n- 供应链管理图:供应商、制造商、仓储、分销商按角色或区域划分 Combo,内部节点展示资源、人员或设备,清晰展示供应链各环节内部结构。\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------ | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------ | ---- |\n| type | 布局类型 | `combo-combined` | - | ✓ |\n| center | 布局中心 | [`PointTuple`](https://github.com/antvis/layout/blob/v5/packages/layout/src/types.ts#L829) | 图中心 | |\n| layout | 布局配置。可固定指定,也可根据 `comboId` 动态返回 | `string` \\| `object` \\| `(comboId?: string) => string \\| object` | - | |\n| nodeSize | 节点大小(直径)。用于碰撞检测 | `number` \\| `number[]` \\| (d?: [NodeData](/manual/data#节点数据nodedata)) => number | - | |\n| nodeSpacing | 节点间距 | `number` \\| (d?: [NodeData](/manual/data#节点数据nodedata)) => number | - | |\n| comboSpacing | Combo 之间的间距 | `number` \\| (d?: unknown) => number | - | |\n| comboPadding | Combo 内部的 padding 值,不用于渲染,仅用于计算力。推荐设置为与视图上 Combo 内部 padding 值相同的值 | `((d?: unknown) => number)` \\| `number` \\| `number[]` \\| `undefined` | - | |\n\n### layout\n\n> _`string | object | (comboId?: string) => string | object`_\n\n5.1 中推荐使用单个 `layout` 字段为不同层级选择布局,而不是分别配置 `innerLayout` 和 `outerLayout`。\n\n**示例**:\n\n```ts\nnew Graph({\n layout: {\n type: 'combo-combined',\n layout: (comboId) => (comboId ? { type: 'grid' } : { type: 'force' }),\n },\n});\n```\n\n## 示例代码\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/combo.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n data,\n layout: {\n type: 'combo-combined',\n comboPadding: 2,\n },\n node: {\n style: {\n size: 20,\n labelText: (d) => d.id,\n },\n palette: {\n type: 'group',\n field: (d) => d.combo,\n },\n },\n edge: {\n style: (model) => {\n const { size, color } = model.data;\n return {\n stroke: color || '#99ADD1',\n lineWidth: size || 1,\n };\n },\n },\n behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas'],\n autoFit: 'view',\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: CompactBox Layout\norder: 5\n---\n\n## Overview\n\nThe CompactBox layout is suitable for visualizing structured tree data. It is evolved from the classic [Reingold–Tilford tidy layout algorithm](http://emr.cs.iit.edu/~reingold/tidier-drawings.pdf), and considers the bounding box of each tree node during layout, effectively maintaining the compactness and hierarchical clarity of the tree structure. See more CompactBox layout [examples](/en/examples#layout-compact-box) and [source code](https://github.com/antvis/hierarchy/blob/master/src/compact-box.js).\n\nCompactBox Tidy Tree Layout Example\n\n## Usage Scenarios\n\n- Decision trees: The compact tree layout can visually and intuitively display each decision path.\n- Knowledge graphs: Show hierarchical relationships and connections between concepts. The compact layout can present complex knowledge networks in limited space.\n\n## Configuration\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'compact-box',\n direction: 'LR',\n getHeight: () => 16,\n getWidth: () => 16,\n getVGap: () => 16,\n getHGap: () => 40,\n },\n});\n```\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| --------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------- | -------- |\n| type | Layout type | `compact-box` | - | ✓ |\n| direction | Layout direction, [options](#direction) | `LR` \\| `RL` \\| `TB` \\| `BT` \\| `H` \\| `V` | `LR` | |\n| getSide | Set whether the node is on the left or right of the root. Only works for `H` direction. [See below](#getside) | (d: { data?: [NodeData](/en/manual/data#节点数据nodedata) }) => string | | |\n| getId | Callback for node id | (d?: [NodeData](/en/manual/data#节点数据nodedata)) => string | | |\n| getWidth | Callback for node width | (d?: [NodeData](/en/manual/data#节点数据nodedata)) => number | | |\n| getHeight | Callback for node height | (d?: [NodeData](/en/manual/data#节点数据nodedata)) => number | | |\n| getHGap | Callback for horizontal gap | (d?: [NodeData](/en/manual/data#节点数据nodedata)) => number | | |\n| getVGap | Callback for vertical gap | (d?: [NodeData](/en/manual/data#节点数据nodedata)) => number | | |\n| radial | Whether to enable radial layout, [see below](#radial) | boolean | false | |\n\n### direction\n\n> `LR` \\| `RL` \\| `TB` \\| `BT` \\| `H` \\| `V` **Default:** `LR`\n\nTree layout direction\n\n- `TB`: Root at the top, layout downwards\n- `BT`: Root at the bottom, layout upwards\n- `LR`: Root at the left, layout to the right\n- `RL`: Root at the right, layout to the left\n- `H`: Root in the middle, horizontal symmetric layout. You can use `getSide` to specify the left/right logic for each node\n- `V`: Root in the middle, vertical symmetric layout\n\n### getSide\n\n> _(d: { data?: [NodeData](/en/manual/data#节点数据nodedata) }) => string_\n\nSet whether the node is on the left or right of the root. Only works for `H` direction. If not set, the algorithm will automatically assign left/right. See [getSide auto logic](https://github.com/antvis/hierarchy/blob/d786901874f59d96c47e2a5dfe17b373eefd72e3/src/layout/separate-root.js#L11).\n\nExample:\n\n```javascript\n({ data }) => {\n // data is a node\n if (data.id === 'test-child-id') return 'right';\n return 'left';\n};\n```\n\n### getId\n\n> _(d?: [NodeData](/en/manual/data#节点数据nodedata)) => string_\n\nCallback for node id\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n return d.id + '_node';\n};\n```\n\n### getWidth\n\n> _(d?: [NodeData](/en/manual/data#节点数据nodedata)) => number_\n\nCallback for node width\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getHeight\n\n> _(d?: [NodeData](/en/manual/data#节点数据nodedata)) => number_\n\nCallback for node height\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getHGap\n\n> _(d?: [NodeData](/en/manual/data#节点数据nodedata)) => number_\n\nCallback for horizontal gap\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getVGap\n\n> _(d?: [NodeData](/en/manual/data#节点数据nodedata)) => number_\n\nCallback for vertical gap\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### radial\n\n> _boolean_\n\nWhether to use radial layout. If `radial` is `true`, it is recommended to set `direction` to `'LR'` or `'RL'`.\n\nimg\n\n## Example Code\n\n```js | ob { inject: true }\nimport { Graph, treeToGraphData } from '@antv/g6';\n\n/**\n * If the node is a leaf node\n * @param {*} d - node data\n * @returns {boolean} - whether the node is a leaf node\n */\nfunction isLeafNode(d) {\n return !d.children || d.children.length === 0;\n}\n\nfetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data: treeToGraphData(data),\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element', 'collapse-expand'],\n node: {\n style: {\n labelText: (d) => d.id,\n labelPlacement: (d) => (isLeafNode(d) ? 'right' : 'left'),\n labelBackground: true,\n ports: [{ placement: 'right' }, { placement: 'left' }],\n },\n animation: {\n enter: false,\n },\n },\n edge: {\n type: 'cubic-horizontal',\n animation: {\n enter: false,\n },\n },\n layout: {\n type: 'compact-box',\n direction: 'LR',\n getHeight: function getHeight() {\n return 32;\n },\n getWidth: function getWidth() {\n return 32;\n },\n getVGap: function getVGap() {\n return 10;\n },\n getHGap: function getHGap() {\n return 100;\n },\n },\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: 紧凑树布局 CompactBox\norder: 5\n---\n\n## 概述\n\n紧凑树布局适用于结构化树形数据的展示,基于经典的 [Reingold–Tilford tidy 布局算法](http://emr.cs.iit.edu/~reingold/tidier-drawings.pdf) 演进而来,通过布局时综合考虑每个树节点的包围盒,有效保持树结构的紧凑性与层次清晰。参考更多 CompactBox 布局[样例](/examples#layout-compact-box)和[源码](https://github.com/antvis/hierarchy/blob/master/src/compact-box.js)\n\nCompactBox 紧凑树布局示例\n\n## 使用场景\n\n- 决策树: 通过紧凑树布局可简单直观的图形化展示每个决策路径\n- 知识图谱: 展示概念之间的层级关系和连接,紧凑布局可以在有限空间内呈现复杂的知识网络\n\n## 配置方式\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'compact-box',\n direction: 'LR',\n getHeight: () => 16,\n getWidth: () => 16,\n getVGap: () => 16,\n getHGap: () => 40,\n },\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ------ | ---- |\n| type | 布局类型 | `compact-box` | - | ✓ |\n| direction | 布局方向,[可选值](#direction) | `LR` \\| `RL` \\| `TB` \\| `BT` \\| `H` \\| `V` | `LR` | |\n| getSide | 设置节点排布在根节点的左侧/右侧,如未设置,则算法自动分配左侧/右侧。注意:该参数仅在 `H` 布局方向上生效 | (d: { data?: [NodeData](/manual/data#节点数据nodedata) }) => string | | |\n| getId | 节点 id 的回调函数 | (d?: [NodeData](/manual/data#节点数据nodedata)) => string | | |\n| getWidth | 计算每个节点的宽度 | (d?: [NodeData](/manual/data#节点数据nodedata)) => number | | |\n| getHeight | 计算每个节点的高度 | (d?: [NodeData](/manual/data#节点数据nodedata)) => number | | |\n| getHGap | 计算每个节点的水平间隙 | (d?: [NodeData](/manual/data#节点数据nodedata)) => number | | |\n| getVGap | 计算每个节点的垂直间隙 | (d?: [NodeData](/manual/data#节点数据nodedata)) => number | | |\n| radial | 是否启用辐射状布局,[说明](#radial) | boolean | false | |\n\n### direction\n\n> `LR` \\| `RL` \\| `TB` \\| `BT` \\| `H` \\| `V` **Default:** `LR`\n\n树布局方向\n\n- `TB`:根节点在上,往下布局\n\n 垂直布局\n\n- `BT`:根节点在下,往上布局\n\n 垂直布局\n\n- `LR`:根节点在左,往右布局\n\n 水平布局\n\n- `RL`:根节点在右,往左布局\n\n 水平布局\n\n- `H`:根节点在中间,水平对称布局。可传入 `getSide` 方法指定每个节点的左右分布逻辑\n\n 水平布局\n\n- `V`:根节点在中间,垂直对称布局\n\n 垂直布局\n\n### getSide\n\n> _(d: { data?: [NodeData](/manual/data#节点数据nodedata) }) => string_\n\n设置节点排布在根节点的左侧/右侧。注意:该参数仅在 `direction` 为 `H` 时生效。如未设置,会默认将子节点前半部分放置在右侧,后半部分放置在左侧,参考 [getSide自动计算逻辑](https://github.com/antvis/hierarchy/blob/d786901874f59d96c47e2a5dfe17b373eefd72e3/src/layout/separate-root.js#L11)。\n\n示例:\n\n```javascript\n({ data }) => {\n // data 是一个节点\n if (data.id === 'test-child-id') return 'right';\n return 'left';\n};\n```\n\n### getId\n\n> _(d?: [NodeData](/manual/data#节点数据nodedata)) => string_\n\n节点 id 的回调函数\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n return d.id + '_node';\n};\n```\n\n### getWidth\n\n> _(d?: [NodeData](/manual/data#节点数据nodedata)) => number_\n\n每个节点的宽度\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getHeight\n\n> _(d?: [NodeData](/manual/data#节点数据nodedata)) => number_\n\n每个节点的高度\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getHGap\n\n> _(d?: [NodeData](/manual/data#节点数据nodedata)) => number_\n\n每个节点的水平间隙\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getVGap\n\n> _(d?: [NodeData](/manual/data#节点数据nodedata)) => number_\n\n每个节点的垂直间隙\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### radial\n\n> _boolean_\n\n是否按照辐射状布局。若 `radial` 为 `true`,建议 `direction` 设置为 `'LR'` 或 `'RL'`\n\nimg\n\n## 代码示例\n\n```js | ob { inject: true }\nimport { Graph, treeToGraphData } from '@antv/g6';\n\n/**\n * If the node is a leaf node\n * @param {*} d - node data\n * @returns {boolean} - whether the node is a leaf node\n */\nfunction isLeafNode(d) {\n return !d.children || d.children.length === 0;\n}\n\nfetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data: treeToGraphData(data),\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element', 'collapse-expand'],\n node: {\n style: {\n labelText: (d) => d.id,\n labelPlacement: (d) => (isLeafNode(d) ? 'right' : 'left'),\n labelBackground: true,\n ports: [{ placement: 'right' }, { placement: 'left' }],\n },\n animation: {\n enter: false,\n },\n },\n edge: {\n type: 'cubic-horizontal',\n animation: {\n enter: false,\n },\n },\n layout: {\n type: 'compact-box',\n direction: 'LR',\n getHeight: function getHeight() {\n return 32;\n },\n getWidth: function getWidth() {\n return 32;\n },\n getVGap: function getVGap() {\n return 10;\n },\n getHGap: function getHGap() {\n return 100;\n },\n },\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: Concentric Layout\norder: 6\n---\n\n## Overview\n\nThe concentric layout arranges nodes in layers according to a certain sorting rule, with each layer of nodes placed around a common center. See more concentric layout [examples](/en/examples#layout-concentric) or [source code](https://github.com/antvis/layout/blob/v5/packages/layout/src/circular.ts).\n\n## Usage Scenarios\n\n- Layered data visualization, such as permission structures, organizational charts, etc., with the center as the top-level role and outer rings as lower-level nodes.\n- Visualization of ranking analysis results, with high-importance nodes in the center and low-importance nodes on the periphery, quickly expressing the relative influence of nodes in the graph.\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | --------------------------------------------------------- | -------- | --------- | --- |\n| type | Layout type | `concentric` | - | ✓ |\n| center | Center position of the circular layout, defaults to the center of the container | [number, number] \\| [number, number, number] | - | |\n| clockwise | Whether to arrange nodes clockwise | boolean | false | |\n| equidistant | Whether the distance between rings is equal | boolean | false | |\n| width | Layout width, defaults to container width | number | - | |\n| height | Layout height, defaults to container height | number | - | |\n| sortBy | The property to sort by (node attribute name). The higher the value, the closer to the center. If set to 'degree', nodes with higher degree are placed closer to the center | string | `degree` | |\n| maxLevelDiff | Maximum attribute difference in the same layer. If undefined, set to maxValue / 4, where maxValue is the maximum value of the sorting property | number | undefined | |\n| nodeSize | Node size (diameter), used for collision detection | number \\| number[] \\| ((nodeData: Node) => number) | 30 | |\n| nodeSpacing | Minimum spacing between rings, used to adjust the radius | number \\| number[] \\| ((node?: Node) => number) | 10 | |\n| preventOverlap | Whether to prevent overlap. Must be used with nodeSize or data.size. Only works if node size is set in data or in this layout config. | boolean | false | |\n| startAngle | The angle (in radians) to start laying out nodes | number | 3 / 2 \\* Math.PI | |\n| sweep | The angle difference between the first and last node in the same layer. If undefined, it is set to `2 _ Math.PI _ (1 - 1 / | level.nodes | )`, where `level.nodes` is the set of nodes in that layer | number | undefined | |\n\n## Example Code\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 500,\n height: 250,\n autoFit: 'view',\n data: {\n nodes: [\n { id: 'center', data: { label: 'Center', level: 0 } },\n { id: 'level1-0', data: { label: 'L1-0', level: 1 } },\n { id: 'level1-1', data: { label: 'L1-1', level: 1 } },\n { id: 'level1-2', data: { label: 'L1-2', level: 1 } },\n { id: 'level1-3', data: { label: 'L1-3', level: 1 } },\n { id: 'level1-4', data: { label: 'L1-4', level: 1 } },\n { id: 'level1-5', data: { label: 'L1-5', level: 1 } },\n { id: 'level2-0', data: { label: 'L2-0', level: 2 } },\n { id: 'level2-1', data: { label: 'L2-1', level: 2 } },\n { id: 'level2-2', data: { label: 'L2-2', level: 2 } },\n { id: 'level2-3', data: { label: 'L2-3', level: 2 } },\n { id: 'level2-4', data: { label: 'L2-4', level: 2 } },\n { id: 'level2-5', data: { label: 'L2-5', level: 2 } },\n { id: 'level2-6', data: { label: 'L2-6', level: 2 } },\n { id: 'level2-7', data: { label: 'L2-7', level: 2 } },\n { id: 'level2-8', data: { label: 'L2-8', level: 2 } },\n { id: 'level2-9', data: { label: 'L2-9', level: 2 } },\n { id: 'level2-10', data: { label: 'L2-10', level: 2 } },\n { id: 'level2-11', data: { label: 'L2-11', level: 2 } },\n ],\n edges: [\n { id: 'e-center-level1-0', source: 'center', target: 'level1-0' },\n { id: 'e-center-level1-1', source: 'center', target: 'level1-1' },\n { id: 'e-center-level1-2', source: 'center', target: 'level1-2' },\n { id: 'e-center-level1-3', source: 'center', target: 'level1-3' },\n { id: 'e-center-level1-4', source: 'center', target: 'level1-4' },\n { id: 'e-center-level1-5', source: 'center', target: 'level1-5' },\n { id: 'e-level1-0-level2-0', source: 'level1-0', target: 'level2-0' },\n { id: 'e-level1-0-level2-1', source: 'level1-0', target: 'level2-1' },\n { id: 'e-level1-1-level2-2', source: 'level1-1', target: 'level2-2' },\n { id: 'e-level1-1-level2-3', source: 'level1-1', target: 'level2-3' },\n { id: 'e-level1-2-level2-4', source: 'level1-2', target: 'level2-4' },\n { id: 'e-level1-2-level2-5', source: 'level1-2', target: 'level2-5' },\n { id: 'e-level1-3-level2-6', source: 'level1-3', target: 'level2-6' },\n { id: 'e-level1-3-level2-7', source: 'level1-3', target: 'level2-7' },\n { id: 'e-level1-4-level2-8', source: 'level1-4', target: 'level2-8' },\n { id: 'e-level1-4-level2-9', source: 'level1-4', target: 'level2-9' },\n { id: 'e-level1-5-level2-10', source: 'level1-5', target: 'level2-10' },\n { id: 'e-level1-5-level2-11', source: 'level1-5', target: 'level2-11' },\n ],\n },\n layout: {\n type: 'concentric',\n nodeSize: 32,\n sortBy: 'degree',\n preventOverlap: true,\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n animation: false,\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 同心圆布局 Concentric\norder: 6\n---\n\n## 概述\n\n同心圆布局是一种将节点根据某种排序规则分层,并以圆心为中心、沿圆周排列每层节点的布局方式。参考更多同心圆布局[样例](/examples#layout-concentric)或[源码](https://github.com/antvis/layout/blob/v5/packages/layout/src/circular.ts)。\n\n## 使用场景\n\n- 分层数据可视化,如权限控制结构、组织架构图等,中心是顶级角色,外圈为下级节点。\n- 排序分析结果可视化,高重要度放中心,低重要度放外围,快速表达图中节点的相对影响力。\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ---------------- | ---- |\n| type | 布局类型 | `concentric` | - | ✓ |\n| center | 圆形布局的中心位置,默认为当前容器的中心位置 | [number, number] \\| [number, number, number] | - | |\n| clockwise | 是否按照顺时针排列 | boolean | false |\n| equidistant | 环与环之间的距离是否相等 | boolean | false | |\n| width | 布局的宽度,默认使用容器宽度 | number | - | |\n| height | 布局的高度,默认使用容器高度 | number | - | |\n| sortBy | 指定排序的依据(节点属性名)
数值越高则该节点被放置得越中心。若为 degree,则会计算节点的度数,度数越高,节点将被放置得越中心 | string | `degree` | |\n| maxLevelDiff | 同一层节点的最大属性差值
若为 undefined,则将会被设置为 maxValue / 4 ,其中 maxValue 为最大的排序依据的属性值。例如,若 sortBy 为 'degree',则 maxValue 为所有节点中度数最大的节点的度数 | number | undefined | |\n| nodeSize | 节点大小(直径)。用于防止节点重叠时的碰撞检测 | number \\| number[] \\| ((nodeData: Node) => number) | 30 | |\n| nodeSpacing | 环与环之间最小间距,用于调整半径 | number \\| number[] \\| ((node?: Node) => number) | 10 | |\n| preventOverlap | 是否防止重叠
必须配合 nodeSize 属性或节点数据中的 data.size 属性,只有在数据中设置了 data.size 或在该布局中配置了与当前图节点大小相同的 nodeSize 值,才能够进行节点重叠的碰撞检测 | boolean | false | |\n| startAngle | 开始布局节点的弧度 | number | 3 / 2 \\* Math.PI | |\n| sweep | 同一层中第一个节点与最后一个节点之间的弧度差
若为 undefined ,则将会被设置为 2 \\* Math.PI \\* (1 - 1 / \\|level.nodes\\|) ,其中 level.nodes 为该算法计算出的每一层的节点,\\|level.nodes\\| 代表该层节点数量 | number | undefined | |\n\n## 代码示例\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 500,\n height: 250,\n autoFit: 'view',\n data: {\n nodes: [\n { id: 'center', data: { label: '中心', level: 0 } },\n { id: 'level1-0', data: { label: 'L1-0', level: 1 } },\n { id: 'level1-1', data: { label: 'L1-1', level: 1 } },\n { id: 'level1-2', data: { label: 'L1-2', level: 1 } },\n { id: 'level1-3', data: { label: 'L1-3', level: 1 } },\n { id: 'level1-4', data: { label: 'L1-4', level: 1 } },\n { id: 'level1-5', data: { label: 'L1-5', level: 1 } },\n { id: 'level2-0', data: { label: 'L2-0', level: 2 } },\n { id: 'level2-1', data: { label: 'L2-1', level: 2 } },\n { id: 'level2-2', data: { label: 'L2-2', level: 2 } },\n { id: 'level2-3', data: { label: 'L2-3', level: 2 } },\n { id: 'level2-4', data: { label: 'L2-4', level: 2 } },\n { id: 'level2-5', data: { label: 'L2-5', level: 2 } },\n { id: 'level2-6', data: { label: 'L2-6', level: 2 } },\n { id: 'level2-7', data: { label: 'L2-7', level: 2 } },\n { id: 'level2-8', data: { label: 'L2-8', level: 2 } },\n { id: 'level2-9', data: { label: 'L2-9', level: 2 } },\n { id: 'level2-10', data: { label: 'L2-10', level: 2 } },\n { id: 'level2-11', data: { label: 'L2-11', level: 2 } },\n ],\n edges: [\n { id: 'e-center-level1-0', source: 'center', target: 'level1-0' },\n { id: 'e-center-level1-1', source: 'center', target: 'level1-1' },\n { id: 'e-center-level1-2', source: 'center', target: 'level1-2' },\n { id: 'e-center-level1-3', source: 'center', target: 'level1-3' },\n { id: 'e-center-level1-4', source: 'center', target: 'level1-4' },\n { id: 'e-center-level1-5', source: 'center', target: 'level1-5' },\n\n { id: 'e-level1-0-level2-0', source: 'level1-0', target: 'level2-0' },\n { id: 'e-level1-0-level2-1', source: 'level1-0', target: 'level2-1' },\n { id: 'e-level1-1-level2-2', source: 'level1-1', target: 'level2-2' },\n { id: 'e-level1-1-level2-3', source: 'level1-1', target: 'level2-3' },\n { id: 'e-level1-2-level2-4', source: 'level1-2', target: 'level2-4' },\n { id: 'e-level1-2-level2-5', source: 'level1-2', target: 'level2-5' },\n { id: 'e-level1-3-level2-6', source: 'level1-3', target: 'level2-6' },\n { id: 'e-level1-3-level2-7', source: 'level1-3', target: 'level2-7' },\n { id: 'e-level1-4-level2-8', source: 'level1-4', target: 'level2-8' },\n { id: 'e-level1-4-level2-9', source: 'level1-4', target: 'level2-9' },\n { id: 'e-level1-5-level2-10', source: 'level1-5', target: 'level2-10' },\n { id: 'e-level1-5-level2-11', source: 'level1-5', target: 'level2-11' },\n ],\n },\n layout: {\n type: 'concentric',\n nodeSize: 32,\n sortBy: 'degree',\n preventOverlap: true,\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n animation: false,\n});\n\ngraph.render();\n```\n\n
展开查看完整代码\n\n```javascript\nimport { Graph } from '@antv/g6';\nconst graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data: {\n nodes: [\n { id: 'center', data: { label: '中心', level: 0 } },\n\n { id: 'level1-0', data: { label: 'L1-0', level: 1 } },\n { id: 'level1-1', data: { label: 'L1-1', level: 1 } },\n { id: 'level1-2', data: { label: 'L1-2', level: 1 } },\n { id: 'level1-3', data: { label: 'L1-3', level: 1 } },\n { id: 'level1-4', data: { label: 'L1-4', level: 1 } },\n { id: 'level1-5', data: { label: 'L1-5', level: 1 } },\n\n { id: 'level2-0', data: { label: 'L2-0', level: 2 } },\n { id: 'level2-1', data: { label: 'L2-1', level: 2 } },\n { id: 'level2-2', data: { label: 'L2-2', level: 2 } },\n { id: 'level2-3', data: { label: 'L2-3', level: 2 } },\n { id: 'level2-4', data: { label: 'L2-4', level: 2 } },\n { id: 'level2-5', data: { label: 'L2-5', level: 2 } },\n { id: 'level2-6', data: { label: 'L2-6', level: 2 } },\n { id: 'level2-7', data: { label: 'L2-7', level: 2 } },\n { id: 'level2-8', data: { label: 'L2-8', level: 2 } },\n { id: 'level2-9', data: { label: 'L2-9', level: 2 } },\n { id: 'level2-10', data: { label: 'L2-10', level: 2 } },\n { id: 'level2-11', data: { label: 'L2-11', level: 2 } },\n ],\n edges: [\n { id: 'e-center-level1-0', source: 'center', target: 'level1-0' },\n { id: 'e-center-level1-1', source: 'center', target: 'level1-1' },\n { id: 'e-center-level1-2', source: 'center', target: 'level1-2' },\n { id: 'e-center-level1-3', source: 'center', target: 'level1-3' },\n { id: 'e-center-level1-4', source: 'center', target: 'level1-4' },\n { id: 'e-center-level1-5', source: 'center', target: 'level1-5' },\n\n { id: 'e-level1-0-level2-0', source: 'level1-0', target: 'level2-0' },\n { id: 'e-level1-0-level2-1', source: 'level1-0', target: 'level2-1' },\n { id: 'e-level1-1-level2-2', source: 'level1-1', target: 'level2-2' },\n { id: 'e-level1-1-level2-3', source: 'level1-1', target: 'level2-3' },\n { id: 'e-level1-2-level2-4', source: 'level1-2', target: 'level2-4' },\n { id: 'e-level1-2-level2-5', source: 'level1-2', target: 'level2-5' },\n { id: 'e-level1-3-level2-6', source: 'level1-3', target: 'level2-6' },\n { id: 'e-level1-3-level2-7', source: 'level1-3', target: 'level2-7' },\n { id: 'e-level1-4-level2-8', source: 'level1-4', target: 'level2-8' },\n { id: 'e-level1-4-level2-9', source: 'level1-4', target: 'level2-9' },\n { id: 'e-level1-5-level2-10', source: 'level1-5', target: 'level2-10' },\n { id: 'e-level1-5-level2-11', source: 'level1-5', target: 'level2-11' },\n ],\n },\n layout: {\n type: 'concentric',\n nodeSize: 32,\n sortBy: 'degree',\n preventOverlap: true,\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n animation: false,\n});\n\ngraph.render();\n```\n\n
\n\n\n\n---\ntitle: Custom Layout\norder: 22\n---\n\n## Overview\n\nIn G6, layouts are divided into two types: 'iterative layout' and 'non-iterative layout'. An iterative layout refers to a layout algorithm that iterates multiple times until convergence, while a non-iterative layout refers to a layout algorithm that executes only once.\n\n## Implement Layout\n\n### Non-Iterative Layout\n\nImplementing a non-iterative layout is relatively straightforward; you only need to implement the `execute` method in `BaseLayout`. Below is a simple implementation of a custom layout:\n\n```typescript\nimport { BaseLayout } from '@antv/g6';\nimport type { GraphData } from '@antv/g6';\n\nclass DiagonalLayout extends BaseLayout {\n id = 'diagonal-layout';\n\n async execute(data: GraphData): Promise {\n const { nodes = [] } = data;\n return {\n nodes: nodes.map((node, index) => ({\n id: node.id,\n style: {\n x: 50 * index + 25,\n y: 50 * index + 25,\n },\n })),\n };\n }\n}\n```\n\nIn the code above, we implemented a simple layout algorithm that arranges nodes along a diagonal line starting from the top-left corner.\n\n\n\n:::info{title=info}\n\nThe `execute` method returns a GraphData object, which only needs to contain the basic information of the elements (such as id, source, target) and the properties added by the layout (such as x, y, control points of edges, etc.), and does not need to contain other unnecessary information.\nIf you only need to layout the nodes, you can return only the node information and do not need to return the edge information.\n:::\n\n### Iterative Layout\n\nThe implementation of an iterative layout also requires inheriting from `BaseLayout`, but in addition to `execute`, you also need to implement the `tick` and `stop` methods. The `tick` method is used to iterate the layout to a specified number of rounds, and the `stop` method is used to stop the layout iteration.\n\nIn addition, in iterative layouts, you need to call `options.onTick` each time the layout iterates to trigger the graph update.\n\n```typescript\ntype onTick = (data: GraphData) => void;\n```\n\nBelow is a simple implementation of an iterative layout:\n\n```typescript\nimport { BaseLayout } from '@antv/g6';\nimport type { GraphData, BaseLayoutOptions } from '@antv/g6';\n\ninterface TickTockLayoutOptions extends BaseLayoutOptions {\n onTick: (data: GraphData) => void;\n}\n\nclass TickTockLayout extends BaseLayout {\n public id = 'custom-layout';\n\n private tickCount = 0;\n\n private data?: GraphData;\n\n private timer?: number;\n\n private resolve?: () => void;\n\n private promise?: Promise;\n\n async execute(data: GraphData, options: TickTockLayoutOptions): Promise {\n const { onTick } = { ...this.options, ...options };\n\n this.tickCount = 0;\n this.data = data;\n\n this.promise = new Promise((resolve) => {\n this.resolve = resolve;\n });\n\n this.timer = window.setInterval(() => {\n onTick(this.simulateTick());\n if (this.tickCount === 10) this.stop();\n }, 200);\n\n await this.promise;\n\n return this.simulateTick();\n }\n\n simulateTick = () => {\n const x = this.tickCount++ % 2 === 0 ? 50 : 150;\n\n return {\n nodes: (this?.data?.nodes || []).map((node, index) => ({\n id: node.id,\n style: { x, y: (index + 1) * 30 },\n })),\n };\n };\n\n tick = () => {\n return this.simulateTick();\n };\n\n stop = () => {\n clearInterval(this.timer);\n this.resolve?.();\n };\n}\n```\n\nIn this example, we have implemented a simple iterative layout algorithm that toggles the x-coordinate of the nodes between 50 and 150 every 200 milliseconds and arranges them in order along the y-axis according to the sequence of the nodes.\n\n\n\n## Register Layout\n\nYou can register through the `register` method provided by G6. For more details, please refer to [Register Layout](/en/manual/core-concept/layout#register-layout)\n\n## Configure Layout\n\nThe type and parameters of the layout can be configured in `options.layout`. For more details, please refer to [Configure Layout](/en/manual/core-concept/layout#configure-layout)\n\n\n\n---\ntitle: 自定义布局\norder: 22\n---\n\n## 概述\n\nG6 中布局分为`迭代布局`和`非迭代布局`两种,迭代布局是指布局算法会迭代多次直到收敛,非迭代布局是指布局算法只执行一次。\n\n## 实现布局\n\n### 非迭代布局\n\n实现一个非迭代布局相对简单,只需要实现 `BaseLayout` 中的 `execute` 方法即可,下面是一个简单的自定义布局实现:\n\n```typescript\nimport { BaseLayout } from '@antv/g6';\nimport type { GraphData } from '@antv/g6';\n\nclass DiagonalLayout extends BaseLayout {\n id = 'diagonal-layout';\n\n async execute(data: GraphData): Promise {\n const { nodes = [] } = data;\n return {\n nodes: nodes.map((node, index) => ({\n id: node.id,\n style: {\n x: 50 * index + 25,\n y: 50 * index + 25,\n },\n })),\n };\n }\n}\n```\n\n在上面的代码中,我们实现了一个简单的布局算法,将节点从左上角沿对角线排列。\n\n\n\n:::info{title=提示}\n`execute` 方法返回的是一个 GraphData 对象,里面仅需要包含元素的基本信息(如 id、source、target)以及布局新增的属性(如 x、y、边的控制点等),不需要包含其他无用的信息。\n如果仅需要对节点进行布局,可以只返回节点信息,不需要返回边的信息。\n:::\n\n### 迭代布局\n\n迭代布局的实现同样需要继承 `BaseLayout`,但是除 `execute` 外还需要实现 `tick` 和 `stop` 方法,`tick` 方法用于将布局迭代到指定轮次,`stop` 方法用于停止布局迭代。\n\n此外,迭代布局中需要在每次迭代调用 `options.onTick` 以触发图的更新。\n\n```typescript\ntype onTick = (data: GraphData) => void;\n```\n\n下面是一个简单的迭代布局实现:\n\n```typescript\nimport { BaseLayout } from '@antv/g6';\nimport type { GraphData, BaseLayoutOptions } from '@antv/g6';\n\ninterface TickTockLayoutOptions extends BaseLayoutOptions {\n onTick: (data: GraphData) => void;\n}\n\nclass TickTockLayout extends BaseLayout {\n public id = 'custom-layout';\n\n private tickCount = 0;\n\n private data?: GraphData;\n\n private timer?: number;\n\n private resolve?: () => void;\n\n private promise?: Promise;\n\n async execute(data: GraphData, options: TickTockLayoutOptions): Promise {\n const { onTick } = { ...this.options, ...options };\n\n this.tickCount = 0;\n this.data = data;\n\n this.promise = new Promise((resolve) => {\n this.resolve = resolve;\n });\n\n this.timer = window.setInterval(() => {\n onTick(this.simulateTick());\n if (this.tickCount === 10) this.stop();\n }, 200);\n\n await this.promise;\n\n return this.simulateTick();\n }\n\n simulateTick = () => {\n const x = this.tickCount++ % 2 === 0 ? 50 : 150;\n\n return {\n nodes: (this?.data?.nodes || []).map((node, index) => ({\n id: node.id,\n style: { x, y: (index + 1) * 30 },\n })),\n };\n };\n\n tick = () => {\n return this.simulateTick();\n };\n\n stop = () => {\n clearInterval(this.timer);\n this.resolve?.();\n };\n}\n```\n\n在这个例子中,我们实现了一个简单的迭代布局算法,每 200ms 将节点的 x 坐标在 50 和 150 之间切换,并按照节点顺序在 y 方向上排列。\n\n\n\n## 注册布局\n\n通过 G6 提供的 register 方法注册即可,详见[注册布局](/manual/layout/overview#注册布局)\n\n## 配置布局\n\n可在 `options.layout` 中配置布局的类型和参数,详见[配置布局](/manual/layout/overview#配置布局)\n\n\n\n---\ntitle: 3D Force-Directed Layout\norder: 7\n---\n\n## Overview\n\nThe D3Force3D layout is a 3D extension based on [d3-force](https://d3js.org/d3-force), which simulates physical forces in three-dimensional space to achieve automatic layout. Compared to 2D layouts, it adds force effects in the Z-axis direction, allowing richer data relationships to be displayed in 3D space.\n\n\"3D\n\n## Core Concepts\n\n### Force System\n\nD3Force3D extends the traditional 2D force-directed layout with the following forces:\n\n- **3D Centering Force**: Pulls nodes toward the center point in 3D space\n- **3D Collision Force**: Prevents node overlap in 3D space\n- **3D Radial Force**: Attracts nodes to a sphere in 3D space\n- **3D Axis Forces**: Applies forces along the X, Y, and Z axes\n\n### Iteration System\n\nThe layout is computed through iterations, mainly involving the following parameters:\n\n- **alpha**: The current energy value of the iteration, controlling node movement speed\n- **alphaDecay**: The decay rate of the energy value\n- **alphaMin**: The minimum energy value; iteration stops below this value\n- **velocityDecay**: The velocity decay factor\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| --------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------- | -------- |\n| type | Layout type | string | `d3-force-3d` | ✓ |\n| nodeSize | Node size (diameter), used for collision detection | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| iterations | Number of force iterations; higher means more precise but slower | number | - | |\n| numDimensions | Number of dimensions (2 or 3) | number | 3 | |\n| forceSimulation | Custom force simulation method | Simulation | - | |\n| onTick | Callback for each iteration | (data: LayoutMapping) => void | - | |\n| randomSource | Random number generator | () => number | - | |\n\n### Iteration Control\n\n| Property | Description | Type | Default | Required |\n| ------------- | ----------------------------- | ------ | ------- | -------- |\n| alpha | Current convergence threshold | number | 1 | |\n| alphaDecay | Convergence decay rate (0-1) | number | 0.028 | |\n| alphaMin | Stop iteration threshold | number | 0.001 | |\n| alphaTarget | Target convergence threshold | number | 0 | |\n| velocityDecay | Velocity decay factor | number | 0.4 | |\n\n### Force Model Options\n\n#### Centering Force (center)\n\n| Property | Description | Type | Default | Required |\n| --------------- | ------------------- | ------ | ------- | -------- |\n| center.x | Center x coordinate | number | 0 | |\n| center.y | Center y coordinate | number | 0 | |\n| center.z | Center z coordinate | number | 0 | |\n| center.strength | Force strength | number | 1 | |\n\n#### Collision Force (collide)\n\n| Property | Description | Type | Default | Required |\n| ------------------ | -------------------- | -------------------------------------------------------------------------- | ------- | -------- |\n| collide.radius | Collision radius | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | 10 | |\n| collide.strength | Force strength | number | 1 | |\n| collide.iterations | Collision iterations | number | 1 | |\n\n#### Link Force (link)\n\n| Property | Description | Type | Default | Required |\n| --------------- | --------------------- | -------------------------------------------------------------------------- | ------- | -------- |\n| link.id | Edge id generator | (edge: EdgeDatum, index: number, edges: EdgeDatum[]) => string | edge.id | |\n| link.distance | Ideal edge length | number \\| ((edge: EdgeDatum, index: number, edges: EdgeDatum[]) => number) | 30 | |\n| link.strength | Force strength | number \\| ((edge: EdgeDatum, index: number, edges: EdgeDatum[]) => number) | 1 | |\n| link.iterations | Link force iterations | number | 1 | |\n\n#### Many-Body Force (manyBody)\n\n| Property | Description | Type | Default | Required |\n| -------------------- | ---------------------------- | -------------------------------------------------------------------------- | -------- | -------- |\n| manyBody.strength | Force strength | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | -30 | |\n| manyBody.theta | Barnes-Hut accuracy | number | 0.9 | |\n| manyBody.distanceMin | Minimum interaction distance | number | 1 | |\n| manyBody.distanceMax | Maximum interaction distance | number | Infinity | |\n\n#### Radial Force (radial)\n\n| Property | Description | Type | Default | Required |\n| --------------- | --------------- | -------------------------------------------------------------------------- | ------- | -------- |\n| radial.strength | Force strength | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | 0.1 | |\n| radial.radius | Target radius | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | 100 | |\n| radial.x | Sphere center x | number | 0 | |\n| radial.y | Sphere center y | number | 0 | |\n| radial.z | Sphere center z | number | 0 | |\n\n#### Axis Forces (x, y, z)\n\nEach axis can be configured separately:\n\n| Property | Description | Type | Default | Required |\n| ---------- | --------------------- | -------------------------------------------------------------------------- | ------- | -------- |\n| x.strength | X-axis force strength | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| x.x | Target x coordinate | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| y.strength | Y-axis force strength | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| y.y | Target y coordinate | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| z.strength | Z-axis force strength | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| z.z | Target z coordinate | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n\n\n\n---\ntitle: 3D D3力导向布局 D3Force3D\norder: 7\n---\n\n## 概述\n\nD3Force3D 布局是基于 [d3-force](https://d3js.org/d3-force) 的三维扩展版本,通过在三维空间中模拟物理力的作用来实现自动布局。相比二维布局,它增加了 Z 轴方向的力作用,能够在三维空间中展现更丰富的数据关系。\n\n\"3D\n\n## 核心概念\n\n### 力系统\n\nD3Force3D 在传统二维力导向布局的基础上,扩展了以下力的作用:\n\n- **三维中心力**:将节点拉向三维空间的中心点\n- **三维碰撞力**:在三维空间中防止节点重叠\n- **三维径向力**:将节点吸引到三维空间中的球面上\n- **三维坐标力**:分别在 X、Y、Z 三个方向上施加作用力\n\n### 迭代系统\n\n布局计算通过迭代来实现,主要涉及以下参数:\n\n- **alpha**:当前迭代的活力值,控制节点移动速度\n- **alphaDecay**:活力值的衰减率\n- **alphaMin**:最小活力值,低于此值停止迭代\n- **velocityDecay**:速度衰减因子\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | -------------------------------------------- | -------------------------------------------------------------------------- | ------------- | ---- |\n| type | 布局类型 | string | `d3-force-3d` | ✓ |\n| nodeSize | 节点大小(直径),用于碰撞检测防止节点重叠 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | 10 | |\n| iterations | 力的迭代次数,值越大布局越精确但性能消耗越大 | number | - | |\n| numDimensions | 维度数量,固定为 3 | number | 3 | |\n| forceSimulation | 自定义力模拟方法 | Simulation | - | |\n| onTick | 每次迭代的回调函数 | (data: LayoutMapping) => void | - | |\n| randomSource | 随机数生成函数 | () => number | - | |\n\n### 迭代控制\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------- | --------------------- | ------ | ------ | ---- |\n| alpha | 当前迭代收敛阈值 | number | 1 | |\n| alphaDecay | 收敛阈值衰减率(0-1) | number | 0.028 | |\n| alphaMin | 停止迭代的阈值 | number | 0.001 | |\n| alphaTarget | 目标收敛阈值 | number | 0 | |\n| velocityDecay | 速度衰减因子 | number | 0.4 | |\n\n### 力模型配置\n\n#### 中心力(center)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | ------------- | ------ | ------ | ---- |\n| center.x | 中心点 x 坐标 | number | 0 | |\n| center.y | 中心点 y 坐标 | number | 0 | |\n| center.z | 中心点 z 坐标 | number | 0 | |\n| center.strength | 力的强度 | number | 1 | |\n\n#### 碰撞力(collide)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------------ | ------------------ | -------------------------------------------------------------------------- | ------ | ---- |\n| collide.radius | 碰撞半径 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | 10 | |\n| collide.strength | 力的强度 | number | 1 | |\n| collide.iterations | 碰撞检测的迭代次数 | number | 1 | |\n\n#### 链接力(link)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | ---------------- | -------------------------------------------------------------------------- | ------- | ---- |\n| link.id | 边的 id 生成函数 | (edge: EdgeDatum, index: number, edges: EdgeDatum[]) => string | edge.id | |\n| link.distance | 理想边长 | number \\| ((edge: EdgeDatum, index: number, edges: EdgeDatum[]) => number) | 30 | |\n| link.strength | 力的强度 | number \\| ((edge: EdgeDatum, index: number, edges: EdgeDatum[]) => number) | 1 | |\n| link.iterations | 链接力的迭代次数 | number | 1 | |\n\n#### 多体力(manyBody)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------------- | ------------------------- | -------------------------------------------------------------------------- | -------- | ---- |\n| manyBody.strength | 力的强度 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | -30 | |\n| manyBody.theta | Barnes-Hut 算法的精度参数 | number | 0.9 | |\n| manyBody.distanceMin | 最小作用距离 | number | 1 | |\n| manyBody.distanceMax | 最大作用距离 | number | Infinity | |\n\n#### 径向力(radial)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | ----------- | -------------------------------------------------------------------------- | ------ | ---- |\n| radial.strength | 力的强度 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | 0.1 | |\n| radial.radius | 目标半径 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | 100 | |\n| radial.x | 球心 x 坐标 | number | 0 | |\n| radial.y | 球心 y 坐标 | number | 0 | |\n| radial.z | 球心 z 坐标 | number | 0 | |\n\n#### 坐标力(x、y、z)\n\n每个方向的力可以单独配置:\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ---------- | ---------------- | -------------------------------------------------------------------------- | ------ | ---- |\n| x.strength | X 轴方向的力强度 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| x.x | 目标 x 坐标 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| y.strength | Y 轴方向的力强度 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| y.y | 目标 y 坐标 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| z.strength | Z 轴方向的力强度 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n| z.z | 目标 z 坐标 | number \\| ((node: NodeDatum, index: number, nodes: NodeDatum[]) => number) | - | |\n\n\n\n---\ntitle: D3 Force-Directed Layout\norder: 8\n---\n\n## Overview\n\nThe D3Force layout is a force-directed layout based on [d3-force](https://d3js.org/d3-force). It simulates physical forces (such as attraction, repulsion, collision, etc.) to make the graph reach a stable state with minimal energy.\n\nThe main features of this layout are:\n\n1. **Automatic arrangement**: No need to manually set node positions, the system will automatically find suitable positions\n2. **Real-time adjustment**: When you drag a node, other nodes will adjust their positions in real time\n3. **Flexible configuration**:\n - You can adjust the attraction and repulsion between nodes\n - You can set the ideal length of edges\n - You can fix the positions of important nodes\n4. **Animation effect**: Nodes move with smooth animation, making changes more natural\n\n\"D3Force\n\n## Core Concepts\n\n### Force System\n\nThe D3Force layout simulates five different forces to achieve automatic layout. Imagine a physical world where these forces act simultaneously and eventually reach equilibrium:\n\n\"force\"\n\n> Note: The arrows of different colors in the diagram represent different types of forces. In the actual layout, these forces are invisible and also affected by other forces.\n\n- **Link Force**: Imagine nodes connected by rubber bands, which pull connected nodes to a suitable distance. The tightness of the rubber band is the force strength, and the ideal length is the distance we set.\n- **Many-Body Force**: Similar to magnets, it allows all nodes to attract or repel each other. When the force strength is negative, nodes repel each other (like like poles of magnets); when positive, they attract (like opposite poles). This force determines the density of the graph.\n- **Center Force**: Like all nodes are tied to the center of the canvas by an invisible string. This force prevents nodes from drifting too far and keeps the graph centered.\n- **Collision Force**: Treats nodes as solid balls. When nodes get too close, they automatically bounce apart. This force mainly prevents node overlap and improves readability.\n- **Radial Force**: Imagine an invisible ring that attracts nodes to the ring. By setting the radius and force strength, nodes can form a beautiful circular layout.\n\n### Iteration System\n\nLayout calculation is an iterative process with two key concepts:\n\n#### Alpha Value (Energy)\n\nLike the \"energy\" of the layout, it determines how vigorously nodes move:\n\n- **Initial state**: Alpha = 1, nodes move vigorously\n- **During calculation**: Alpha gradually decreases, node movement slows\n- **End state**: When Alpha < alphaMin, nodes stop moving\n\n#### Iterations\n\nControls the number of times forces are applied in each calculation:\n\n- **Effect**: The larger the value, the more precise the layout, but the slower the computation\n- **Adjustment**:\n - Simple graphs: use the default value\n - Complex graphs: increase the number of iterations as needed\n - Real-time interaction: use fewer iterations\n\n> Tip: Iterations and alpha value work together. Increasing iterations makes each step more precise, while alpha controls the overall progress.\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| --------------- | -------------------------------------------------- | ------------------------------------------ | ---------- | -------- |\n| type | Layout type | string | 'd3-force' | ✓ |\n| centerX | Layout center x coordinate | number | width / 2 | |\n| centerY | Layout center y coordinate | number | height / 2 | |\n| centerStrength | Center force strength | number | - | |\n| nodeSize | Node size (diameter), for collision detection | number \\| ((node, index, nodes) => number) | 10 | |\n| nodeSpacing | Extra spacing between nodes | number \\| ((node, index, nodes) => number) | 0 | |\n| iterations | Number of force iterations, higher is more precise | number | - | |\n| onTick | Callback for each iteration, for real-time results | (data: LayoutMapping) => void | - | |\n| forceSimulation | Custom force simulation, defaults to d3.js method | Simulation | - | |\n| randomSource | Function to generate random numbers | () => number | - | |\n\n### Iteration Control\n\n| Property | Description | Type | Default | Required |\n| ------------- | ---------------------------------------------------- | ------ | ------- | -------- |\n| alpha | Current convergence threshold, controls activity | number | 1 | |\n| alphaMin | Minimum threshold to stop, when alpha < this, stop | number | 0.001 | |\n| alphaDecay | Decay rate of alpha, [0, 1], 0.028 ≈ 300 iterations | number | 0.028 | |\n| alphaTarget | Target alpha, system tries to converge to this value | number | 0 | |\n| velocityDecay | Velocity decay factor, higher means slower movement | number | 0.4 | |\n\n### Force Model Options\n\n#### Link Force (link)\n\n| Property | Description | Type | Default | Required |\n| --------------- | -------------------------------------------- | ------------------------------------------ | ----------- | -------- |\n| link.id | Function to generate edge id | (edge, index, edges) => string | (e) => e.id | |\n| link.distance | Ideal edge length | number \\| ((edge, index, edges) => number) | 30 | |\n| link.strength | Force strength, higher means closer to ideal | number \\| ((edge, index, edges) => number) | 1 | |\n| link.iterations | Number of link force iterations | number | 1 | |\n\n#### Many-Body Force (manyBody)\n\n| Property | Description | Type | Default | Required |\n| -------------------- | --------------------------------------------------------------- | ------------------------------------------ | -------- | -------- |\n| manyBody.strength | Force strength, negative for repulsion, positive for attraction | number \\| ((node, index, nodes) => number) | -30 | |\n| manyBody.theta | Barnes-Hut accuracy, smaller is more accurate | number | 0.9 | |\n| manyBody.distanceMin | Minimum distance, prevents excessive force | number | 1 | |\n| manyBody.distanceMax | Maximum distance, beyond which no force is applied | number | Infinity | |\n\n#### Center Force (center)\n\n| Property | Description | Type | Default | Required |\n| --------------- | --------------------------------------------- | ------ | ------- | -------- |\n| center.x | Center x coordinate | number | - | |\n| center.y | Center y coordinate | number | - | |\n| center.strength | Force strength, higher means closer to center | number | - | |\n\n> Note: In `5.1`, `centerX` / `centerY` are the recommended shortcut fields. If you configure the `center` force directly, it follows the native `d3-force` style.\n\n#### Collision Force (collide)\n\n| Property | Description | Type | Default | Required |\n| ------------------ | ----------------------------------------------- | ------------------------------------------ | ------- | -------- |\n| collide.radius | Collision radius, nodes repel if closer | number \\| ((node, index, nodes) => number) | 10 | |\n| collide.strength | Force strength, higher means stronger repulsion | number | 1 | |\n| collide.iterations | Number of collision iterations | number | 1 | |\n\n#### Radial Force (radial)\n\n| Property | Description | Type | Default | Required |\n| --------------- | --------------------------------------------- | ------------------------------------------ | ------- | -------- |\n| radial.strength | Force strength, higher means closer to radius | number \\| ((node, index, nodes) => number) | 0.1 | |\n| radial.radius | Target radius, nodes are attracted to circle | number \\| ((node, index, nodes) => number) | 100 | |\n| radial.x | Center x coordinate | number | 0 | |\n| radial.y | Center y coordinate | number | 0 | |\n\n#### X Axis Force (x)\n\n| Property | Description | Type | Default | Required |\n| ---------- | ----------------------------------------- | ------------------------------------------ | ------- | -------- |\n| x.strength | Force strength in x direction | number \\| ((node, index, nodes) => number) | - | |\n| x.x | Target x coordinate, nodes attracted here | number \\| ((node, index, nodes) => number) | - | |\n\n#### Y Axis Force (y)\n\n| Property | Description | Type | Default | Required |\n| ---------- | ----------------------------------------- | ------------------------------------------ | ------- | -------- |\n| y.strength | Force strength in y direction | number \\| ((node, index, nodes) => number) | - | |\n| y.y | Target y coordinate, nodes attracted here | number \\| ((node, index, nodes) => number) | - | |\n\n## Code Examples\n\n### Prevent Node Overlap\n\n```js\n{\n layout: {\n type: 'd3-force',\n collide: {\n // Prevent nodes from overlapping by specifying a collision radius for each node.\n radius: (d) => d.size / 2,\n },\n },\n}\n```\n\nSee [Example - Prevent Node Overlap in Force-Directed Layout](/en/examples/layout/force-directed/#prevent-overlap)\n\n### Team Clustering Layout\n\nThis example shows how to use force-directed layout for team clustering, where nodes of different teams automatically cluster together.\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 500,\n height: 250,\n autoFit: 'view',\n data: {\n nodes: [\n // Team A\n { id: 'A1', team: 'A', label: 'A1', size: 30 },\n { id: 'A2', team: 'A', label: 'A2', size: 20 },\n { id: 'A3', team: 'A', label: 'A3', size: 20 },\n { id: 'A4', team: 'A', label: 'A4', size: 20 },\n // Team B\n { id: 'B1', team: 'B', label: 'B1', size: 30 },\n { id: 'B2', team: 'B', label: 'B2', size: 20 },\n { id: 'B3', team: 'B', label: 'B3', size: 20 },\n { id: 'B4', team: 'B', label: 'B4', size: 20 },\n // Team C\n { id: 'C1', team: 'C', label: 'C1', size: 30 },\n { id: 'C2', team: 'C', label: 'C2', size: 20 },\n { id: 'C3', team: 'C', label: 'C3', size: 20 },\n { id: 'C4', team: 'C', label: 'C4', size: 20 },\n ],\n edges: [\n // Team A internal connections\n { source: 'A1', target: 'A2' },\n { source: 'A1', target: 'A3' },\n { source: 'A1', target: 'A4' },\n // Team B internal connections\n { source: 'B1', target: 'B2' },\n { source: 'B1', target: 'B3' },\n { source: 'B1', target: 'B4' },\n // Team C internal connections\n { source: 'C1', target: 'C2' },\n { source: 'C1', target: 'C3' },\n { source: 'C1', target: 'C4' },\n // Few connections between teams\n { source: 'A1', target: 'B1' },\n { source: 'B1', target: 'C1' },\n ],\n },\n node: {\n style: {\n size: (d) => d.size,\n fill: (d) => {\n // Different colors for different teams\n const colors = {\n A: '#FF6B6B',\n B: '#4ECDC4',\n C: '#45B7D1',\n };\n return colors[d.team];\n },\n labelText: (d) => d.label,\n labelPlacement: 'center',\n labelFill: '#fff',\n },\n },\n edge: {\n style: {\n stroke: '#aaa',\n },\n },\n layout: {\n type: 'd3-force',\n // Configure link force - nodes within the same team are closer\n link: {\n distance: (d) => {\n // Shorter distance within the same team\n if (d.source.team === d.target.team) return 50;\n // Longer distance between teams\n return 200;\n },\n strength: (d) => {\n // Stronger connection within the same team\n if (d.source.team === d.target.team) return 0.7;\n // Weaker connection between teams\n return 0.1;\n },\n },\n // Configure many-body force - control repulsion between nodes\n manyBody: {\n strength: (d) => {\n // Team leader nodes (ending with 1) have stronger repulsion\n if (d.label.endsWith('1')) return -100;\n return -30;\n },\n },\n // Configure collision force - prevent node overlap\n collide: {\n radius: 35,\n strength: 0.8,\n },\n // Configure center force - keep the graph centered\n center: {\n strength: 0.05,\n },\n },\n behaviors: ['drag-element-force'],\n});\n\ngraph.render();\n```\n\n
Show full code\n\n```javascript\nimport { Graph } from '@antv/g6';\n\n// Create mock data with nodes from different teams\nconst data = {\n nodes: [\n // Team A\n { id: 'A1', team: 'A', label: 'A1', size: 30 },\n { id: 'A2', team: 'A', label: 'A2', size: 20 },\n { id: 'A3', team: 'A', label: 'A3', size: 20 },\n { id: 'A4', team: 'A', label: 'A4', size: 20 },\n // Team B\n { id: 'B1', team: 'B', label: 'B1', size: 30 },\n { id: 'B2', team: 'B', label: 'B2', size: 20 },\n { id: 'B3', team: 'B', label: 'B3', size: 20 },\n { id: 'B4', team: 'B', label: 'B4', size: 20 },\n // Team C\n { id: 'C1', team: 'C', label: 'C1', size: 30 },\n { id: 'C2', team: 'C', label: 'C2', size: 20 },\n { id: 'C3', team: 'C', label: 'C3', size: 20 },\n { id: 'C4', team: 'C', label: 'C4', size: 20 },\n ],\n edges: [\n // Team A internal connections\n { source: 'A1', target: 'A2' },\n { source: 'A1', target: 'A3' },\n { source: 'A1', target: 'A4' },\n // Team B internal connections\n { source: 'B1', target: 'B2' },\n { source: 'B1', target: 'B3' },\n { source: 'B1', target: 'B4' },\n // Team C internal connections\n { source: 'C1', target: 'C2' },\n { source: 'C1', target: 'C3' },\n { source: 'C1', target: 'C4' },\n // Few connections between teams\n { source: 'A1', target: 'B1' },\n { source: 'B1', target: 'C1' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: {\n size: (d) => d.size,\n fill: (d) => {\n // Different colors for different teams\n const colors = {\n A: '#FF6B6B',\n B: '#4ECDC4',\n C: '#45B7D1',\n };\n return colors[d.team];\n },\n labelText: (d) => d.label,\n labelPlacement: 'center',\n labelFill: '#fff',\n },\n },\n edge: {\n style: {\n stroke: '#aaa',\n },\n },\n layout: {\n type: 'd3-force',\n // Configure link force - nodes within the same team are closer\n link: {\n distance: (d) => {\n // Shorter distance within the same team\n if (d.source.team === d.target.team) return 50;\n // Longer distance between teams\n return 200;\n },\n strength: (d) => {\n // Stronger connection within the same team\n if (d.source.team === d.target.team) return 0.7;\n // Weaker connection between teams\n return 0.1;\n },\n },\n // Configure many-body force - control repulsion between nodes\n manyBody: {\n strength: (d) => {\n // Team leader nodes (ending with 1) have stronger repulsion\n if (d.label.endsWith('1')) return -100;\n return -30;\n },\n },\n // Configure collision force - prevent node overlap\n collide: {\n radius: 35,\n strength: 0.8,\n },\n // Configure center force - keep the graph centered\n center: {\n strength: 0.05,\n },\n },\n behaviors: ['drag-element-force'],\n});\n\ngraph.render();\n```\n\n
\n\nMain configuration notes:\n\n- `link.distance`: Shorter within teams, longer between teams\n- `link.strength`: Stronger within teams, weaker between teams\n- `manyBody.strength`: Controls repulsion between nodes\n- `collide`: Prevents node overlap\n- `center`: Keeps the layout centered\n\nSee also [Customize parameters for different nodes](/en/examples/layout/force-directed/#functional-params).\n\n\n\n---\ntitle: D3力导向布局 D3Force\norder: 8\n---\n\n## 概述\n\nD3Force 布局是基于 [d3-force](https://d3js.org/d3-force) 实现的力导向布局。它通过模拟物理力的作用(如引力、斥力、碰撞等),使图布局达到一个能量最小的稳定状态。\n\n这种布局的主要特点是:\n\n1. **自动排列**:不需要手动设置节点位置,系统会自动找到合适的位置\n2. **实时调整**:当你拖动某个节点时,其他节点会实时跟随调整位置\n3. **灵活配置**:\n - 可以调整节点间的吸引力和排斥力\n - 可以设置边的理想长度\n - 可以固定某些重要节点的位置\n4. **动画效果**:节点移动时会有平滑的动画,让变化更自然\n\n\"D3Force\n\n## 核心概念\n\n### 力系统 Force System\n\nD3Force 布局通过模拟五种不同的力来实现自动布局。想象一个物理世界,这些力同时作用,最终达到平衡:\n\n\"force\"\n\n> 注:图中不同颜色的箭头代表不同类型的力,实际布局中这些力是无形的,同时也会受其他力影响。\n\n- **链接力**(Link Force):想象节点之间连着橡皮筋,可以把相连的节点拉到合适的距离。橡皮筋的松紧度就是力的强度(strength),理想长度就是我们设置的距离(distance)。\n- **多体力**(Many-Body Force):类似磁铁的效果,可以让所有节点互相吸引或排斥。力的强度为负值时节点会互相排斥(像相同磁极),为正值时会互相吸引(像相反磁极)。这个力决定了图的疏密程度。\n- **中心力**(Center Force):就像所有节点都被一根看不见的绳子拴在画布中心。这个力可以防止节点飘得太远,让整个图保持在画布的中心位置。\n- **碰撞力**(Collision Force):让节点变成有实体大小的小球,当节点太近时会自动弹开。这个力主要用来防止节点重叠,提高图的可读性。\n- **径向力**(Radial Force):想象有一个看不见的圆环,这个力会把节点吸引到圆环上。通过设置圆的半径和力的强度,可以让节点形成漂亮的环形布局。\n\n### 迭代系统(Iteration System)\n\n布局计算是一个反复调整的过程,包含两个关键概念:\n\n#### Alpha 值(活力值)\n\n就像布局的\"能量\",决定节点移动的剧烈程度:\n\n- **初始状态**:Alpha = 1,节点移动剧烈\n- **计算过程**:Alpha 值逐渐降低,节点移动变缓\n- **结束状态**:当 Alpha < alphaMin 时,节点停止移动\n\n#### 迭代次数(Iterations)\n\n控制每次计算时力的作用次数:\n\n- **作用**:值越大,布局越精确,但计算越慢\n- **调节**:\n - 简单图:使用默认值即可\n - 复杂图:可以适当增加迭代次数\n - 实时交互:建议使用较小的迭代次数\n\n> 提示:迭代次数(iterations)和活力值(alpha)是相互配合的。增加迭代次数可以让每一步计算更精确,而活力值则控制整体计算的进度。\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | --------------------------------------------- | ------------------------------------------ | ---------- | ---- |\n| type | 布局类型 | string | 'd3-force' | ✓ |\n| centerX | 布局中心点 x 坐标 | number | width / 2 | |\n| centerY | 布局中心点 y 坐标 | number | height / 2 | |\n| centerStrength | 中心力强度 | number | - | |\n| nodeSize | 节点大小(直径),用于碰撞检测防止节点重叠 | number \\| ((node, index, nodes) => number) | 10 | |\n| nodeSpacing | 节点间额外间距 | number \\| ((node, index, nodes) => number) | 0 | |\n| iterations | 力的迭代次数,值越大布局越精确但性能消耗越大 | number | - | |\n| onTick | 每次迭代的回调函数,用于实时获取布局结果 | (data: LayoutMapping) => void | - | |\n| forceSimulation | 自定义力模拟方法,若不指定则使用 d3.js 的方法 | Simulation | - | |\n| randomSource | 用于生成随机数的函数 | () => number | - | |\n\n### 迭代控制\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------- | ------------------------------------------------------ | ------ | ------ | ---- |\n| alpha | 当前迭代的收敛阈值,控制布局的活跃程度 | number | 1 | |\n| alphaMin | 停止迭代的最小阈值,当 alpha 小于该值时停止迭代 | number | 0.001 | |\n| alphaDecay | 收敛阈值的衰减率,范围 [0, 1],0.028 对应约 300 次迭代 | number | 0.028 | |\n| alphaTarget | 目标收敛阈值,系统会尝试将 alpha 收敛到该值 | number | 0 | |\n| velocityDecay | 速度衰减因子,值越大节点运动越缓慢 | number | 0.4 | |\n\n### 力模型配置\n\n#### 链接力(link)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | ---------------------------------- | ------------------------------------------ | ----------- | ---- |\n| link.id | 边的 id 生成函数 | (edge, index, edges) => string | (e) => e.id | |\n| link.distance | 理想边长,边会趋向于该长度 | number \\| ((edge, index, edges) => number) | 30 | |\n| link.strength | 力的强度,值越大边长越接近理想边长 | number \\| ((edge, index, edges) => number) | 1 | |\n| link.iterations | 链接力的迭代次数 | number | 1 | |\n\n#### 多体力(manyBody)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------------- | ----------------------------------------------------- | ------------------------------------------ | -------- | ---- |\n| manyBody.strength | 力的强度,负值为斥力,正值为引力 | number \\| ((node, index, nodes) => number) | -30 | |\n| manyBody.theta | Barnes-Hut 算法的精度参数,值越小越精确但性能消耗越大 | number | 0.9 | |\n| manyBody.distanceMin | 最小作用距离,防止力过大 | number | 1 | |\n| manyBody.distanceMax | 最大作用距离,超过该距离的节点不产生力 | number | Infinity | |\n\n#### 中心力(center)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | ---------------------------------- | ------ | ------ | ---- |\n| center.x | 中心点 x 坐标 | number | - | |\n| center.y | 中心点 y 坐标 | number | - | |\n| center.strength | 力的强度,值越大节点越趋向于中心点 | number | - | |\n\n> 补充:5.1 中推荐优先使用 `centerX` / `centerY` 作为快捷配置;如果直接配置 `center` force,则按 d3-force 原生写法生效。\n\n#### 碰撞力(collide)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------------ | -------------------------------------- | ------------------------------------------ | ------ | ---- |\n| collide.radius | 碰撞半径,小于该距离的节点会产生排斥力 | number \\| ((node, index, nodes) => number) | 10 | |\n| collide.strength | 力的强度,值越大排斥效果越明显 | number | 1 | |\n| collide.iterations | 碰撞检测的迭代次数 | number | 1 | |\n\n#### 径向力(radial)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | -------------------------------------- | ------------------------------------------ | ------ | ---- |\n| radial.strength | 力的强度,值越大节点越趋向于目标半径 | number \\| ((node, index, nodes) => number) | 0.1 | |\n| radial.radius | 目标半径,节点会被吸引到该半径的圆周上 | number \\| ((node, index, nodes) => number) | 100 | |\n| radial.x | 圆心 x 坐标 | number | 0 | |\n| radial.y | 圆心 y 坐标 | number | 0 | |\n\n#### X 轴力(x)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ---------- | ----------------------------------- | ------------------------------------------ | ------ | ---- |\n| x.strength | X 轴方向的力强度 | number \\| ((node, index, nodes) => number) | - | |\n| x.x | 目标 x 坐标,节点会被吸引到这个位置 | number \\| ((node, index, nodes) => number) | - | |\n\n#### Y 轴力(y)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ---------- | ----------------------------------- | ------------------------------------------ | ------ | ---- |\n| y.strength | Y 轴方向的力强度 | number \\| ((node, index, nodes) => number) | - | |\n| y.y | 目标 y 坐标,节点会被吸引到这个位置 | number \\| ((node, index, nodes) => number) | - | |\n\n## 代码示例\n\n### 防止节点重叠\n\n```js\n{\n layout: {\n type: 'd3-force',\n collide: {\n // Prevent nodes from overlapping by specifying a collision radius for each node.\n radius: (d) => d.size / 2,\n },\n },\n}\n```\n\n效果见 [示例 - 力导向布局防止节点重叠](/examples/layout/force-directed/#prevent-overlap)\n\n### 团队聚类布局\n\n该示例展示了如何使用力导向布局实现团队聚类效果,不同团队的节点会自动聚集在一起。\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 500,\n height: 250,\n autoFit: 'view',\n data: {\n nodes: [\n // 团队 A\n { id: 'A1', team: 'A', label: 'A1', size: 30 },\n { id: 'A2', team: 'A', label: 'A2', size: 20 },\n { id: 'A3', team: 'A', label: 'A3', size: 20 },\n { id: 'A4', team: 'A', label: 'A4', size: 20 },\n // 团队 B\n { id: 'B1', team: 'B', label: 'B1', size: 30 },\n { id: 'B2', team: 'B', label: 'B2', size: 20 },\n { id: 'B3', team: 'B', label: 'B3', size: 20 },\n { id: 'B4', team: 'B', label: 'B4', size: 20 },\n // 团队 C\n { id: 'C1', team: 'C', label: 'C1', size: 30 },\n { id: 'C2', team: 'C', label: 'C2', size: 20 },\n { id: 'C3', team: 'C', label: 'C3', size: 20 },\n { id: 'C4', team: 'C', label: 'C4', size: 20 },\n ],\n edges: [\n // 团队 A 内部连接\n { source: 'A1', target: 'A2' },\n { source: 'A1', target: 'A3' },\n { source: 'A1', target: 'A4' },\n // 团队 B 内部连接\n { source: 'B1', target: 'B2' },\n { source: 'B1', target: 'B3' },\n { source: 'B1', target: 'B4' },\n // 团队 C 内部连接\n { source: 'C1', target: 'C2' },\n { source: 'C1', target: 'C3' },\n { source: 'C1', target: 'C4' },\n // 团队间的少量连接\n { source: 'A1', target: 'B1' },\n { source: 'B1', target: 'C1' },\n ],\n },\n node: {\n style: {\n size: (d) => d.size,\n fill: (d) => {\n // 不同团队使用不同颜色\n const colors = {\n A: '#FF6B6B',\n B: '#4ECDC4',\n C: '#45B7D1',\n };\n return colors[d.team];\n },\n labelText: (d) => d.label,\n labelPlacement: 'center',\n labelFill: '#fff',\n },\n },\n edge: {\n style: {\n stroke: '#aaa',\n },\n },\n layout: {\n type: 'd3-force',\n // 配置链接力 - 团队内部节点更靠近\n link: {\n distance: (d) => {\n // 同一团队内的连接距离更短\n if (d.source.team === d.target.team) return 50;\n // 不同团队间的连接距离更长\n return 200;\n },\n strength: (d) => {\n // 同一团队内的连接强度更大\n if (d.source.team === d.target.team) return 0.7;\n // 不同团队间的连接强度更小\n return 0.1;\n },\n },\n // 配置多体力 - 控制节点间的排斥力\n manyBody: {\n strength: (d) => {\n // 团队领导节点(编号1)的排斥力更强\n if (d.label.endsWith('1')) return -100;\n return -30;\n },\n },\n // 配置碰撞力 - 防止节点重叠\n collide: {\n radius: 35,\n strength: 0.8,\n },\n // 配置中心力 - 保持图形在画布中心\n center: {\n strength: 0.05,\n },\n },\n behaviors: ['drag-element-force'],\n});\n\ngraph.render();\n```\n\n
展开查看完整代码\n\n```javascript\nimport { Graph } from '@antv/g6';\n\n// 创建模拟数据,包含不同团队的节点\nconst data = {\n nodes: [\n // 团队 A\n { id: 'A1', team: 'A', label: 'A1', size: 30 },\n { id: 'A2', team: 'A', label: 'A2', size: 20 },\n { id: 'A3', team: 'A', label: 'A3', size: 20 },\n { id: 'A4', team: 'A', label: 'A4', size: 20 },\n // 团队 B\n { id: 'B1', team: 'B', label: 'B1', size: 30 },\n { id: 'B2', team: 'B', label: 'B2', size: 20 },\n { id: 'B3', team: 'B', label: 'B3', size: 20 },\n { id: 'B4', team: 'B', label: 'B4', size: 20 },\n // 团队 C\n { id: 'C1', team: 'C', label: 'C1', size: 30 },\n { id: 'C2', team: 'C', label: 'C2', size: 20 },\n { id: 'C3', team: 'C', label: 'C3', size: 20 },\n { id: 'C4', team: 'C', label: 'C4', size: 20 },\n ],\n edges: [\n // 团队 A 内部连接\n { source: 'A1', target: 'A2' },\n { source: 'A1', target: 'A3' },\n { source: 'A1', target: 'A4' },\n // 团队 B 内部连接\n { source: 'B1', target: 'B2' },\n { source: 'B1', target: 'B3' },\n { source: 'B1', target: 'B4' },\n // 团队 C 内部连接\n { source: 'C1', target: 'C2' },\n { source: 'C1', target: 'C3' },\n { source: 'C1', target: 'C4' },\n // 团队间的少量连接\n { source: 'A1', target: 'B1' },\n { source: 'B1', target: 'C1' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: {\n size: (d) => d.size,\n fill: (d) => {\n // 不同团队使用不同颜色\n const colors = {\n A: '#FF6B6B',\n B: '#4ECDC4',\n C: '#45B7D1',\n };\n return colors[d.team];\n },\n labelText: (d) => d.label,\n labelPlacement: 'center',\n labelFill: '#fff',\n },\n },\n edge: {\n style: {\n stroke: '#aaa',\n },\n },\n layout: {\n type: 'd3-force',\n // 配置链接力 - 团队内部节点更靠近\n link: {\n distance: (d) => {\n // 同一团队内的连接距离更短\n if (d.source.team === d.target.team) return 50;\n // 不同团队间的连接距离更长\n return 200;\n },\n strength: (d) => {\n // 同一团队内的连接强度更大\n if (d.source.team === d.target.team) return 0.7;\n // 不同团队间的连接强度更小\n return 0.1;\n },\n },\n // 配置多体力 - 控制节点间的排斥力\n manyBody: {\n strength: (d) => {\n // 团队领导节点(编号1)的排斥力更强\n if (d.label.endsWith('1')) return -100;\n return -30;\n },\n },\n // 配置碰撞力 - 防止节点重叠\n collide: {\n radius: 35,\n strength: 0.8,\n },\n // 配置中心力 - 保持图形在画布中心\n center: {\n strength: 0.05,\n },\n },\n behaviors: ['drag-element-force'],\n});\n\ngraph.render();\n```\n\n
\n\n主要配置说明:\n\n- `link.distance`:团队内部距离短,团队间距离长\n- `link.strength`:团队内部连接强度大,团队间连接强度小\n- `manyBody.strength`:控制节点间排斥力\n- `collide`:防止节点重叠\n- `center`:保持整体布局在画布中心\n\n还可以参考 [定制不同节点的参数](/examples/layout/force-directed/#functional-params) 示例。\n\n\n\n---\ntitle: Dagre Layout\norder: 9\n---\n\n# Dagre Layout\n\n## Overview\n\nDagre is a hierarchical layout suitable for directed acyclic graphs (DAGs). It can automatically handle the direction and spacing between nodes and supports both horizontal and vertical layouts. See more Dagre layout [examples](/en/examples#layout-dagre), [source code](https://github.com/dagrejs/dagre/blob/master/lib/layout.js), and [official documentation](https://github.com/dagrejs/dagre/wiki).\n\nDagre Layout\n\n## Configuration\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'dagre',\n rankdir: 'TB',\n align: 'UL',\n nodesep: 50,\n ranksep: 50,\n },\n});\n```\n\n## Options\n\n> For more options, refer to the [official documentation](https://github.com/dagrejs/dagre/wiki#configuring-the-layout)\n\n\"Dagre\n\n| Property | Description | Type | Default | Required |\n| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------- | ----------------- | -------- |\n| type | Layout type | `dagre` | - | ✓ |\n| rankdir | Layout direction, options | `TB` \\| `BT` \\| `LR` \\| `RL` | `TB` | |\n| align | Node alignment, options | `UL` \\| `UR` \\| `DL` \\| `DR` | `UL` | |\n| nodesep | Node spacing (px). For `TB` or `BT`, it is the horizontal spacing; for `LR` or `RL`, it is the vertical spacing | number | 50 | |\n| ranksep | Rank spacing (px). For `TB` or `BT`, it is the vertical spacing between adjacent ranks; for `LR` or `RL`, it is the horizontal spacing | number | 100 | |\n| ranker | Algorithm for assigning ranks to nodes: `longest-path`, `tight-tree`, or `network-simplex` | `network-simplex` \\| `tight-tree` \\| `longest-path` | `network-simplex` | |\n| directed | Whether to treat the graph as directed | boolean | true | |\n| compound | Whether to support nested structures | boolean | true | |\n| multigraph | Whether to allow multi-edges | boolean | true | |\n| nodeSize | G6 custom property, specify node size for all or each node. If a single number, width and height are the same; if array: `[width, height]` | number \\| number[] \\| () => (number \\| number[]) | [0, 0] | |\n| edgeMinLen | Minimum number of ranks crossed by an edge | number \\| (edge) => number | 1 | |\n| edgeWeight | Edge weight, used to affect optimization priority | number \\| (edge) => number | | |\n| edgeLabelSize | Edge label size, used to reserve layout space | number[] \\| (edge) => number[] | | |\n| edgeLabelPos | Edge label position | string \\| (edge) => string | | |\n| edgeLabelOffset | Offset between the label and the edge | number \\| (edge) => number | | |\n\n> Note: `dagre` does not require configuring `controlPoints` separately. G6 automatically converts the polyline points returned by the layout into `style.controlPoints` on the edge.\n\n### rankdir\n\n> `TB` | `BT` | `LR` | `RL`, **Default**: `TB`\n\nLayout direction\n\n- `TB`: Top to Bottom;\n\nTop to Bottom Layout\n\n- `BT`: Bottom to Top;\n\nBottom to Top Layout\n\n- `LR`: Left to Right;\n\nLeft to Right Layout\n\n- `RL`: Right to Left.\n\nRight to Left Layout\n\n### align\n\n> `UL` | `UR` | `DL` | `DR`, **Default**: `UL`\n\nNode alignment\n\n- `UL`: Upper Left\n- `UR`: Upper Right\n- `DL`: Down Left\n- `DR`: Down Right\n\n### nodesep\n\n> number, **Default**: 50\n\nNode spacing (px). For `TB` or `BT`, it's the horizontal spacing; for `LR` or `RL`, it's the vertical spacing\n\n### ranksep\n\n> number, **Default**: 50\n\nRank spacing (px). For `TB` or `BT`, it's the vertical spacing between ranks; for `LR` or `RL`, it's the horizontal spacing between ranks\n\n### ranker\n\n> `network-simplex` | `tight-tree` | `longest-path`, **Default**: `network-simplex`\n\nAlgorithm for assigning ranks to nodes, supports three algorithms:\n\n- `longest-path`: Uses DFS to recursively find the longest path for each node. Simple and fast, but may result in many long edges.\n- `tight-tree`: An optimization algorithm to reduce the number of long edges. It first uses `longest-path` to compute initial ranks, then adjusts slack edges to build a feasible tree.\n- `network-simplex`: Based on [A Technique for Drawing Directed Graphs](https://www.graphviz.org/documentation/TSE93.pdf), iteratively modifies node ranks to minimize slack edges.\n\n### nodeSize\n\n> number \\| number[] \\| () => (number \\| number[])\n\nG6 custom property, specify node size for all or each node. If a single number, width and height are the same; if array: `[width, height]`\n\n```js\n(d) => {\n // d is a node\n if (d.id === 'testId') return 20;\n return [10, 20];\n};\n```\n\n## Applicable Scenarios\n\n- **Flowcharts**: Suitable for displaying flowcharts, automatically handling direction and spacing between nodes.\n- **Dependency Graphs**: Display dependencies between packages or modules.\n- **Task Scheduling Graphs**: Show dependencies and execution order between tasks.\n\n## Related Documentation\n\n> The following documents can help you better understand Dagre layout\n\n- [Graph Layout Algorithms|Detailed Dagre Layout](https://mp.weixin.qq.com/s/EdyTfFUH7fyMefNSBXI2nA)\n- [In-depth Interpretation of Dagre Layout Algorithm](https://www.yuque.com/antv/g6-blog/xxp5nl)\n\n\n\n---\ntitle: Dagre 布局\norder: 9\n---\n\n## 概述\n\nDagre 是一种层次化布局,适用于有向无环图(DAG)的布局场景,能够自动处理节点之间的方向和间距,支持水平和垂直布局。参考更多 Dagre 布局[样例](/examples#layout-dagre)或[源码](https://github.com/dagrejs/dagre/blob/master/lib/layout.js)以及[官方文档](https://github.com/dagrejs/dagre/wiki)。\n\nDagre布局\n\n## 配置方式\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'dagre',\n rankdir: 'TB',\n align: 'UL',\n nodesep: 50,\n ranksep: 50,\n },\n});\n```\n\n## 配置项\n\n> 更多配置项可参考[官方文档](https://github.com/dagrejs/dagre/wiki#configuring-the-layout)\n\n\"Dagre\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ----------------- | ---- |\n| type | 布局类型 | `dagre` | - | ✓ |\n| rankdir | 布局方向,可选值 | `TB` \\| `BT` \\| `LR` \\| `RL` | `TB` | |\n| align | 节点对齐方式,可选值 | `UL` \\| `UR` \\| `DL` \\| `DR` | `UL` | |\n| nodesep | 节点间距(px)。在 rankdir 为 `TB` 或 `BT` 时是节点的水平间距;在 rankdir 为 `LR` 或 `RL` 时代表节点的竖直方向间距 | number | 50 | |\n| ranksep | 层间距(px)。在 rankdir 为 `TB` 或 `BT` 时是竖直方向相邻层间距;在 rankdir 为 `LR` 或 `RL` 时代表水平方向相邻层间距 | number | 100 | |\n| ranker | 为每个节点分配等级的算法,共支持三种算法,分别是:`longest-path`、`tight-tree`、`network-simplex` | `network-simplex` \\| `tight-tree` \\| `longest-path` | `network-simplex` | |\n| directed | 是否按有向图处理 | boolean | true | |\n| compound | 是否支持嵌套结构 | boolean | true | |\n| multigraph | 是否允许多重边 | boolean | true | |\n| nodeSize | G6 自定义属性,统一指定或为每个节点指定节点大小。如果仅返回单个 number,则表示节点的宽度和高度相同;如果返回一个数组,则形如:`[width, height]` | number \\| number[] \\| () => (number \\| number[]) | [0, 0] | |\n| edgeMinLen | 边跨越的最小层数 | number \\| (edge) => number | 1 | |\n| edgeWeight | 边权重,用于影响优化优先级 | number \\| (edge) => number | | |\n| edgeLabelSize | 边标签尺寸,用于预留空间 | number[] \\| (edge) => number[] | | |\n| edgeLabelPos | 边标签位置 | string \\| (edge) => string | | |\n| edgeLabelOffset | 标签与边的偏移 | number \\| (edge) => number | | |\n\n> 补充:`dagre` 不需要单独配置 `controlPoints`,G6 会把布局输出的折线点自动转换为边的 `style.controlPoints`。\n\n### rankdir\n\n> `TB` | `BT` | `LR` | `RL`, **Default**: `TB`\n\n布局方向\n\n- `TB`:从上到下;\n\n:从上到下布局\n\n- `BT`:从下到上;\n\n从下到上布局\n\n- `LR`:从左到右;\n\n从左到右布局\n\n- `RL`:从右到左。\n\n水平布局\n\n### align\n\n> `UL` | `UR` | `DL` | `DR`, **Default**: `UL`\n\n节点对齐方式\n\n- `UL`:左上对齐\n- `UR`:右上对齐\n- `DL`:左下对齐\n- `DR`:右下对齐\n\n### nodesep\n\n> number, **Default**: 50\n\n节点间距(px)。在rankdir 为 `TB` 或 `BT` 时是节点的水平间距;在rankdir 为 `LR` 或 `RL` 时代表节点的竖直方向间距\n\n### ranksep\n\n> number, **Default**: 50\n\n层间距(px)。在rankdir 为 `TB` 或 `BT` 时是竖直方向相邻层间距;在rankdir 为 `LR` 或 `RL` 时代表水平方向相邻层间距\n\n### ranker\n\n> `network-simplex` | `tight-tree` | `longest-path`, **Default**: `network-simplex`\n\n为每个节点分配层级的算法,共支持三种算法,分别是:\n\n- `longest-path`: 最长路径算法,使用DFS深度优先搜索,递归查找每个节点的最长路径。优点是计算简单速度快,但会导致长边过多;\n- `tight-tree`: 紧凑树算法,一种优化算法,目的是减少长边的数量。先用最长路径算法`longest-path`计算出初始层级,然后调整松弛边的长度,从而构建可行树。\n- `network-simplex`: 网络单形法,参考算法[A Technique for Drawing Directed Graphs](https://www.graphviz.org/documentation/TSE93.pdf),核心思想是迭代修改节点的层级,缩小松弛边。\n\n### nodeSize\n\n> number \\| number[] \\| () => (number \\| number[])\n\nG6自定义属性,统一指定或为每个节点指定节点大小。如果仅返回单个number,则表示节点的宽度和高度相同;如果返回一个数组,则形如:`[width, height]`\n\n```js\n(d) => {\n // d 是一个节点\n if (d.id === 'testId') return 20;\n return [10, 20];\n};\n```\n\n### controlPoints\n\n> boolean, **Default**: false\n\n是否保留边的控制点。\n\n## 布局适用场景\n\n- **流程图**:适合展示流程图,节点之间的方向和间距会自动处理;\n- **依赖关系图**:展示软件包或模块之间的依赖关系;\n- **任务调度图**:展示任务之间的依赖关系和执行顺序。\n\n## 相关文档\n\n> 以下文档可以帮助你更好地理解Dagre 布局\n\n- [图布局算法|详解 Dagre 布局](https://mp.weixin.qq.com/s/EdyTfFUH7fyMefNSBXI2nA)\n- [深入解读Dagre布局算法](https://www.yuque.com/antv/g6-blog/xxp5nl)\n\n\n\n---\ntitle: Dendrogram Layout\norder: 10\n---\n\n## Overview\n\nThe dendrogram layout is suitable for visualizing hierarchical clustering data. Its feature is that all child nodes are laid out on the same level, node size is not considered, and each node is treated as 1px.\n\nDendrogram Layout Example\n\n## Configuration\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'dendrogram',\n direction: 'LR',\n nodeSep: 30,\n rankSep: 250,\n radial: false,\n },\n});\n```\n\n## Options\n\n\"Dendrogram\n\n| Property | Description | Type | Default | Required |\n| --------- | ------------------------------------------------------ | ------------------------------------------ | ------- | -------- |\n| type | Layout type | `dendrogram` | - | ✓ |\n| direction | Layout direction, [options](#direction) | `LR` \\| `RL` \\| `TB` \\| `BT` \\| `H` \\| `V` | `LR` | |\n| nodeSep | Node spacing, distance between nodes on the same level | number | 20 | |\n| rankSep | Rank spacing, distance between different levels | number | 200 | |\n| radial | Whether to enable radial layout, [see below](#radial) | boolean | false | |\n\n### direction\n\nTree layout direction options:\n\n- `TB`: Root at the top, layout downward\n\nTB direction\n\n- `BT`: Root at the bottom, layout upward\n\nBT direction\n\n- `LR`: Root at the left, layout to the right\n\nLR direction\n\n- `RL`: Root at the right, layout to the left\n\nRL direction\n\n- `H`: Root in the middle, horizontal symmetric layout\n\nH direction\n\n- `V`: Root in the middle, vertical symmetric layout\n\nV direction\n\n### radial\n\nWhether to enable radial layout mode. When enabled, nodes are distributed radially around the root node.\n\nIf `radial` is set to `true`, it is recommended to set `direction` to `'LR'` or `'RL'` for best results.\n\nRadial Layout\n\n\n\n---\ntitle: 生态树 Dendrogram\norder: 10\n---\n\n## 概述\n\n生态树布局适用于层次聚类数据的可视化,其特点是所有子节点布局在同一层级,不考虑节点大小,每个节点被当成 1px 处理。\n\n生态树布局示例\n\n## 配置方式\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'dendrogram',\n direction: 'LR',\n nodeSep: 30,\n rankSep: 250,\n radial: false,\n },\n});\n```\n\n## 配置项\n\n\"生态树配置项图解\"\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------- | ---------------------------------------------- | ------------------------------------------ | ------ | ---- |\n| type | 布局类型 | `dendrogram` | - | ✓ |\n| direction | 布局方向,[可选值](#direction) | `LR` \\| `RL` \\| `TB` \\| `BT` \\| `H` \\| `V` | `LR` | |\n| nodeSep | 节点间距,即同一层级节点之间的距离,单位为像素 | number | 20 | |\n| rankSep | 层级间距,即不同层级之间的距离,单位为像素 | number | 200 | |\n| radial | 是否启用辐射状布局,[说明](#radial) | boolean | false | |\n\n### direction\n\n树布局的方向,有以下选项:\n\n- `TB`:根节点在上,往下布局\n\nTB方向\n\n- `BT`:根节点在下,往上布局\n\nBT方向\n\n- `LR`:根节点在左,往右布局\n\nLR方向\n\n- `RL`:根节点在右,往左布局\n\nRL方向\n\n- `H`:根节点在中间,水平对称布局\n\nH方向\n\n- `V`:根节点在中间,垂直对称布局\n\nV方向\n\n### radial\n\n是否启用辐射状布局模式。启用后,节点将以根节点为中心呈辐射状分布。\n\n若 `radial` 设置为 `true`,建议将 `direction` 设置为 `'LR'` 或 `'RL'` 以获得最佳效果。\n\n辐射状布局\n\n\n\n---\ntitle: Fishbone Layout\norder: 11\n---\n\n## Overview\n\nFishbone layout is a graphical layout specifically designed for representing hierarchical data. By simulating the shape of a fishbone, it arranges data nodes according to their hierarchy, making the hierarchical relationships of the data clearer and more intuitive. Fishbone layout is especially suitable for datasets that need to show causality, hierarchy, or classification information.\n\n## Use Cases\n\n- Displaying hierarchical data, such as organizational structures or classification systems\n- Showing problem analysis processes, such as fault analysis or quality analysis\n- Displaying decision processes, such as decision trees or factor analysis\n\n## Online Demo\n\n\n\n## Basic Usage\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'fishbone',\n direction: 'LR',\n hGap: 50,\n vGap: 50,\n getRibSep: () => 60,\n },\n});\n```\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| ---------------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------- |\n| type | Layout type | `fishbone` | - | ✓ |\n| direction | Layout direction, `RL` (right to left, head on right), `LR` (left to right, head on left) | `RL` \\| `LR` | `RL` | |\n| hGap | Horizontal gap | number | - | |\n| vGap | Vertical gap | number | - | |\n| getRibSep | Function to get rib gap | (node: NodeData) => number | () => 60 | |\n| width | Layout width | number | - | |\n| height | Layout height | number | - | |\n| nodeSize | Node size | number \\| [number, number] \\| [number, number, number] \\| ((node: NodeData) => number \\| [number, number] \\| [number, number, number]) | - | |\n| isLayoutInvisibleNodes | Whether invisible nodes participate in layout (effective when preLayout is true) | boolean | - | |\n| nodeFilter | Nodes to participate in this layout | (node: NodeData) => boolean | - | |\n| preLayout | Use pre-layout, calculate layout before initializing elements (not suitable for pipeline layout) | boolean | - | |\n\n## Code Example\n\n### Basic Usage\n\nThe simplest configuration:\n\n```js\nimport { Graph, treeToGraphData } from '@antv/g6';\n\nconst graph = new Graph({\n layout: {\n type: 'fishbone',\n },\n autoFit: 'view',\n data: treeToGraphData({\n nodes: [\n { id: 'root', data: { label: 'Root' } },\n { id: 'child1', data: { label: 'Child 1' } },\n { id: 'child2', data: { label: 'Child 2' } },\n { id: 'child3', data: { label: 'Child 3' } },\n ],\n edges: [\n { id: 'e1', source: 'root', target: 'child1' },\n { id: 'e2', source: 'root', target: 'child2' },\n { id: 'e3', source: 'root', target: 'child3' },\n ],\n }),\n edge: {\n type: 'polyline',\n style: {\n lineWidth: 3,\n },\n },\n behaviors: ['drag-canvas'],\n});\n```\n\nResult:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n layout: {\n type: 'fishbone',\n },\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'Quality',\n depth: 0,\n children: ['Machine', 'Method', 'Material', 'Man Power', 'Measurement', 'Milieu'],\n },\n {\n id: 'Machine',\n depth: 1,\n children: ['Mill', 'Mixer', 'Metal Lathe'],\n },\n {\n id: 'Mill',\n depth: 2,\n },\n {\n id: 'Mixer',\n depth: 2,\n },\n {\n id: 'Metal Lathe',\n depth: 2,\n children: ['Milling'],\n },\n {\n id: 'Milling',\n depth: 3,\n },\n {\n id: 'Method',\n depth: 1,\n },\n {\n id: 'Material',\n depth: 1,\n children: ['Masonite', 'Marscapone', 'Meat'],\n },\n {\n id: 'Masonite',\n depth: 2,\n children: ['spearMint', 'pepperMint', 'test1'],\n },\n {\n id: 'spearMint',\n depth: 3,\n },\n {\n id: 'pepperMint',\n depth: 3,\n children: ['test3'],\n },\n {\n id: 'test3',\n depth: 4,\n },\n {\n id: 'test1',\n depth: 3,\n children: ['test4'],\n },\n {\n id: 'test4',\n depth: 4,\n },\n {\n id: 'Marscapone',\n depth: 2,\n children: ['Malty', 'Minty'],\n },\n {\n id: 'Malty',\n depth: 3,\n },\n {\n id: 'Minty',\n depth: 3,\n },\n {\n id: 'Meat',\n depth: 2,\n children: ['Mutton'],\n },\n {\n id: 'Mutton',\n depth: 3,\n },\n {\n id: 'Man Power',\n depth: 1,\n children: ['Manager', \"Master's Student\", 'Magician', 'Miner', 'Magister', 'Massage Artist'],\n },\n {\n id: 'Manager',\n depth: 2,\n },\n {\n id: \"Master's Student\",\n depth: 2,\n },\n {\n id: 'Magician',\n depth: 2,\n },\n {\n id: 'Miner',\n depth: 2,\n },\n {\n id: 'Magister',\n depth: 2,\n children: ['Malpractice'],\n },\n {\n id: 'Malpractice',\n depth: 3,\n },\n {\n id: 'Massage Artist',\n depth: 2,\n children: ['Masseur', 'Masseuse'],\n },\n {\n id: 'Masseur',\n depth: 3,\n },\n {\n id: 'Masseuse',\n depth: 3,\n },\n {\n id: 'Measurement',\n depth: 1,\n children: ['Malleability'],\n },\n {\n id: 'Malleability',\n depth: 2,\n },\n {\n id: 'Milieu',\n depth: 1,\n children: ['Marine'],\n },\n {\n id: 'Marine',\n depth: 2,\n },\n ],\n edges: [\n {\n source: 'Quality',\n target: 'Machine',\n },\n {\n source: 'Quality',\n target: 'Method',\n },\n {\n source: 'Quality',\n target: 'Material',\n },\n {\n source: 'Quality',\n target: 'Man Power',\n },\n {\n source: 'Quality',\n target: 'Measurement',\n },\n {\n source: 'Quality',\n target: 'Milieu',\n },\n {\n source: 'Machine',\n target: 'Mill',\n },\n {\n source: 'Machine',\n target: 'Mixer',\n },\n {\n source: 'Machine',\n target: 'Metal Lathe',\n },\n {\n source: 'Metal Lathe',\n target: 'Milling',\n },\n {\n source: 'Material',\n target: 'Masonite',\n },\n {\n source: 'Material',\n target: 'Marscapone',\n },\n {\n source: 'Material',\n target: 'Meat',\n },\n {\n source: 'Masonite',\n target: 'spearMint',\n },\n {\n source: 'Masonite',\n target: 'pepperMint',\n },\n {\n source: 'Masonite',\n target: 'test1',\n },\n {\n source: 'pepperMint',\n target: 'test3',\n },\n {\n source: 'test1',\n target: 'test4',\n },\n {\n source: 'Marscapone',\n target: 'Malty',\n },\n {\n source: 'Marscapone',\n target: 'Minty',\n },\n {\n source: 'Meat',\n target: 'Mutton',\n },\n {\n source: 'Man Power',\n target: 'Manager',\n },\n {\n source: 'Man Power',\n target: \"Master's Student\",\n },\n {\n source: 'Man Power',\n target: 'Magician',\n },\n {\n source: 'Man Power',\n target: 'Miner',\n },\n {\n source: 'Man Power',\n target: 'Magister',\n },\n {\n source: 'Man Power',\n target: 'Massage Artist',\n },\n {\n source: 'Magister',\n target: 'Malpractice',\n },\n {\n source: 'Massage Artist',\n target: 'Masseur',\n },\n {\n source: 'Massage Artist',\n target: 'Masseuse',\n },\n {\n source: 'Measurement',\n target: 'Malleability',\n },\n {\n source: 'Milieu',\n target: 'Marine',\n },\n ],\n },\n edge: {\n type: 'polyline',\n style: {\n lineWidth: 3,\n },\n },\n behaviors: ['drag-canvas'],\n});\n\ngraph.render();\n```\n\n## Real Case\n\n```js | ob { inject: true }\nimport { Graph, treeToGraphData } from '@antv/g6';\n\nconst data = {\n id: 'Quality',\n children: [\n {\n id: 'Machine',\n children: [{ id: 'Mill' }, { id: 'Mixer' }, { id: 'Metal Lathe', children: [{ id: 'Milling' }] }],\n },\n { id: 'Method' },\n {\n id: 'Material',\n children: [\n {\n id: 'Masonite',\n children: [\n { id: 'spearMint' },\n { id: 'pepperMint', children: [{ id: 'test3' }] },\n { id: 'test1', children: [{ id: 'test4' }] },\n ],\n },\n {\n id: 'Marscapone',\n children: [{ id: 'Malty' }, { id: 'Minty' }],\n },\n { id: 'Meat', children: [{ id: 'Mutton' }] },\n ],\n },\n {\n id: 'Man Power',\n children: [\n { id: 'Manager' },\n { id: \"Master's Student\" },\n { id: 'Magician' },\n { id: 'Miner' },\n { id: 'Magister', children: [{ id: 'Malpractice' }] },\n {\n id: 'Massage Artist',\n children: [{ id: 'Masseur' }, { id: 'Masseuse' }],\n },\n ],\n },\n {\n id: 'Measurement',\n children: [{ id: 'Malleability' }],\n },\n {\n id: 'Milieu',\n children: [{ id: 'Marine' }],\n },\n ],\n};\n\nexport const layoutFishbone = async (context) => {\n const graph = new Graph({\n ...context,\n container: 'container',\n autoFit: 'view',\n data: treeToGraphData(data),\n node: {\n type: 'rect',\n style: {\n size: [32, 32],\n // fill: () => randomColor(),\n label: false,\n labelFill: '#262626',\n labelFontFamily: 'Gill Sans',\n labelMaxLines: 2,\n labelMaxWidth: '100%',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n labelWordWrap: true,\n },\n },\n edge: {\n type: 'polyline',\n style: {\n lineWidth: 3,\n },\n },\n layout: {\n type: 'fishbone',\n vGap: 48,\n hGap: 48,\n direction: 'RL',\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n animation: false,\n });\n\n await graph.render();\n\n layoutFishbone.form = (panel) => {\n const config = {\n type: 'fishbone',\n direction: 'RL',\n };\n\n return [\n panel\n .add(config, 'direction', ['LR', 'RL'])\n .name('Direction')\n .onChange((value) => {\n graph.setLayout((prev) => ({ ...prev, direction: value }));\n graph.render();\n }),\n ];\n };\n\n return graph;\n};\n\nlayoutFishbone();\n```\n\n- [Fishbone Layout](/en/examples/layout/fishbone/#basic)\n\n\n\n---\ntitle: 鱼骨布局 Fishbone\norder: 11\n---\n\n## 概述\n\n鱼骨布局是一种专门用于表示层次结构数据的图形布局方式。它通过模拟鱼骨的形状,将数据节点按照层次结构排列,使得数据的层次关系更加清晰直观。鱼骨布局特别适用于需要展示因果关系、层次结构或分类信息的数据集。\n\n## 使用场景\n\n- 需要展示层次结构数据,如组织结构、分类体系\n- 需要展示问题分析过程,如故障分析、质量分析\n- 需要展示决策过程,如决策树、影响因素分析\n\n## 在线体验\n\n\n\n## 基本用法\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'fishbone',\n direction: 'LR',\n hGap: 50,\n vGap: 50,\n getRibSep: () => 60,\n },\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ---------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---- |\n| type | 布局类型 | `fishbone` | - | ✓ |\n| direction | 排布方向,`RL` 从右到左,鱼头在右;`LR` 从左到右,鱼头在左 | `RL` \\| `LR` | `RL` | |\n| hGap | 水平间距 | number | - | |\n| vGap | 垂直间距 | number | - | |\n| getRibSep | 获取鱼骨间距 | (node: NodeData) => number | () => 60 | |\n| width | 布局宽度 | number | - | |\n| height | 布局高度 | number | - | |\n| nodeSize | 节点大小 | number \\| [number, number] \\| [number, number, number] \\| ((node: NodeData) => number \\| [number, number] \\| [number, number, number]) | - | |\n| isLayoutInvisibleNodes | 不可见节点是否参与布局,当 preLayout 为 true 时生效 | boolean | - | |\n| nodeFilter | 参与该布局的节点 | (node: NodeData) => boolean | - | |\n| preLayout | 使用前布局,在初始化元素前计算布局,不适用于流水线布局 | boolean | - | |\n\n## 代码示例\n\n### 基础用法\n\n最简单的配置方式:\n\n```js\nimport { Graph, treeToGraphData } from '@antv/g6';\n\nconst graph = new Graph({\n layout: {\n type: 'fishbone',\n },\n autoFit: 'view',\n data: treeToGraphData({\n nodes: [\n { id: 'root', data: { label: 'Root' } },\n { id: 'child1', data: { label: 'Child 1' } },\n { id: 'child2', data: { label: 'Child 2' } },\n { id: 'child3', data: { label: 'Child 3' } },\n ],\n edges: [\n { id: 'e1', source: 'root', target: 'child1' },\n { id: 'e2', source: 'root', target: 'child2' },\n { id: 'e3', source: 'root', target: 'child3' },\n ],\n }),\n edge: {\n type: 'polyline',\n style: {\n lineWidth: 3,\n },\n },\n behaviors: ['drag-canvas'],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n layout: {\n type: 'fishbone',\n },\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'Quality',\n depth: 0,\n children: ['Machine', 'Method', 'Material', 'Man Power', 'Measurement', 'Milieu'],\n },\n {\n id: 'Machine',\n depth: 1,\n children: ['Mill', 'Mixer', 'Metal Lathe'],\n },\n {\n id: 'Mill',\n depth: 2,\n },\n {\n id: 'Mixer',\n depth: 2,\n },\n {\n id: 'Metal Lathe',\n depth: 2,\n children: ['Milling'],\n },\n {\n id: 'Milling',\n depth: 3,\n },\n {\n id: 'Method',\n depth: 1,\n },\n {\n id: 'Material',\n depth: 1,\n children: ['Masonite', 'Marscapone', 'Meat'],\n },\n {\n id: 'Masonite',\n depth: 2,\n children: ['spearMint', 'pepperMint', 'test1'],\n },\n {\n id: 'spearMint',\n depth: 3,\n },\n {\n id: 'pepperMint',\n depth: 3,\n children: ['test3'],\n },\n {\n id: 'test3',\n depth: 4,\n },\n {\n id: 'test1',\n depth: 3,\n children: ['test4'],\n },\n {\n id: 'test4',\n depth: 4,\n },\n {\n id: 'Marscapone',\n depth: 2,\n children: ['Malty', 'Minty'],\n },\n {\n id: 'Malty',\n depth: 3,\n },\n {\n id: 'Minty',\n depth: 3,\n },\n {\n id: 'Meat',\n depth: 2,\n children: ['Mutton'],\n },\n {\n id: 'Mutton',\n depth: 3,\n },\n {\n id: 'Man Power',\n depth: 1,\n children: ['Manager', \"Master's Student\", 'Magician', 'Miner', 'Magister', 'Massage Artist'],\n },\n {\n id: 'Manager',\n depth: 2,\n },\n {\n id: \"Master's Student\",\n depth: 2,\n },\n {\n id: 'Magician',\n depth: 2,\n },\n {\n id: 'Miner',\n depth: 2,\n },\n {\n id: 'Magister',\n depth: 2,\n children: ['Malpractice'],\n },\n {\n id: 'Malpractice',\n depth: 3,\n },\n {\n id: 'Massage Artist',\n depth: 2,\n children: ['Masseur', 'Masseuse'],\n },\n {\n id: 'Masseur',\n depth: 3,\n },\n {\n id: 'Masseuse',\n depth: 3,\n },\n {\n id: 'Measurement',\n depth: 1,\n children: ['Malleability'],\n },\n {\n id: 'Malleability',\n depth: 2,\n },\n {\n id: 'Milieu',\n depth: 1,\n children: ['Marine'],\n },\n {\n id: 'Marine',\n depth: 2,\n },\n ],\n edges: [\n {\n source: 'Quality',\n target: 'Machine',\n },\n {\n source: 'Quality',\n target: 'Method',\n },\n {\n source: 'Quality',\n target: 'Material',\n },\n {\n source: 'Quality',\n target: 'Man Power',\n },\n {\n source: 'Quality',\n target: 'Measurement',\n },\n {\n source: 'Quality',\n target: 'Milieu',\n },\n {\n source: 'Machine',\n target: 'Mill',\n },\n {\n source: 'Machine',\n target: 'Mixer',\n },\n {\n source: 'Machine',\n target: 'Metal Lathe',\n },\n {\n source: 'Metal Lathe',\n target: 'Milling',\n },\n {\n source: 'Material',\n target: 'Masonite',\n },\n {\n source: 'Material',\n target: 'Marscapone',\n },\n {\n source: 'Material',\n target: 'Meat',\n },\n {\n source: 'Masonite',\n target: 'spearMint',\n },\n {\n source: 'Masonite',\n target: 'pepperMint',\n },\n {\n source: 'Masonite',\n target: 'test1',\n },\n {\n source: 'pepperMint',\n target: 'test3',\n },\n {\n source: 'test1',\n target: 'test4',\n },\n {\n source: 'Marscapone',\n target: 'Malty',\n },\n {\n source: 'Marscapone',\n target: 'Minty',\n },\n {\n source: 'Meat',\n target: 'Mutton',\n },\n {\n source: 'Man Power',\n target: 'Manager',\n },\n {\n source: 'Man Power',\n target: \"Master's Student\",\n },\n {\n source: 'Man Power',\n target: 'Magician',\n },\n {\n source: 'Man Power',\n target: 'Miner',\n },\n {\n source: 'Man Power',\n target: 'Magister',\n },\n {\n source: 'Man Power',\n target: 'Massage Artist',\n },\n {\n source: 'Magister',\n target: 'Malpractice',\n },\n {\n source: 'Massage Artist',\n target: 'Masseur',\n },\n {\n source: 'Massage Artist',\n target: 'Masseuse',\n },\n {\n source: 'Measurement',\n target: 'Malleability',\n },\n {\n source: 'Milieu',\n target: 'Marine',\n },\n ],\n },\n edge: {\n type: 'polyline',\n style: {\n lineWidth: 3,\n },\n },\n behaviors: ['drag-canvas'],\n});\n\ngraph.render();\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph, treeToGraphData } from '@antv/g6';\n\nconst data = {\n id: 'Quality',\n children: [\n {\n id: 'Machine',\n children: [{ id: 'Mill' }, { id: 'Mixer' }, { id: 'Metal Lathe', children: [{ id: 'Milling' }] }],\n },\n { id: 'Method' },\n {\n id: 'Material',\n children: [\n {\n id: 'Masonite',\n children: [\n { id: 'spearMint' },\n { id: 'pepperMint', children: [{ id: 'test3' }] },\n { id: 'test1', children: [{ id: 'test4' }] },\n ],\n },\n {\n id: 'Marscapone',\n children: [{ id: 'Malty' }, { id: 'Minty' }],\n },\n { id: 'Meat', children: [{ id: 'Mutton' }] },\n ],\n },\n {\n id: 'Man Power',\n children: [\n { id: 'Manager' },\n { id: \"Master's Student\" },\n { id: 'Magician' },\n { id: 'Miner' },\n { id: 'Magister', children: [{ id: 'Malpractice' }] },\n {\n id: 'Massage Artist',\n children: [{ id: 'Masseur' }, { id: 'Masseuse' }],\n },\n ],\n },\n {\n id: 'Measurement',\n children: [{ id: 'Malleability' }],\n },\n {\n id: 'Milieu',\n children: [{ id: 'Marine' }],\n },\n ],\n};\n\nexport const layoutFishbone = async (context) => {\n const graph = new Graph({\n ...context,\n container: 'container',\n autoFit: 'view',\n data: treeToGraphData(data),\n node: {\n type: 'rect',\n style: {\n size: [32, 32],\n // fill: () => randomColor(),\n label: false,\n labelFill: '#262626',\n labelFontFamily: 'Gill Sans',\n labelMaxLines: 2,\n labelMaxWidth: '100%',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n labelWordWrap: true,\n },\n },\n edge: {\n type: 'polyline',\n style: {\n lineWidth: 3,\n },\n },\n layout: {\n type: 'fishbone',\n vGap: 48,\n hGap: 48,\n direction: 'RL',\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n animation: false,\n });\n\n await graph.render();\n\n layoutFishbone.form = (panel) => {\n const config = {\n type: 'fishbone',\n direction: 'RL',\n };\n\n return [\n panel\n .add(config, 'direction', ['LR', 'RL'])\n .name('Direction')\n .onChange((value) => {\n graph.setLayout((prev) => ({ ...prev, direction: value }));\n graph.render();\n }),\n ];\n };\n\n return graph;\n};\n\nlayoutFishbone();\n```\n\n- [Fishbone布局](/examples/layout/fishbone/#basic)\n\n\n\n---\ntitle: ForceAtlas2 Force-directed Layout\norder: 12\n---\n\n## Overview\n\nForceAtlas2 is a force-directed layout algorithm that optimizes node positions by simulating forces in a physical system. This layout is especially suitable for visualizing large-scale network data, effectively revealing relationships and cluster structures among nodes.\n\n## Use Cases\n\n- Social network analysis: Display user relationship networks, with node degree reflecting user influence\n- Knowledge graphs: Show associations between concepts, discover knowledge domains through clustering\n- System architecture diagrams: Show dependencies between system components, highlight core components via hub mode\n\n## Online Demo\n\n\n\n## Basic Usage\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'force-atlas2',\n preventOverlap: true,\n kr: 20,\n center: [250, 250],\n },\n});\n```\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------- | -------- |\n| type | Layout type, must be `force-atlas2` | `force-atlas2` | - | ✓ |\n| maxIteration | Maximum number of iterations | number | 500 | |\n| minMovement | Convergence threshold | number | 0.4 | |\n| barnesHut | Whether to enable quadtree acceleration. When enabled, improves performance for large graphs but may affect layout quality. By default, enabled if node count > 250. | boolean | - | |\n| dissuadeHubs | Whether to enable hub mode. If true, nodes with higher in-degree are more likely to be placed at the center than those with high out-degree | boolean | false | |\n| height | Layout height. Defaults to container height | number | - | |\n| kg | Gravity coefficient. The larger the value, the more concentrated the layout is at the center | number | 1 | |\n| kr | Repulsion coefficient. Adjusts the compactness of the layout. The larger the value, the looser the layout | number | 5 | |\n| ks | Controls the speed of node movement during iteration | number | 0.1 | |\n| ksmax | Maximum node movement speed during iteration | number | 10 | |\n| mode | Clustering mode. In `linlog` mode, clusters are more compact | `normal` \\| `linlog` | `normal` | |\n| nodeSize | Node size (diameter). Used for repulsion calculation when `preventOverlap` is enabled. If not set, uses `data.size` in node data | Size \\| ((node?: Node) => Size) | - | |\n| nodeSpacing | Spacing between nodes | number \\| ((node?: Node) => number) | - | |\n| onTick | Callback for each iteration | (data: LayoutMapping) => void | - | |\n| preventOverlap | Whether to prevent node overlap. When enabled, layout considers node size to avoid overlap. Node size is specified by `nodeSize` or `data.size` in node data | boolean | false | |\n| prune | Whether to enable auto-pruning. By default, enabled if node count > 100. Pruning speeds up convergence but may reduce layout quality. Set to false to disable auto-activation | boolean | - | |\n| tao | Tolerance for stopping oscillation when layout is near convergence | number | 0.1 | |\n| width | Layout width. Defaults to container width | number | - | |\n| center | Layout center, format [x, y]. Each node is attracted to this point, gravity controlled by `kg`. If not set, uses canvas center | [number, number] | - | |\n\n## Code Example\n\n### Basic Usage\n\n```js\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n layout: {\n type: 'force-atlas2',\n preventOverlap: true,\n kr: 20,\n },\n autoFit: 'view',\n data: {\n nodes: [\n { id: 'node1' },\n { id: 'node2' },\n { id: 'node3' },\n { id: 'node4' },\n { id: 'node5' },\n { id: 'node6' },\n { id: 'node7' },\n { id: 'node8' },\n { id: 'node9' },\n { id: 'node10' },\n { id: 'node11' },\n { id: 'node12' },\n { id: 'node13' },\n { id: 'node14' },\n { id: 'node15' },\n ],\n edges: [\n { source: 'node1', target: 'node2' },\n { source: 'node2', target: 'node3' },\n { source: 'node3', target: 'node4' },\n { source: 'node4', target: 'node5' },\n { source: 'node5', target: 'node6' },\n { source: 'node6', target: 'node7' },\n { source: 'node7', target: 'node8' },\n { source: 'node8', target: 'node9' },\n { source: 'node9', target: 'node10' },\n { source: 'node10', target: 'node11' },\n { source: 'node11', target: 'node12' },\n { source: 'node12', target: 'node13' },\n { source: 'node13', target: 'node14' },\n { source: 'node14', target: 'node15' },\n { source: 'node15', target: 'node1' },\n { source: 'node1', target: 'node8' },\n { source: 'node2', target: 'node9' },\n { source: 'node3', target: 'node10' },\n { source: 'node4', target: 'node11' },\n { source: 'node5', target: 'node12' },\n { source: 'node6', target: 'node13' },\n { source: 'node7', target: 'node14' },\n ],\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n});\n```\n\nResult:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 300,\n layout: {\n type: 'force-atlas2',\n preventOverlap: true,\n kr: 20,\n },\n data: {\n nodes: [\n { id: 'node1' },\n { id: 'node2' },\n { id: 'node3' },\n { id: 'node4' },\n { id: 'node5' },\n { id: 'node6' },\n { id: 'node7' },\n { id: 'node8' },\n { id: 'node9' },\n { id: 'node10' },\n { id: 'node11' },\n { id: 'node12' },\n { id: 'node13' },\n { id: 'node14' },\n { id: 'node15' },\n ],\n edges: [\n { source: 'node1', target: 'node2' },\n { source: 'node2', target: 'node3' },\n { source: 'node3', target: 'node4' },\n { source: 'node4', target: 'node5' },\n { source: 'node5', target: 'node6' },\n { source: 'node6', target: 'node7' },\n { source: 'node7', target: 'node8' },\n { source: 'node8', target: 'node9' },\n { source: 'node9', target: 'node10' },\n { source: 'node10', target: 'node11' },\n { source: 'node11', target: 'node12' },\n { source: 'node12', target: 'node13' },\n { source: 'node13', target: 'node14' },\n { source: 'node14', target: 'node15' },\n { source: 'node15', target: 'node1' },\n { source: 'node1', target: 'node8' },\n { source: 'node2', target: 'node9' },\n { source: 'node3', target: 'node10' },\n { source: 'node4', target: 'node11' },\n { source: 'node5', target: 'node12' },\n { source: 'node6', target: 'node13' },\n { source: 'node7', target: 'node14' },\n ],\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n## Real Example\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n data,\n autoFit: 'view',\n layout: {\n type: 'force-atlas2',\n preventOverlap: true,\n kr: 20,\n center: [250, 250],\n },\n behaviors: ['zoom-canvas', 'drag-canvas'],\n autoResize: true,\n zoomRange: [0.1, 5],\n });\n\n graph.render();\n });\n```\n\n- [ForceAtlas2 Layout](/en/examples/layout/force-directed/#atlas2)\n\n\n\n---\ntitle: ForceAtlas2 力导向布局\norder: 12\n---\n\n## 概述\n\nForceAtlas2 是一种基于力导向的布局算法,它通过模拟物理系统中的力来优化节点位置。该布局特别适用于大规模网络数据的可视化,能够有效地展示节点之间的关系和聚类结构。\n\n## 使用场景\n\n- 社交网络分析:展示用户之间的关系网络,通过节点度数反映用户影响力\n- 知识图谱:展示概念之间的关联关系,通过聚类效果发现知识领域\n- 系统架构图:展示系统组件之间的依赖关系,通过 hub 模式突出核心组件\n\n## 在线体验\n\n\n\n## 基本用法\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'force-atlas2',\n preventOverlap: true,\n kr: 20,\n center: [250, 250],\n },\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------- | ---- |\n| type | 布局类型,必须为 `force-atlas2` | `force-atlas2` | - | ✓ |\n| maxIteration | 最大迭代次数 | number | 500 | |\n| minMovement | 收敛阈值 | number | 0.4 | |\n| barnesHut | 是否开启四叉树加速,开启后可以提升大规模图的布局性能,但可能会影响布局质量。默认情况下为 undefined,当节点数量大于 250 时它将会被激活。设置为 false 则不会自动被激活 | boolean | - | |\n| dissuadeHubs | 是否开启 hub 模式。若为 true,相比与出度大的节点,入度大的节点将会有更高的优先级被放置在中心位置 | boolean | false | |\n| height | 布局高度,默认使用容器高度 | number | - | |\n| kg | 重力系数,`kg` 越大,布局越聚集在中心 | number | 1 | |\n| kr | 斥力系数,可用于调整布局的紧凑程度。kr 越大,布局越松散 | number | 5 | |\n| ks | 控制迭代过程中,节点移动的速度 | number | 0.1 | |\n| ksmax | 迭代过程中,最大的节点移动的速度上限 | number | 10 | |\n| mode | 聚类模式,`linlog` 模式下,聚类将更加紧凑 | `normal` \\| `linlog` | `normal` | |\n| nodeSize | 节点大小(直径)。当开启 `preventOverlap` 时,用于计算节点之间的斥力。如果不设置,则使用节点数据中的 size 属性 | Size \\| ((node?: Node) => Size) | - | |\n| nodeSpacing | 节点间距 | number \\| ((node?: Node) => number) | - | |\n| onTick | 每一次迭代的回调函数 | (data: LayoutMapping) => void | - | |\n| preventOverlap | 是否防止节点重叠。开启后,布局会考虑节点大小,避免节点重叠 | boolean | false | |\n| prune | 是否开启自动剪枝模式。默认情况下为 undefined,当节点数量大于 100 时它将会被激活。注意,剪枝能够提高收敛速度,但可能会降低图的布局质量。设置为 false 则不会自动被激活 | boolean | - | |\n| tao | 迭代接近收敛时停止震荡的容忍度 | number | 0.1 | |\n| width | 布局宽度,默认使用容器宽度 | number | - | |\n| center | 布局中心点,用于指定重力的中心,格式为 [x, y]。每个节点都会受到一个指向该中心点的重力,重力大小由 `kg` 参数控制。如果不设置,则使用画布中心点 | [number, number] | - | |\n\n## 代码示例\n\n### 基础用法\n\n```js\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n layout: {\n type: 'force-atlas2',\n preventOverlap: true,\n kr: 20,\n },\n autoFit: 'view',\n data: {\n nodes: [\n { id: 'node1' },\n { id: 'node2' },\n { id: 'node3' },\n { id: 'node4' },\n { id: 'node5' },\n { id: 'node6' },\n { id: 'node7' },\n { id: 'node8' },\n { id: 'node9' },\n { id: 'node10' },\n { id: 'node11' },\n { id: 'node12' },\n { id: 'node13' },\n { id: 'node14' },\n { id: 'node15' },\n ],\n edges: [\n { source: 'node1', target: 'node2' },\n { source: 'node2', target: 'node3' },\n { source: 'node3', target: 'node4' },\n { source: 'node4', target: 'node5' },\n { source: 'node5', target: 'node6' },\n { source: 'node6', target: 'node7' },\n { source: 'node7', target: 'node8' },\n { source: 'node8', target: 'node9' },\n { source: 'node9', target: 'node10' },\n { source: 'node10', target: 'node11' },\n { source: 'node11', target: 'node12' },\n { source: 'node12', target: 'node13' },\n { source: 'node13', target: 'node14' },\n { source: 'node14', target: 'node15' },\n { source: 'node15', target: 'node1' },\n { source: 'node1', target: 'node8' },\n { source: 'node2', target: 'node9' },\n { source: 'node3', target: 'node10' },\n { source: 'node4', target: 'node11' },\n { source: 'node5', target: 'node12' },\n { source: 'node6', target: 'node13' },\n { source: 'node7', target: 'node14' },\n ],\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 300,\n layout: {\n type: 'force-atlas2',\n preventOverlap: true,\n kr: 20,\n },\n data: {\n nodes: [\n { id: 'node1' },\n { id: 'node2' },\n { id: 'node3' },\n { id: 'node4' },\n { id: 'node5' },\n { id: 'node6' },\n { id: 'node7' },\n { id: 'node8' },\n { id: 'node9' },\n { id: 'node10' },\n { id: 'node11' },\n { id: 'node12' },\n { id: 'node13' },\n { id: 'node14' },\n { id: 'node15' },\n ],\n edges: [\n { source: 'node1', target: 'node2' },\n { source: 'node2', target: 'node3' },\n { source: 'node3', target: 'node4' },\n { source: 'node4', target: 'node5' },\n { source: 'node5', target: 'node6' },\n { source: 'node6', target: 'node7' },\n { source: 'node7', target: 'node8' },\n { source: 'node8', target: 'node9' },\n { source: 'node9', target: 'node10' },\n { source: 'node10', target: 'node11' },\n { source: 'node11', target: 'node12' },\n { source: 'node12', target: 'node13' },\n { source: 'node13', target: 'node14' },\n { source: 'node14', target: 'node15' },\n { source: 'node15', target: 'node1' },\n { source: 'node1', target: 'node8' },\n { source: 'node2', target: 'node9' },\n { source: 'node3', target: 'node10' },\n { source: 'node4', target: 'node11' },\n { source: 'node5', target: 'node12' },\n { source: 'node6', target: 'node13' },\n { source: 'node7', target: 'node14' },\n ],\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n data,\n autoFit: 'view',\n layout: {\n type: 'force-atlas2',\n preventOverlap: true,\n kr: 20,\n center: [250, 250],\n },\n behaviors: ['zoom-canvas', 'drag-canvas'],\n autoResize: true,\n zoomRange: [0.1, 5],\n });\n\n graph.render();\n });\n```\n\n- [ForceAtlas2布局](/examples/layout/force-directed/#atlas2)\n\n\n\n---\ntitle: Force-directed Layout\norder: 13\n---\n\n## Overview\n\nForce-directed layout is a graph layout algorithm based on physical simulation that determines node positions by simulating attraction and repulsion forces between nodes. This layout is particularly suitable for displaying complex relationship networks, such as social networks and knowledge graphs.\n\nThe force-directed layout automatically calculates and adjusts node positions to maintain appropriate distances between connected nodes while minimizing edge crossings. During the layout process, it simulates a physical system where nodes repel each other like charged particles, and edges connect nodes like springs.\n\nKey features of force-directed layout include:\n\n1. **Automatic Arrangement**: No need to manually set node positions, the system automatically finds suitable positions\n2. **Real-time Adjustment**: When you drag a node, other nodes will adjust their positions in real-time\n3. **Flexible Configuration**:\n - Can adjust attraction and repulsion forces between nodes\n - Can set edge lengths\n - Can prevent node overlap\n4. **Animation Effects**: Smooth animations during node movement make changes more natural\n\nForce-directed Layout Example\n\n## Core Concepts\n\n### Basic Principles of Force-directed Layout\n\nForce-directed layout is a graph layout algorithm based on physical simulation that models nodes and edges as a physical system:\n\n- Nodes are treated as physical particles\n- Edges are treated as springs\n- The entire system reaches its lowest energy state through physical simulation\n\n### Detailed Core Forces\n\n#### Node Repulsion\n\n- **Physical Model**: Coulomb's Law\n- **Function**: Prevents node overlap and ensures more uniform node distribution, where `factor` and `coulombDisScale` control the overall strength and range of repulsion.\n- **Formula**:\n Repulsion Force\n\n - k: Repulsion coefficient (`factor` / `coulombDisScale²`)\n - q1,q2: Node strength (`nodeStrength`)\n - r: Distance between nodes\n\n#### Edge Attraction\n\n- **Physical Model**: Hooke's Law\n- **Function**: Simulates edge tension, moving nodes along edge directions, where `edgeStrength` and `linkDistance` control edge \"stiffness\" and length.\n- **Formula**:\n Edge Attraction\n\n - ka: Edge attraction strength (`edgeStrength`)\n - L: Edge length (`linkDistance`)\n - r: Actual edge length\n\n#### Centripetal Force\n\n- **Physical Model**: Newton's Universal Law of Gravitation\n- **Function**: Attracts nodes toward the canvas center or cluster centers, where `gravity` and `center` control gravity strength and center point position\n- **Formula**:\n Centripetal Force\n\n - G: Gravitational constant (`gravity`)\n - xc: Center point coordinates (`center`)\n - mass: Node mass (`nodeSize`)\n\n#### Interaction of Three Forces\n\n- **Physical Model**: Force interactions, generating acceleration\n- **Function**: Repulsion, edge attraction, and centripetal force work together, affecting node movement through acceleration superposition, ultimately reaching the lowest energy state.\n- **Formula**:\n Force Interactions\n\n### Physical System\n\n#### Node Velocity Formula\n\n- **Formula**:\n Node Velocity Formula\n\n - v: Velocity\n - a: Acceleration\n - dt: Time step (`interval`)\n - damping: Damping coefficient (`damping`)\n\n- **Function**:\n 1. Controls node movement stability\n 2. Damping coefficient prevents system oscillation\n 3. Time step affects displacement per iteration\n\n#### Node Position Formula\n\n- **Formula**:\n Node Position Formula\n\n - x: Node position\n - v: Node velocity\n - dt: Time step (`interval`)\n\n- **Function**:\n 1. Updates node position based on velocity\n 2. Ensures motion continuity\n 3. Prevents node overlap through `preventOverlap`\n\n#### Cluster Center Calculation\n\n- **Formula**:\n Cluster Center Calculation\n\n - n: Number of nodes in cluster\n - (xi​,yi​): Position of each node\n\n- **Function**:\n 1. Calculates cluster center\n 2. Centripetal force pulls nodes toward their cluster center\n 3. Cluster center can change dynamically\n\n#### Cluster Strength Calculation\n\n- **Formula**:\n Cluster Strength Calculation\n\n - s: Cluster strength (`clusterNodeStrength`)\n - xc​: Cluster center\n\n- **Function**:\n 1. Controls cluster compactness\n 2. Higher cluster strength means tighter clusters\n 3. Can be dynamically adjusted based on node properties\n\n#### Mass Effect on Forces\n\n- **Formula**:\n Mass Effect on Forces\n\n - a: Acceleration\n - F: Force (repulsion, edge attraction, centripetal force)\n - mass: Node mass\n\n- **Function**:\n 1. Nodes with larger mass move less\n 2. Nodes with smaller mass move more\n 3. Mass calculation can be customized through `getMass`\n\n#### Energy Calculation\n\n- **Formula**:\n Energy Calculation\n\n - m: Node mass\n - v: Node velocity\n\n- **Function**:\n 1. Monitors layout convergence\n 2. System stabilizes when energy approaches zero\n\n#### System Convergence Condition\n\n- **Formula**:\n System Convergence Condition\n\n- **Function**:\n 1. Controls iteration count\n 2. Stops when movement is below threshold\n 3. Can choose between mean, maximum, or minimum through `distanceThresholdMode`\n\n### Force Interaction Diagram\n\n```mermaid\ngraph TD\n A[Input] --> B[Initialize Parameters];\n B --> C[Build Layout Calculation];\n C --> D[Iterative Calculation];\n D --> E{Converged?};\n E -->|Yes| F[Output Layout];\n E -->|No| G[Calculate Repulsion];\n G --> H[Calculate Edge Attraction];\n H --> I[Calculate Centripetal Force];\n I --> J[Update Velocity];\n J --> K[Update Position];\n K --> D;\n```\n\nForce Simulation Diagram\n\n## Configuration Options\n\nBased on the physical characteristics of force-directed layout, the following configuration options are available:\n\n### Basic Configuration\n\n| Property | Description | Default Value | Required |\n| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -------- |\n| type | Layout type | `force` | ✓ |\n| dimensions | Layout dimensions, 2 for 2D layout, 3 for 3D layout | 2 | |\n| width | Layout width | Canvas width | |\n| height | Layout height | Canvas height | |\n| center | Layout center point | Graph center | |\n| maxIteration | Maximum iteration count, if 0 will auto-adjust | 0 | |\n| minMovement | Stop iteration when average movement distance is less than 0.4 | 0.4 | |\n| distanceThresholdMode | Movement distance calculation mode: mean: stop when average movement distance is less than `minMovement`; max: stop when maximum movement distance is less than `minMovement`; min: stop when minimum movement distance is less than `minMovement` | `mean` | |\n| maxDistance | Maximum distance | | |\n\n### Force-related Configuration\n\n#### Repulsion Configuration\n\n| Property | Description | Default Value | Required |\n| --------------- | --------------------------------------------------------------------------------------------------- | ------------- | -------- |\n| nodeStrength | Node force, positive values represent attraction between nodes, negative values represent repulsion | 1000 | |\n| factor | Repulsion coefficient, larger values mean stronger repulsion | 1 | |\n| coulombDisScale | Coulomb coefficient, a factor for repulsion, larger values mean stronger repulsion between nodes | 0.005 | |\n\n#### Edge Attraction Configuration\n\n| Property | Description | Default Value | Required |\n| ------------ | -------------------------------------------------------------------------------------------------------------- | ------------- | -------- |\n| edgeStrength | Edge force (attraction) strength, fixed force or callback function to dynamically return different edge forces | 500 | |\n| linkDistance | Edge length, fixed length or callback function to dynamically return different edge lengths | 200 | |\n\n#### Centripetal Force Configuration\n\n| Property | Description | Default Value | Required |\n| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -------- |\n| gravity | Center force strength, the force attracting all nodes to the center. Larger values mean more compact layout | 10 | |\n| centripetalOptions | Centripetal force configuration, including center and strength for leaf nodes, isolated nodes, and other nodes. leaf: leaf node centripetal force; single: single node centripetal force; others: other node centripetal force; center: custom center point function | [0, 0] | |\n\n#### Clustering Configuration\n\n| Property | Description | Default Value | Required |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -------- |\n| clustering | Whether to cluster all nodes. If true, will use the field specified by nodeClusterBy in node data for clustering. centripetalOptions.single, centripetalOptions.leaf, and centripetalOptions.others will use the value returned by getClusterNodeStrength; leaf and centripetalOptions.center will use the average center of all nodes in the current cluster | `false` | |\n| nodeClusterBy | Specifies the field name in node data for clustering. Takes effect when clustering is true. Automatically generates centripetalOptions, can be used with clusterNodeStrength | | |\n| clusterNodeStrength | Used with clustering and nodeClusterBy to specify the strength of the cluster centripetal force | | |\n| leafCluster | Whether to cluster leaf nodes. If true, centripetalOptions.single will be 100; centripetalOptions.leaf will use the value returned by getClusterNodeStrength; getClusterNodeStrength.center will return the average center of all leaf nodes | false | |\n\n#### Performance and Optimization Configuration\n\n| Property | Description | Default Value | Required |\n| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -------- |\n| damping | Damping coefficient, range [0, 1]. Larger values mean slower speed decrease | 0.9 | |\n| maxSpeed | Maximum movement length per iteration | 200 | |\n| interval | Controls the movement speed of each node per iteration | 0.02 | |\n| preventOverlap | Whether to prevent overlap. Must be used with nodeSize or data.size in node data. Only when data.size is set in the data or nodeSize is configured in the layout with the same value as the node size in the graph, collision detection for node overlap can be performed | true | |\n| nodeSize | Node size (diameter). Used for collision detection to prevent node overlap. Fixed size or callback function to dynamically return node size | | |\n| nodeSpacing | Takes effect when preventOverlap is true. Minimum spacing between node edges to prevent overlap. Can be a callback to set different spacing for different nodes | | |\n| collideStrength | Strength of anti-overlap force, range [0, 1] | 1 | |\n\n#### Other Configuration\n\n| Property | Description | Default Value | Required |\n| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -------- |\n| getMass | Callback for the mass of each node. The parameter is the node's internal data, and the return value is the mass | | |\n| getCenter | Callback for the x, y, and strength of the centripetal force for each node. If not specified, no extra centripetal force is applied | | |\n| onTick | Callback for each iteration | | |\n| monitor | Callback for monitoring each iteration. energy indicates the convergence energy of the layout. May incur extra computation if configured; if not configured, no computation is performed | | |\n\n## Code Examples\n\n### Basic Usage\n\n```js\nconst graph = new Graph({\n container: 'container',\n layout: {\n type: 'force',\n // Prevent node overlap\n preventOverlap: true,\n // Node size\n nodeSize: 20,\n // Layout width\n width: 800,\n // Layout height\n height: 600,\n },\n});\n```\n\n### Preventing Node Overlap\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'force',\n // Prevent node overlap\n preventOverlap: true,\n // Node size\n nodeSize: 20,\n },\n});\n```\n\n### Force-directed Layout\n\nThis example demonstrates how to create a basic force-directed graph using force-directed layout.\n\n```js\nimport { Graph, NodeEvent } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', label: 'Node 1', size: 30 },\n { id: 'node2', label: 'Node 2', size: 20 },\n { id: 'node3', label: 'Node 3', size: 20 },\n { id: 'node4', label: 'Node 4', size: 20 },\n { id: 'node5', label: 'Node 5', size: 30 },\n { id: 'node6', label: 'Node 6', size: 20 },\n ],\n edges: [\n { source: 'node1', target: 'node2' },\n { source: 'node1', target: 'node3' },\n { source: 'node2', target: 'node4' },\n { source: 'node3', target: 'node4' },\n { source: 'node4', target: 'node5' },\n { source: 'node5', target: 'node6' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n autoFit: 'view',\n modes: {\n default: ['drag-canvas', 'zoom-canvas'],\n },\n layout: {\n type: 'force',\n // Prevent node overlap\n preventOverlap: true,\n // Node size\n nodeSize: 20,\n // Centripetal force\n gravity: 0.9,\n // Iteration count\n iterations: 100,\n },\n node: {\n style: {\n size: (d) => d.size,\n fill: '#9EC9FF',\n stroke: '#69C8FF',\n label: (d) => d.label,\n labelPlacement: 'center',\n labelFill: '#333',\n },\n },\n edge: {\n style: {\n stroke: '#e2e2e2',\n },\n },\n});\n\ngraph.on(NodeEvent.CLICK, async (e) => {\n const nodeId = e.target.id;\n graph.updateNodeData([{ id: nodeId, size: 200 }]);\n await graph.render();\n});\n\ngraph.render();\n```\n\nKey configuration explanations:\n\n- `preventOverlap`: Enable node overlap detection\n- `nodeSize`: Set node size\n- `gravity`: Set node centripetal force\n- `iterations`: Set layout calculation precision\n\nYou can also refer to [View Examples](https://g6.antv.antgroup.com/examples/layout/force-directed/#force) for more usage examples.\n\n\n\n---\ntitle: Force 力导向布局\norder: 13\n---\n\n## 概述\n\n力导向布局是一种基于物理模拟的图布局算法,它通过模拟节点间的引力和斥力来确定节点的位置。这种布局方式特别适合展示复杂的关系网络,如社交网络、知识图谱等。\n\n力导向布局会自动计算并调整节点位置,使得相连的节点保持适当的距离,同时尽量减少边的交叉。布局过程中会模拟物理系统,节点会像带电粒子一样相互排斥,边则像弹簧一样连接节点。\n\n力导向布局的主要特点包括:\n\n1. **自动排列**:不需要手动设置节点位置,系统会自动找到合适的位置\n2. **实时调整**:当你拖动某个节点时,其他节点会实时跟随调整位置\n3. **灵活配置**:\n - 可以调整节点间的吸引力和排斥力\n - 可以设置边的长度\n - 可以防止节点重叠\n4. **动画效果**:节点移动时会有平滑的动画,让变化更自然\n\n力导向布局示例\n\n## 核心概念\n\n### Force 力导向布局基本原理\n\n力导向布局是一种基于物理模拟的图布局算法,它将图中的节点和边模拟为物理系统:\n\n- 节点被视为物理粒子\n- 边被视为弹簧\n- 整个系统通过物理模拟达到能量最低状态\n\n### 核心力详解\n\n#### 斥力(Node Repulsion)\n\n- **物理模型**:库伦定律(Coulomb's Law)\n- **作用**:防止节点重叠,让节点分布更均匀,其中 `factor` 和 `coulombDisScale` 控制斥力的总体强度和范围。\n- **公式**:\n 斥力\n\n - k: 斥力系数(`factor` / `coulombDisScale²`)\n - q1,q2: 节点强度(`nodeStrength`)\n - r: 节点间距离\n\n#### 边拉力(Edge Attraction)\n\n- **物理模型**:胡克定律(Hooke's Law)\n- **作用**:模拟边的拉力,使节点沿着边的方向移动,其中 `edgeStrength` 和 `linkDistance` 控制边的“硬度”和长度。\n- **公式**:\n 边拉力\n\n - ka: 边拉力强度(`edgeStrength`)\n - L: 边的长度(`linkDistance`)\n - r: 实际边长度\n\n#### 向心力(Gravity)\n\n- **物理模型**:牛顿万有引力定律(Newton's Universal Law of Gravitational)\n- **作用**:使节点向画布中心或者聚类中心聚集,其中 `gravity` 和 `center` 控制重力强度和中心点位置\n- **公式**:\n 向心力\n\n - G: 万有引力常数(`gravity`)\n - xc: 中心点坐标(`center`)\n - mass: 节点质量(`nodeSize`)\n\n#### 三种力的相互作用\n\n- **物理模型**:力的相互作用,产生加速度\n- **作用**:斥力、边拉力、向心力共同作用,通过加速度叠加影响节点运动,最终达到能量最低状态。\n- **公式**:\n 力的相互作用\n\n### 物理系统\n\n#### 节点运动速度公式\n\n- **公式**:\n 节点运动速度公式\n\n - v: 速度\n - a: 加速度\n - dt: 时间步长(`interval`)\n - damping: 阻尼系数(`damping`)\n\n- **作用**:\n 1. 控制节点移动的稳定性\n 2. 阻尼系数防止系统震荡\n 3. 时间步长影响每次迭代的位移\n\n#### 节点位置公式\n\n- **公式**:\n 节点位置公式\n\n - x: 节点位置\n - v: 节点速度\n - dt: 时间步长(`interval`)\n\n- **作用**:\n 1. 根据速度更新节点位置\n 2. 确保运动连续性\n 3. 通过 `preventOverlap` 防止节点重叠\n\n#### 聚类中心计算\n\n- **公式**:\n 聚类中心计算\n\n - n: 聚类内节点数量\n - (xi​,yi​): 每个节点的位置\n\n- **作用**:\n 1. 计算聚类中心\n 2. 向心力将节点拉向所属聚类中心\n 3. 聚类中心可动态变化\n\n#### 聚类强度计算\n\n- **公式**:\n 聚类强度计算\n\n - s: 聚类强度(`clusterNodeStrength`)\n - xc​: 聚类中心\n\n- **作用**:\n 1. 控制聚类的紧密程度\n 2. 聚类强度越大,聚类越紧凑\n 3. 可根据节点属性动态调整\n\n#### 质量对力的影响\n\n- **公式**:\n 质量对力的影响\n\n - a: 加速度\n - F: 力(斥力、边拉力、向心力)\n - mass: 节点质量\n\n- **作用**:\n 1. 质量大的节点移动较小\n 2. 质量小的节点移动较大\n 3. 通过 `getMass` 可自定义质量计算\n\n#### 能量计算\n\n- **公式**:\n 能量计算\n\n - m: 节点质量\n - v: 节点速度\n\n- **作用**:\n 1. 监控布局收敛情况\n 2. 能量趋近于零时系统趋于稳定\n\n#### 系统收敛条件\n\n- **公式**:\n 系统收敛条件\n\n- **作用**:\n 1. 控制迭代次数\n 2. 移动量小于阈值时停止\n 3. 通过 `distanceThresholdMode` 可选择平均值、最大值或最小值\n\n### 力相互作用图\n\n力作用模拟图\n\n## 配置项\n\n根据上述力导向布局的物理特性,有以下配置项:\n\n### 基础配置\n\n| 属性 | 描述 | 默认值 | 必选 |\n| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ---- |\n| type | 布局类型 | `force` | ✓ |\n| dimensions | 布局维度,2表示2D布局,3表示3D布局 | 2 | |\n| width | 布局宽度 | 画布宽度 | |\n| height | 布局高度 | 画布高度 | |\n| center | 布局的中心点 | 图中心 | |\n| maxIteration | 最大迭代次数,若为 0 则将自动调整 | 0 | |\n| minMovement | 当平均移动距离小于0.4时停止迭代 | 0.4 | |\n| distanceThresholdMode | 移动距离的计算模式:mean: 平均移动距离小于 `minMovement` 时停止迭代;max: 最大移动距离小于时 `minMovement` 时停止迭代;min: 最小移动距离小于时 `minMovement` | `mean` | |\n| maxDistance | 最大距离 | | |\n\n### 力相关配置\n\n#### 斥力配置\n\n| 属性 | 描述 | 默认值 | 必选 |\n| --------------- | ------------------------------------------------------------------ | ------ | ---- |\n| nodeStrength | 节点作用力,正数代表节点之间的引力作用,负数代表节点之间的斥力作用 | 1000 | |\n| factor | 斥力系数,数值越大,斥力越大 | 1 | |\n| coulombDisScale | 库伦系数,斥力的一个系数,数字越大,节点之间的斥力越大 | 0.005 | |\n\n#### 边拉力配置\n\n| 属性 | 描述 | 默认值 | 必选 |\n| ------------ | ------------------------------------------------------------------ | ------ | ---- |\n| edgeStrength | 边的作用力(引力)大小,固定作用力或回调函数动态返回不同边的作用力 | 500 | |\n| linkDistance | 边的长度,固定长度或回调函数动态返回不同边的长度 | 200 | |\n\n#### 向心力配置\n\n| 属性 | 描述 | 默认值 | 必选 |\n| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ | ---- |\n| gravity | 向中心力大小,指所有节点被吸引到 center 的力。数字越大,布局越紧凑 | 10 | |\n| centripetalOptions | 向心力配置,包括叶子节点、离散点、其他节点的向心中心及向心力大小。leaf: 叶子节点向心力;single: 单点向心力;others: 其他节点向心力;center: 自定义中心点函数 | [0, 0] | |\n\n#### 聚类配置\n\n| 属性 | 描述 | 默认值 | 必选 |\n| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---- |\n| clustering | 是否需要全部节点聚类,若为 `true`,将使用 `nodeClusterBy` 配置的节点数据中的字段作为聚类依据。 `centripetalOptions.single`、`centripetalOptions.leaf`、`centripetalOptions.others` 将使用 `getClusterNodeStrength` 返回值;`leaf`、`centripetalOptions.center` 将使用当前节点所属聚类中所有节点的平均中心 | `false` | |\n| nodeClusterBy | 指定节点数据中的字段名称作为节点聚类的依据,`clustering` 为 true 时生效,自动生成 `centripetalOptions`,可配合 `clusterNodeStrength` 使用 | | |\n| clusterNodeStrength | 配合 `clustering` 和 `nodeClusterBy` 使用,指定聚类向心力的大小 | | |\n| leafCluster | 是否需要叶子节点聚类,若为 `true`,则 `centripetalOptions.single` 将为 100;`centripetalOptions.leaf` 将使用 `getClusterNodeStrength` 返回值;`getClusterNodeStrength.center` 将为叶子节点返回当前所有叶子节点的平均中心 | false | |\n\n#### 性能与优化配置\n\n| 属性 | 描述 | 默认值 | 必选 |\n| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---- |\n| damping | 阻尼系数,取值范围 [0, 1]。数字越大,速度降低得越慢 | 0.9 | |\n| maxSpeed | 一次迭代的最大移动长度 | 200 | |\n| interval | 控制每个迭代节点的移动速度 | 0.02 | |\n| preventOverlap | 是否防止重叠,必须配合下面属性 `nodeSize` 或节点数据中的 `data.size` 属性,只有在数据中设置了 `data.size` 或在该布局中配置了与当前图节点大小相同的 `nodeSize` 值,才能够进行节点重叠的碰撞检测 | true | |\n| nodeSize | 节点大小(直径)。用于防止节点重叠时的碰撞检测,固定大小或者回调函数动态返回节点大小 | | |\n| nodeSpacing | `preventOverlap` 为 `true` 时生效, 防止重叠时节点边缘间距的最小值。可以是回调函数, 为不同节点设置不同的最小间距 | | |\n| collideStrength | 防止重叠的力强度,范围 [0, 1] | 1 | |\n\n#### 其他配置\n\n| 属性 | 描述 | 默认值 | 必选 |\n| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---- |\n| getMass | 获取节点质量的回调函数,入参为节点内部流转数据,用于计算节点质量大小 | | |\n| getCenter | 每个节点中心力的 x、y、强度的回调函数,若不指定,则没有额外中心力 | | |\n| onTick | 每一次迭代的回调函数 | | |\n| monitor | 每个迭代的监控信息回调,energy 表示布局的收敛能量。若配置可能带来额外的计算能量性能消耗,不配置则不计算。入参为迭代监控信息 `{ energy: number; nodes: Node[]; edges: Edge[]; iterations: number; }` | | |\n\n## 代码示例\n\n### 基础用法\n\n```js\nconst graph = new Graph({\n container: 'container',\n layout: {\n type: 'force',\n // 防止节点重叠\n preventOverlap: true,\n // 节点大小\n nodeSize: 20,\n // 布局宽度\n width: 800,\n // 布局高度\n height: 600,\n },\n});\n```\n\n### 防止节点重叠\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'force',\n // 防止节点重叠\n preventOverlap: true,\n // 节点大小\n nodeSize: 20,\n },\n});\n```\n\n### 力导向布局\n\n该示例展示了如何使用力导向布局创建一个基础的力导向图。\n\n```js\nimport { Graph, NodeEvent } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', label: 'Node 1', size: 30 },\n { id: 'node2', label: 'Node 2', size: 20 },\n { id: 'node3', label: 'Node 3', size: 20 },\n { id: 'node4', label: 'Node 4', size: 20 },\n { id: 'node5', label: 'Node 5', size: 30 },\n { id: 'node6', label: 'Node 6', size: 20 },\n ],\n edges: [\n { source: 'node1', target: 'node2' },\n { source: 'node1', target: 'node3' },\n { source: 'node2', target: 'node4' },\n { source: 'node3', target: 'node4' },\n { source: 'node4', target: 'node5' },\n { source: 'node5', target: 'node6' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n autoFit: 'view',\n modes: {\n default: ['drag-canvas', 'zoom-canvas'],\n },\n layout: {\n type: 'force',\n // 防止节点重叠\n preventOverlap: true,\n // 节点大小\n nodeSize: 20,\n // 向心力\n gravity: 0.9,\n // 迭代次数\n iterations: 100,\n },\n node: {\n style: {\n size: (d) => d.size,\n fill: '#9EC9FF',\n stroke: '#69C8FF',\n label: (d) => d.label,\n labelPlacement: 'center',\n labelFill: '#333',\n },\n },\n edge: {\n style: {\n stroke: '#e2e2e2',\n },\n },\n});\n\ngraph.on(NodeEvent.CLICK, async (e) => {\n const nodeId = e.target.id;\n graph.updateNodeData([{ id: nodeId, size: 200 }]);\n await graph.render();\n});\n\ngraph.render();\n```\n\n主要配置说明:\n\n- `preventOverlap`: 开启节点重叠检测\n- `nodeSize`: 设置节点大小\n- `gravity`: 设置节点向心力\n- `iterations`: 设置布局计算的精确程度\n\n还可以参考 [查看示例](https://g6.antv.antgroup.com/examples/layout/force-directed/#force) 获取更多用法。\n\n\n\n---\ntitle: Fruchterman Force-directed Layout\norder: 14\n---\n\n## Overview\n\nThe Fruchterman layout is a force-directed layout based on the algorithm from [Graph Drawing by Force-directed Placement](https://www.mathe2.uni-bayreuth.de/axel/papers/reingold:graph_drawing_by_force_directed_placement.pdf). By flexibly configuring parameters to simulate physical forces, the layout automatically reaches a stable equilibrium state with minimal energy. It supports both basic uniform distribution and cluster layouts. See more Fruchterman force-directed layout [examples](/en/examples#layout-fruchterman) and [source code](https://github.com/antvis/layout/blob/v5/packages/layout/src/fruchterman.ts).\n\n## Use Cases\n\n- Basic uniform distribution: Suitable for displaying network graphs with evenly distributed nodes and clear overall structure, such as network topology and knowledge graphs.\n- Cluster layout: Suitable for visualizing data with internal aggregation or grouping, such as community structure display and association group analysis.\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------- | -------- |\n| type | Layout type | `'fruchterman'` | - | ✓ |\n| height | Layout height | `number` | container height | |\n| width | Layout width | `number` | container width | |\n| maxIteration | Maximum number of iterations | `number` | 500 | |\n| minMovement | Convergence threshold | `number` | 0.4 | |\n| gravity | Central force, i.e., the force attracting all nodes to the [center](https://github.com/antvis/layout/blob/v5/packages/layout/src/types.ts#L915). The larger the value, the more compact the layout | `number` | 10 | |\n| speed | Node movement speed per iteration. Too high a speed may cause strong oscillation | `number` | 5 | |\n| onTick | Callback for each iteration | (data: [LayoutMapping](https://github.com/antvis/layout/blob/v5/packages/layout/src/types.ts#L69)) => void | - | |\n\n### Cluster Layout\n\n| Property | Description | Type | Default | Required |\n| -------------- | ----------------------------------------------------------------------------------------- | --------- | ----------- | -------- |\n| clustering | Whether to use cluster layout | `boolean` | `false` | |\n| nodeClusterBy | Field name in node data for clustering, effective when `clustering` is true | `string` | `'cluster'` | |\n| clusterGravity | Gravity within clusters, affects cluster compactness, effective when `clustering` is true | `number` | 10 | |\n\n## Example Code\n\n### Basic Layout\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 500,\n height: 250,\n data: {\n nodes: [\n { id: '0' },\n { id: '1' },\n { id: '2' },\n { id: '3' },\n { id: '4' },\n { id: '5' },\n { id: '6' },\n { id: '7' },\n { id: '8' },\n { id: '9' },\n { id: '10' },\n ],\n edges: [\n { source: '0', target: '1' },\n { source: '0', target: '2' },\n { source: '0', target: '3' },\n { source: '0', target: '4' },\n { source: '0', target: '7' },\n { source: '0', target: '8' },\n { source: '0', target: '9' },\n { source: '0', target: '10' },\n { source: '2', target: '3' },\n { source: '4', target: '5' },\n { source: '4', target: '6' },\n { source: '5', target: '6' },\n { source: '9', target: '10' },\n ],\n },\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n },\n },\n layout: {\n type: 'fruchterman',\n gravity: 5,\n speed: 5,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n
Show full code\n\n```javascript\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: '0' },\n { id: '1' },\n { id: '2' },\n { id: '3' },\n { id: '4' },\n { id: '5' },\n { id: '6' },\n { id: '7' },\n { id: '8' },\n { id: '9' },\n { id: '10' },\n ],\n edges: [\n { source: '0', target: '1' },\n { source: '0', target: '2' },\n { source: '0', target: '3' },\n { source: '0', target: '4' },\n { source: '0', target: '7' },\n { source: '0', target: '8' },\n { source: '0', target: '9' },\n { source: '0', target: '10' },\n { source: '2', target: '3' },\n { source: '4', target: '5' },\n { source: '4', target: '6' },\n { source: '5', target: '6' },\n { source: '9', target: '10' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n },\n },\n layout: {\n type: 'fruchterman',\n gravity: 5,\n speed: 5,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n
\n\n### Cluster Layout\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 500,\n height: 250,\n data: {\n nodes: [\n { id: '0', data: { cluster: 'a' } },\n { id: '1', data: { cluster: 'a' } },\n { id: '2', data: { cluster: 'a' } },\n { id: '3', data: { cluster: 'a' } },\n { id: '4', data: { cluster: 'a' } },\n { id: '5', data: { cluster: 'b' } },\n { id: '6', data: { cluster: 'b' } },\n { id: '7', data: { cluster: 'b' } },\n { id: '8', data: { cluster: 'c' } },\n { id: '9', data: { cluster: 'c' } },\n { id: '10', data: { cluster: 'c' } },\n ],\n edges: [\n { source: '0', target: '1' },\n { source: '0', target: '2' },\n { source: '0', target: '4' },\n { source: '0', target: '6' },\n { source: '2', target: '3' },\n { source: '2', target: '4' },\n { source: '3', target: '4' },\n { source: '5', target: '6' },\n { source: '6', target: '7' },\n { source: '7', target: '8' },\n { source: '8', target: '9' },\n { source: '8', target: '10' },\n ],\n },\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => `${d.data.cluster}-${d.id}`,\n },\n palette: {\n type: 'group',\n field: 'cluster',\n },\n },\n edge: {\n style: {\n endArrow: true,\n },\n },\n layout: {\n type: 'fruchterman',\n gravity: 6,\n speed: 5,\n\n // Cluster layout parameters\n clustering: true,\n nodeClusterBy: 'cluster',\n clusterGravity: 3,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n
Show full code\n\n```javascript\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: '0', data: { cluster: 'a' } },\n { id: '1', data: { cluster: 'a' } },\n { id: '2', data: { cluster: 'a' } },\n { id: '3', data: { cluster: 'a' } },\n { id: '4', data: { cluster: 'a' } },\n { id: '5', data: { cluster: 'b' } },\n { id: '6', data: { cluster: 'b' } },\n { id: '7', data: { cluster: 'b' } },\n { id: '8', data: { cluster: 'c' } },\n { id: '9', data: { cluster: 'c' } },\n { id: '10', data: { cluster: 'c' } },\n ],\n edges: [\n { source: '0', target: '1' },\n { source: '0', target: '2' },\n { source: '0', target: '4' },\n { source: '0', target: '6' },\n { source: '2', target: '3' },\n { source: '2', target: '4' },\n { source: '3', target: '4' },\n { source: '5', target: '6' },\n { source: '6', target: '7' },\n { source: '7', target: '8' },\n { source: '8', target: '9' },\n { source: '8', target: '10' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => `${d.data.cluster}-${d.id}`,\n },\n palette: {\n type: 'group',\n field: 'cluster',\n },\n },\n edge: {\n style: {\n endArrow: true,\n },\n },\n layout: {\n type: 'fruchterman',\n gravity: 6,\n speed: 5,\n\n // Cluster layout parameters\n clustering: true,\n nodeClusterBy: 'cluster',\n clusterGravity: 3,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n
\n\n\n\n---\ntitle: Fruchterman 力导向布局\norder: 14\n---\n\n## 概述\n\nFruchterman 布局是基于 [Graph Drawing by Force-directed Placement](https://www.mathe2.uni-bayreuth.de/axel/papers/reingold:graph_drawing_by_force_directed_placement.pdf) 算法实现的一种力导向布局,通过灵活的参数配置模拟物理作用,使整个布局自动达到能量最小的稳定平衡状态,支持基础均匀分布和聚类布局。参考更多 Fruchterman 力导向布局[样例](/examples#layout-fruchterman)和[源码](https://github.com/antvis/layout/blob/v5/packages/layout/src/fruchterman.ts)\n\n## 使用场景\n\n- 基础均匀分布: 适用于展示节点均匀分布,整体结构清晰的网络关系图, 比如网络拓扑、知识图谱。\n- 聚类布局: 适用于具有内部聚合特性或分组的数据可视化展示, 比如社区结构展示、关联组分析。\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -------- | ---- |\n| type | 布局类型 | `'fruchterman'` | - | ✓ |\n| height | 布局的高度 | `number` | 容器高度 | |\n| width | 布局的宽度 | `number` | 容器宽度 | |\n| maxIteration | 最大迭代次数 | `number` | 500 | |\n| minMovement | 收敛阈值 | `number` | 0.4 | |\n| gravity | 中心力大小,指所有节点被吸引到 [center](https://github.com/antvis/layout/blob/v5/packages/layout/src/types.ts#L915) 的力。数字越大,布局越紧凑 | `number` | 10 | |\n| speed | 每次迭代节点移动的速度。速度太快可能会导致强烈震荡 | `number` | 5 | |\n| onTick | 每一次迭代的回调函数 | (data: [LayoutMapping](https://github.com/antvis/layout/blob/v5/packages/layout/src/types.ts#L69)) => void | - | |\n\n### 聚类布局\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | -------------------------------------------------------------------------- | --------- | ----------- | ---- |\n| clustering | 是否按照聚类布局 | `boolean` | `false` | |\n| nodeClusterBy | 聚类布局依据的节点数据 `data` 中的字段名,在 `clustering` 为 `true` 时生效 | `string` | `'cluster'` | |\n| clusterGravity | 聚类内部的重力大小,影响聚类的紧凑程度,在 `clustering` 为 `true` 时生效 | `number` | 10 | |\n\n## 示例代码\n\n### 基本布局\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 500,\n height: 250,\n data: {\n nodes: [\n { id: '0' },\n { id: '1' },\n { id: '2' },\n { id: '3' },\n { id: '4' },\n { id: '5' },\n { id: '6' },\n { id: '7' },\n { id: '8' },\n { id: '9' },\n { id: '10' },\n ],\n edges: [\n { source: '0', target: '1' },\n { source: '0', target: '2' },\n { source: '0', target: '3' },\n { source: '0', target: '4' },\n { source: '0', target: '7' },\n { source: '0', target: '8' },\n { source: '0', target: '9' },\n { source: '0', target: '10' },\n { source: '2', target: '3' },\n { source: '4', target: '5' },\n { source: '4', target: '6' },\n { source: '5', target: '6' },\n { source: '9', target: '10' },\n ],\n },\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n },\n },\n layout: {\n type: 'fruchterman',\n gravity: 5,\n speed: 5,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n
展开查看完整代码\n\n```javascript\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: '0' },\n { id: '1' },\n { id: '2' },\n { id: '3' },\n { id: '4' },\n { id: '5' },\n { id: '6' },\n { id: '7' },\n { id: '8' },\n { id: '9' },\n { id: '10' },\n ],\n edges: [\n { source: '0', target: '1' },\n { source: '0', target: '2' },\n { source: '0', target: '3' },\n { source: '0', target: '4' },\n { source: '0', target: '7' },\n { source: '0', target: '8' },\n { source: '0', target: '9' },\n { source: '0', target: '10' },\n { source: '2', target: '3' },\n { source: '4', target: '5' },\n { source: '4', target: '6' },\n { source: '5', target: '6' },\n { source: '9', target: '10' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n },\n },\n layout: {\n type: 'fruchterman',\n gravity: 5,\n speed: 5,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n
\n\n### 聚类布局\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 500,\n height: 250,\n data: {\n nodes: [\n { id: '0', data: { cluster: 'a' } },\n { id: '1', data: { cluster: 'a' } },\n { id: '2', data: { cluster: 'a' } },\n { id: '3', data: { cluster: 'a' } },\n { id: '4', data: { cluster: 'a' } },\n { id: '5', data: { cluster: 'b' } },\n { id: '6', data: { cluster: 'b' } },\n { id: '7', data: { cluster: 'b' } },\n { id: '8', data: { cluster: 'c' } },\n { id: '9', data: { cluster: 'c' } },\n { id: '10', data: { cluster: 'c' } },\n ],\n edges: [\n { source: '0', target: '1' },\n { source: '0', target: '2' },\n { source: '0', target: '4' },\n { source: '0', target: '6' },\n { source: '2', target: '3' },\n { source: '2', target: '4' },\n { source: '3', target: '4' },\n { source: '5', target: '6' },\n { source: '6', target: '7' },\n { source: '7', target: '8' },\n { source: '8', target: '9' },\n { source: '8', target: '10' },\n ],\n },\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => `${d.data.cluster}-${d.id}`,\n },\n palette: {\n type: 'group',\n field: 'cluster',\n },\n },\n edge: {\n style: {\n endArrow: true,\n },\n },\n layout: {\n type: 'fruchterman',\n gravity: 6,\n speed: 5,\n\n // 聚类布局参数\n clustering: true,\n nodeClusterBy: 'cluster',\n clusterGravity: 3,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n
展开查看完整代码\n\n```javascript\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: '0', data: { cluster: 'a' } },\n { id: '1', data: { cluster: 'a' } },\n { id: '2', data: { cluster: 'a' } },\n { id: '3', data: { cluster: 'a' } },\n { id: '4', data: { cluster: 'a' } },\n { id: '5', data: { cluster: 'b' } },\n { id: '6', data: { cluster: 'b' } },\n { id: '7', data: { cluster: 'b' } },\n { id: '8', data: { cluster: 'c' } },\n { id: '9', data: { cluster: 'c' } },\n { id: '10', data: { cluster: 'c' } },\n ],\n edges: [\n { source: '0', target: '1' },\n { source: '0', target: '2' },\n { source: '0', target: '4' },\n { source: '0', target: '6' },\n { source: '2', target: '3' },\n { source: '2', target: '4' },\n { source: '3', target: '4' },\n { source: '5', target: '6' },\n { source: '6', target: '7' },\n { source: '7', target: '8' },\n { source: '8', target: '9' },\n { source: '8', target: '10' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => `${d.data.cluster}-${d.id}`,\n },\n palette: {\n type: 'group',\n field: 'cluster',\n },\n },\n edge: {\n style: {\n endArrow: true,\n },\n },\n layout: {\n type: 'fruchterman',\n gravity: 6,\n speed: 5,\n\n // 聚类布局参数\n clustering: true,\n nodeClusterBy: 'cluster',\n clusterGravity: 3,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n
\n\n\n\n---\ntitle: Grid Layout\norder: 15\n---\n\n## Overview\n\nThe grid layout arranges nodes in a grid pattern, suitable for scenarios where nodes need to be arranged neatly. This layout supports automatic calculation of the number of rows and columns, or you can specify them manually. It also supports preventing node overlap.\n\n## Use Cases\n\n- Visualizing data in a matrix or table format\n\n## Online Demo\n\n```js | ob { pin: false }\ncreateGraph(\n {\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n autoFit: 'view',\n node: {\n style: {\n size: 20,\n label: true,\n labelText: (datum) => datum.id,\n labelBackground: true,\n icon: false,\n },\n palette: {\n type: 'group',\n field: (datum) => datum.data.value,\n color: ['#1783FF', '#00C9C9', '#F08F56', '#D580FF'],\n },\n },\n edge: {\n style: {\n stroke: '#bfbfbf',\n },\n },\n behaviors: ['drag-canvas'],\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n width: 400,\n height: 400,\n preventOverlap: true,\n nodeSize: 30,\n condense: false,\n },\n },\n { width: 600, height: 400 },\n (gui, graph) => {\n const options = {\n type: 'grid',\n cols: 5,\n rows: 5,\n width: 400,\n height: 400,\n preventOverlap: true,\n nodeSize: 30,\n condense: false,\n };\n\n const optionFolder = gui.addFolder('Grid Layout Options');\n optionFolder.add(options, 'type').disable(true);\n optionFolder.add(options, 'cols', 2, 10, 1);\n optionFolder.add(options, 'rows', 2, 10, 1);\n optionFolder.add(options, 'width', 200, 600, 50);\n optionFolder.add(options, 'height', 200, 600, 50);\n optionFolder.add(options, 'preventOverlap');\n optionFolder.add(options, 'nodeSize', 10, 50, 5);\n optionFolder.add(options, 'condense');\n\n optionFolder.onChange(({ property, value }) => {\n graph.setLayout({\n type: 'grid',\n [property]: value,\n });\n graph.layout();\n });\n },\n);\n```\n\n## Configuration\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'grid',\n begin: [0, 0],\n cols: 5,\n rows: 5,\n width: 300,\n height: 300,\n preventOverlap: true,\n nodeSize: 30,\n condense: false,\n },\n});\n```\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| --------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | --------- | -------- |\n| type | Layout type | `grid` | - | ✓ |\n| begin | Grid start position (top-left corner), default is `[0, 0]` | [number, number] | [0, 0] | |\n| cols | Number of columns. If undefined, the algorithm calculates it automatically based on node count, layout space, and rows (if set) | number | undefined | |\n| rows | Number of rows. If undefined, the algorithm calculates it automatically based on node count, layout space, and cols (if set) | number | 10 | |\n| width | Layout area width. In G6, the container width is used as the default value | number | 300 | |\n| height | Layout area height. In G6, the container height is used as the default value | number | 300 | |\n| condense | If false, uses all available canvas space; if true, uses the minimum canvas space | boolean | false | |\n| nodeSize | Node size (diameter), used for collision detection when preventing overlap | Size \\| ((nodeData: Node) => Size) | - | |\n| nodeSpacing | Node spacing, used to adjust the gap between nodes | ((node?: Node) => number) \\| number | - | |\n| position | Specify the row and column for each node | (node?: Node) => { row?: number; col?: number; } | undefined | |\n| preventOverlap | Whether to prevent node overlap. Requires nodeSize or size property in node data | boolean | false | |\n| preventOverlapPadding | Padding when preventing overlap. Effective when preventOverlap is true | number | 10 | |\n| sortBy | Sort basis (node property name). Higher values are placed more centrally. If undefined, degree is used for sorting | string | undefined | |\n\n### preventOverlap\n\n> _boolean_ **Default:** `false`\n\nWhether to prevent overlap\n\nMust be used with nodeSize or the size property in node data. Only when data has data.size or nodeSize is set in the layout, collision detection for node overlap can be performed.\n\n### preventOverlapPadding\n\n> _number_ **Default:** `10`\n\nPadding when preventing overlap. Effective when preventOverlap is true.\n\n### sortBy\n\n> _string_ **Default:** `undefined`\n\nSort basis (node property name). Higher values are placed more centrally. If undefined, degree is used for sorting. In G6, the container width is used as the default value for grid layout width. When used alone, the default is 300.\n\n## Code Examples\n\n### Basic Usage\n\nThe simplest configuration:\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n },\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n});\n```\n\nResult:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n },\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n node: {\n style: {\n size: 20,\n label: true,\n labelText: (datum) => datum.id,\n labelBackground: true,\n },\n },\n edge: {\n style: {\n stroke: '#bfbfbf',\n },\n },\n});\n\ngraph.render();\n```\n\n### Custom Configuration\n\nYou can customize the grid layout in various ways:\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'grid',\n begin: [50, 50], // Start layout from [50, 50]\n cols: 4, // 4 columns\n rows: 6, // 6 rows\n width: 400, // Layout area width\n height: 600, // Layout area height\n preventOverlap: true, // Prevent node overlap\n nodeSize: 30, // Node size\n condense: true, // Use minimum space\n sortBy: 'value', // Sort by value property\n },\n data: {\n nodes: Array.from({ length: 24 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100, // Property for sorting\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 24)}`,\n target: `node-${Math.floor(Math.random() * 24)}`,\n })),\n },\n});\n```\n\nResult:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n layout: {\n type: 'grid',\n begin: [50, 50],\n cols: 4,\n rows: 6,\n width: 400,\n height: 600,\n preventOverlap: true,\n nodeSize: 30,\n condense: true,\n sortBy: 'value',\n },\n data: {\n nodes: Array.from({ length: 24 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 24)}`,\n target: `node-${Math.floor(Math.random() * 24)}`,\n })),\n },\n node: {\n style: {\n size: 20,\n label: true,\n labelText: (datum) => datum.id,\n labelBackground: true,\n },\n palette: {\n type: 'group',\n field: (datum) => datum.data.value,\n color: ['#1783FF', '#00C9C9', '#F08F56', '#D580FF'],\n },\n },\n edge: {\n style: {\n stroke: '#bfbfbf',\n },\n },\n});\n\ngraph.render();\n```\n\n### Specify Node Position\n\nYou can specify the position for specific nodes using the `position` property:\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n position: (node) => {\n // Specify position for specific nodes\n if (node.id === 'node-0') return { row: 0, col: 0 }; // Top-left\n if (node.id === 'node-1') return { row: 0, col: 4 }; // Top-right\n if (node.id === 'node-2') return { row: 4, col: 0 }; // Bottom-left\n if (node.id === 'node-3') return { row: 4, col: 4 }; // Bottom-right\n return undefined; // Other nodes are auto-arranged\n },\n },\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n});\n```\n\nResult:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n position: (node) => {\n if (node.id === 'node-0') return { row: 0, col: 0 };\n if (node.id === 'node-1') return { row: 0, col: 4 };\n if (node.id === 'node-2') return { row: 4, col: 0 };\n if (node.id === 'node-3') return { row: 4, col: 4 };\n return undefined;\n },\n },\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n node: {\n style: {\n size: 20,\n label: true,\n labelText: (datum) => datum.id,\n labelBackground: true,\n },\n },\n edge: {\n style: {\n stroke: '#bfbfbf',\n },\n },\n});\n\ngraph.render();\n```\n\n## Real Cases\n\n- [Grid Layout](/en/examples/layout/grid/#basic)\n\n\n\n---\ntitle: 网格布局 Grid\norder: 15\n---\n\n## 概述\n\n网格布局将节点按照网格形式排列,适用于需要整齐排列节点的场景。该布局支持自动计算行列数,也可以手动指定行列数,并支持防止节点重叠。\n\n## 使用场景\n\n- 在数据可视化中需要展示矩阵或表格形式的数据关系\n\n## 在线体验\n\n```js | ob { pin: false }\ncreateGraph(\n {\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n autoFit: 'view',\n node: {\n style: {\n size: 20,\n label: true,\n labelText: (datum) => datum.id,\n labelBackground: true,\n icon: false,\n },\n palette: {\n type: 'group',\n field: (datum) => datum.data.value,\n color: ['#1783FF', '#00C9C9', '#F08F56', '#D580FF'],\n },\n },\n edge: {\n style: {\n stroke: '#bfbfbf',\n },\n },\n behaviors: ['drag-canvas'],\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n width: 400,\n height: 400,\n preventOverlap: true,\n nodeSize: 30,\n condense: false,\n },\n },\n { width: 600, height: 400 },\n (gui, graph) => {\n const options = {\n type: 'grid',\n cols: 5,\n rows: 5,\n width: 400,\n height: 400,\n preventOverlap: true,\n nodeSize: 30,\n condense: false,\n };\n\n const optionFolder = gui.addFolder('Grid Layout Options');\n optionFolder.add(options, 'type').disable(true);\n optionFolder.add(options, 'cols', 2, 10, 1);\n optionFolder.add(options, 'rows', 2, 10, 1);\n optionFolder.add(options, 'width', 200, 600, 50);\n optionFolder.add(options, 'height', 200, 600, 50);\n optionFolder.add(options, 'preventOverlap');\n optionFolder.add(options, 'nodeSize', 10, 50, 5);\n optionFolder.add(options, 'condense');\n\n optionFolder.onChange(({ property, value }) => {\n graph.setLayout({\n type: 'grid',\n [property]: value,\n });\n graph.layout();\n });\n },\n);\n```\n\n## 配置方式\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'grid',\n begin: [0, 0],\n cols: 5,\n rows: 5,\n width: 300,\n height: 300,\n preventOverlap: true,\n nodeSize: 30,\n condense: false,\n },\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | --------- | ---- |\n| type | 布局类型 | `grid` | - | ✓ |\n| begin | 网格开始位置(左上角),默认为 `[0, 0]` | [number, number] | [0, 0] | |\n| cols | 网格的列数,为 undefined 时算法根据节点数量、布局空间、rows(若指定)自动计算 | number | undefined | |\n| rows | 网格的行数,为 undefined 时算法根据节点数量、布局空间、cols(若指定)自动计算 | number | 10 | |\n| width | 布局区域宽度,在 G6 中使用当前容器的宽度作为默认值 | number | 300 | |\n| height | 布局区域高度,在 G6 中使用当前容器的高度作为默认值 | number | 300 | |\n| condense | 为 false 时表示利用所有可用画布空间,为 true 时表示利用最小的画布空间 | boolean | false | |\n| nodeSize | 节点大小(直径),用于防止节点重叠时的碰撞检测 | Size \\| ((nodeData: Node) => Size) | - | |\n| nodeSpacing | 节点间距,用于调整节点之间的间隔 | ((node?: Node) => number) \\| number | - | |\n| position | 指定每个节点所在的行和列 | (node?: Node) => { row?: number; col?: number; } | undefined | |\n| preventOverlap | 是否防止节点重叠,需要配合 nodeSize 或节点数据中的 size 属性使用 | boolean | false | |\n| preventOverlapPadding | 避免重叠时节点的间距 padding,preventOverlap 为 true 时生效 | number | 10 | |\n| sortBy | 指定排序的依据(节点属性名),数值越高则该节点被放置得越中心。若为 undefined,则会计算节点的度数,度数越高,节点将被放置得越中心 | string | undefined | |\n\n### preventOverlap\n\n> _boolean_ **Default:** `false`\n\n是否防止重叠\n\n必须配合下面属性 nodeSize 或节点数据中的 data.size 属性,只有在数据中设置了 data.size 或在该布局中配置了与当前图节点大小相同的 nodeSize 值,才能够进行节点重叠的碰撞检测\n\n### preventOverlapPadding\n\n> _number_ **Default:** `10`\n\n避免重叠时节点的间距 padding,preventOverlap 为 true 时生效\n\n### sortBy\n\n> _string_ **Default:** `undefined`\n\n指定排序的依据(节点属性名),数值越高则该节点被放置得越中心。若为 undefined,则会计算节点的度数,度数越高,节点将被放置得越中心\n\n在 G6 中使用当前容器的宽度作为 grid 布局 width 的默认值。单独使用此布局时默认值为 300\n\n## 代码示例\n\n### 基础用法\n\n最简单的配置方式:\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n },\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n },\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n node: {\n style: {\n size: 20,\n label: true,\n labelText: (datum) => datum.id,\n labelBackground: true,\n },\n },\n edge: {\n style: {\n stroke: '#bfbfbf',\n },\n },\n});\n\ngraph.render();\n```\n\n### 自定义配置\n\n可以通过多种方式自定义网格布局:\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'grid',\n begin: [50, 50], // 从坐标 [50, 50] 开始布局\n cols: 4, // 指定 4 列\n rows: 6, // 指定 6 行\n width: 400, // 布局区域宽度\n height: 600, // 布局区域高度\n preventOverlap: true, // 防止节点重叠\n nodeSize: 30, // 节点大小\n condense: true, // 使用最小空间\n sortBy: 'value', // 按 value 属性排序\n },\n data: {\n nodes: Array.from({ length: 24 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100, // 用于排序的属性\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 24)}`,\n target: `node-${Math.floor(Math.random() * 24)}`,\n })),\n },\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n layout: {\n type: 'grid',\n begin: [50, 50],\n cols: 4,\n rows: 6,\n width: 400,\n height: 600,\n preventOverlap: true,\n nodeSize: 30,\n condense: true,\n sortBy: 'value',\n },\n data: {\n nodes: Array.from({ length: 24 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 24)}`,\n target: `node-${Math.floor(Math.random() * 24)}`,\n })),\n },\n node: {\n style: {\n size: 20,\n label: true,\n labelText: (datum) => datum.id,\n labelBackground: true,\n },\n palette: {\n type: 'group',\n field: (datum) => datum.data.value,\n color: ['#1783FF', '#00C9C9', '#F08F56', '#D580FF'],\n },\n },\n edge: {\n style: {\n stroke: '#bfbfbf',\n },\n },\n});\n\ngraph.render();\n```\n\n### 指定节点位置\n\n可以通过 `position` 属性为特定节点指定位置:\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n position: (node) => {\n // 为特定节点指定位置\n if (node.id === 'node-0') return { row: 0, col: 0 }; // 左上角\n if (node.id === 'node-1') return { row: 0, col: 4 }; // 右上角\n if (node.id === 'node-2') return { row: 4, col: 0 }; // 左下角\n if (node.id === 'node-3') return { row: 4, col: 4 }; // 右下角\n return undefined; // 其他节点自动布局\n },\n },\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n layout: {\n type: 'grid',\n cols: 5,\n rows: 5,\n position: (node) => {\n if (node.id === 'node-0') return { row: 0, col: 0 };\n if (node.id === 'node-1') return { row: 0, col: 4 };\n if (node.id === 'node-2') return { row: 4, col: 0 };\n if (node.id === 'node-3') return { row: 4, col: 4 };\n return undefined;\n },\n },\n data: {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n },\n node: {\n style: {\n size: 20,\n label: true,\n labelText: (datum) => datum.id,\n labelBackground: true,\n },\n },\n edge: {\n style: {\n stroke: '#bfbfbf',\n },\n },\n});\n\ngraph.render();\n```\n\n## 实际案例\n\n- [Grid布局](/examples/layout/grid/#basic)\n\n\n\n---\ntitle: Indented Tree\norder: 16\n---\n\n# Indented Tree Layout\n\n## Overview\n\nIndented tree layout represents the hierarchy of tree nodes through indentation in the horizontal direction. Each element occupies a row or column, commonly used in file directory structures, organizational charts, and other scenarios. This layout provides a clear structure for displaying hierarchical relationships.\n\nIndented Tree Layout\n\n## Use Cases\n\n- File directory structure visualization\n- Organizational charts\n- Classification system display\n- Tree-like data where hierarchical relationships need to be emphasized\n\n## Configuration Items\n\n> IndentedLayout supports common layout configuration items and specific configuration items, as shown below.\n\n| Property | Description | Type | Default | Required |\n| ---------------------- | ------------------------------------------------------------------- | ------------------------------- | ---------- | -------- |\n| type | Layout type, must be 'indented' | 'indented' | - | ✓ |\n| direction | Layout direction, see details below | 'LR' \\| 'RL' \\| 'H' | 'LR' | |\n| indent | Column spacing, fixed value or function | number \\| (d?: Node) => number | 20 | |\n| getWidth | Get each node's width, effective when direction='H' | (d?: Node) => number | - | |\n| getHeight | Get each node's height | (d?: Node) => number | - | |\n| getSide | Node placement on left/right side of root, overrides direction='H' | (d?: Node) => 'left' \\| 'right' | - | |\n| dropCap | Whether the first child of each node starts on the next line | boolean | true | |\n| isLayoutInvisibleNodes | Whether invisible nodes participate in layout (when preLayout=true) | boolean | false | |\n| nodeFilter | Nodes participating in this layout | (node: NodeData) => boolean | () => true | |\n| preLayout | Use pre-layout, calculate layout before initializing elements | boolean | false | |\n| enableWorker | Whether to run layout in WebWorker | boolean | - | |\n| iterations | Number of iterations for iterative layout | number | - | |\n\n### Complex Type Explanations\n\n- **direction**\n\n - `'LR'`: Root node on the left, layout to the right\n LR\n - `'RL'`: Root node on the right, layout to the left\n RL\n - `'H'`: Root node in the middle, horizontal symmetric layout\n H\n\n- **indent**\n\n - Fixed value: Consistent indentation for all levels\n - Function: (d?: Node) => number, customize indentation based on node\n - Example:\n ```js\n (d) => {\n if (d.parent?.id === 'testId') return d.parent.x + 50;\n return 100;\n };\n ```\n\n- **getWidth/getHeight**\n\n - Used to customize each node's width/height, often for content adaptation\n - Example:\n ```js\n (d) => (d.id === 'testId' ? 50 : 100);\n ```\n\n- **getSide**\n - Specifies which side of the root node a node should be placed, only effective when direction='H'\n - Example:\n ```js\n (d) => (d.id === 'testId' ? 'left' : 'right');\n ```\n\n## Example Code\n\n> For more examples, see [Online Demo](https://g6.antv.antgroup.com/en/examples/layout/indented)\n\n### Automatic Child Node Distribution\n\n\n\n```js\nimport { Graph, treeToGraphData } from '@antv/g6';\n\nfetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n data: treeToGraphData(data),\n autoFit: 'view',\n layout: {\n type: 'indented',\n direction: 'H',\n indent: 80,\n getHeight: () => 16,\n getWidth: () => 32,\n },\n });\n graph.render();\n });\n```\n\n### Right Side Child Node Distribution\n\n\n\n```js\n// ... code as above, layout.direction: 'LR'\n```\n\n### Left Side Child Node Distribution\n\n\n\n```js\n// ... code as above, layout.direction: 'RL'\n```\n\n### Custom Child Node Distribution\n\n\n\n```js\nlayout: {\n type: 'indented',\n direction: 'H',\n indent: 80,\n getHeight: () => 16,\n getWidth: () => 32,\n getSide: (d) => {\n if (d.id === 'Regression' || d.id === 'Classification') return 'left';\n return 'right';\n },\n}\n```\n\n### No Line Break for First Child Node\n\n\n\n```js\nlayout: {\n type: 'indented',\n direction: 'LR',\n indent: 80,\n getHeight: () => 16,\n getWidth: () => 32,\n dropCap: false,\n}\n```\n\n\n\n---\ntitle: 缩进树 Indented\norder: 16\n---\n\n## 概述\n\nIndented(缩进树)布局是一种通过水平方向的缩进量来表示树节点层级的布局方式。每个元素占据一行或一列,常用于文件目录结构、组织架构等场景。该布局结构清晰,便于展示层级关系。\n\nIndented 缩进树布局示意图\n\n## 使用场景\n\n- 文件目录结构可视化\n- 组织架构树\n- 分类体系展示\n- 需要突出层级关系的树状数据\n\n## 配置项\n\n> IndentedLayout 支持通用布局配置项和专有配置项,详见下表。\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ---------------------- | ---------------------------------------------------- | ------------------------------- | ---------- | ---- |\n| type | 布局类型,需为 'indented' | 'indented' | - | ✓ |\n| direction | 布局方向,根节点在左/右/中间,详见下方说明 | 'LR' \\| 'RL' \\| 'H' | 'LR' | |\n| indent | 列间间距,支持固定值或函数 | number \\| (d?: Node) => number | 20 | |\n| getWidth | 获取每个节点宽度,仅 direction='H' 时生效 | (d?: Node) => number | - | |\n| getHeight | 获取每个节点高度 | (d?: Node) => number | - | |\n| getSide | 节点排布在根节点的左/右侧,设置后 direction='H' 失效 | (d?: Node) => 'left' \\| 'right' | - | |\n| dropCap | 每个节点的第一个子节点是否换行 | boolean | true | |\n| isLayoutInvisibleNodes | 不可见节点是否参与布局(preLayout=true 时生效) | boolean | false | |\n| nodeFilter | 参与该布局的节点 | (node: NodeData) => boolean | () => true | |\n| preLayout | 使用前布局,在初始化元素前计算布局 | boolean | false | |\n| enableWorker | 是否在 WebWorker 中运行布局 | boolean | - | |\n| iterations | 迭代布局的迭代次数 | number | - | |\n\n### 复杂类型说明\n\n- **direction**\n\n - `'LR'`:根节点在左,向右布局\n LR\n - `'RL'`:根节点在右,向左布局\n RL\n - `'H'`:根节点在中间,水平对称布局\n H\n\n- **indent**\n\n - 固定数值:所有层级缩进一致\n - 函数:(d?: Node) => number,可根据节点自定义缩进\n - 示例:\n ```js\n (d) => {\n if (d.parent?.id === 'testId') return d.parent.x + 50;\n return 100;\n };\n ```\n\n- **getWidth/getHeight**\n\n - 用于自定义每个节点的宽度/高度,常用于自适应内容\n - 示例:\n ```js\n (d) => (d.id === 'testId' ? 50 : 100);\n ```\n\n- **getSide**\n - 指定节点在根节点的哪一侧,仅 direction='H' 时生效\n - 示例:\n ```js\n (d) => (d.id === 'testId' ? 'left' : 'right');\n ```\n\n## 示例代码\n\n> 更多示例可参考 [在线 Demo](https://g6.antv.antgroup.com/examples/layout/indented)\n\n### 子节点自动分布\n\n\n\n```js\nimport { Graph, treeToGraphData } from '@antv/g6';\n\nfetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n data: treeToGraphData(data),\n autoFit: 'view',\n layout: {\n type: 'indented',\n direction: 'H',\n indent: 80,\n getHeight: () => 16,\n getWidth: () => 32,\n },\n });\n graph.render();\n });\n```\n\n### 子节点右侧分布\n\n\n\n```js\n// ... 代码同上,layout.direction: 'LR'\n```\n\n### 子节点左侧分布\n\n\n\n```js\n// ... 代码同上,layout.direction: 'RL'\n```\n\n### 自定义子节点分布\n\n\n\n```js\nlayout: {\n type: 'indented',\n direction: 'H',\n indent: 80,\n getHeight: () => 16,\n getWidth: () => 32,\n getSide: (d) => {\n if (d.id === 'Regression' || d.id === 'Classification') return 'left';\n return 'right';\n },\n}\n```\n\n### 首子节点不换行\n\n\n\n```js\nlayout: {\n type: 'indented',\n direction: 'LR',\n indent: 80,\n getHeight: () => 16,\n getWidth: () => 32,\n dropCap: false,\n}\n```\n\n\n\n---\ntitle: MDS High-dimensional Data Dimensionality Reduction Layout\norder: 17\n---\n\n# MDS High-dimensional Data Dimensionality Reduction Layout\n\n## Overview\n\nMDS (Multidimensional Scaling) is a classic dimensionality reduction algorithm. In G6, the MDS layout constructs a distance matrix between nodes and restores their relative distances in high-dimensional space as much as possible in 2D space. It is suitable for graph visualization scenarios that show similarity, distance, or structural relationships between nodes.\n\n## Use Cases\n\n- Data dimensionality reduction visualization\n- Displaying distance relationships between nodes\n\n## Configuration\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'mds',\n center: [300, 300],\n linkDistance: 100,\n },\n});\n```\n\n## Options\n\n| Option | Description | Type | Default | Required |\n| :--------------- | :------------------------------------ | :---------------- | :------------ | :------- |\n| **type** | Layout type | `mds` | - | Yes |\n| **center** | Center position of the layout | `[number,number]` | canvas center | No |\n| **linkDistance** | Ideal length of edges (spring length) | `number` | 200 | No |\n\n**center**\n\nThe center coordinates of the layout. All nodes will be symmetrically distributed around this point.\n\n**linkDistance**\n\n> number Default: 50\n\nThe ideal distance between nodes. The larger the value, the more dispersed the nodes.\n\n## Code Example\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 300,\n autoFit: 'view',\n data,\n node: {\n style: {\n labelText: (d) => d.id,\n },\n },\n layout: {\n type: 'mds',\n nodeSize: 32,\n linkDistance: 100,\n },\n behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 高维数据降维布局 MDS\norder: 17\n---\n\n## 概述\n\nMDS(多维尺度分析,Multidimensional Scaling)是一种经典的降维算法。在 G6 中,MDS 布局通过构造节点间的距离矩阵,在二维空间中尽可能还原它们在高维空间中的相对距离。适用于展示节点之间相似度、距离或结构关系的图可视化场景。\n\n## 使用场景\n\n- 数据降维可视化\n- 展示节点之间的距离关系\n\n## 配置方式\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'mds',\n center: [300, 300],\n linkDistance: 100,\n },\n});\n```\n\n## 配置项\n\n| 配置项 | 描述 | 类型 | 默认值 | 必选 |\n| :--------------- | :--------------------------------- | :---------------- | :------- | :--- |\n| **type** | 布局类型 | `mds` | - | 是 |\n| **center** | 布局的中心位置 | `[number,number]` | 画布中心 | 否 |\n| **linkDistance** | 边的理想长度(弹簧未受力时的长度) | `number` | 200 | 否 |\n\n**center**\n\n布局的中心点坐标,所有节点会围绕该点对称分布。\n\n**linkDistance**\n\n> number Default: 50\n\n节点之间的理想距离,越大则节点间距离越分散。\n\n## 代码示例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: Array.from({ length: 25 }, (_, i) => ({\n id: `node-${i}`,\n data: {\n value: Math.random() * 100,\n },\n })),\n edges: Array.from({ length: 20 }, (_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 25)}`,\n target: `node-${Math.floor(Math.random() * 25)}`,\n })),\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 300,\n autoFit: 'view',\n data,\n node: {\n style: {\n labelText: (d) => d.id,\n },\n },\n layout: {\n type: 'mds',\n nodeSize: 32,\n linkDistance: 100,\n },\n behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Mindmap Tree\norder: 18\n---\n\n## Overview\n\nThe mindmap tree layout is suitable for hierarchical layouts of tree structures, supporting expansion on both left and right sides. Nodes at the same depth will be placed on the same layer. Note: the layout **does** take node size into account. See more mindmap layout [examples](/en/examples#layout-mindmap) or [source code](https://github.com/antvis/hierarchy/blob/master/src/mindmap.js).\n\nimg\n\n## Configuration\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'mindmap',\n direction: 'H',\n preLayout: false,\n getHeight: () => 32,\n getWidth: () => 32,\n getVGap: () => 16,\n getHGap: () => 72,\n },\n});\n```\n\n## Options\n\n\"Mindmap\n\n| Property | Description | Type | Default | Required |\n| --------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------- | ------- | -------- |\n| type | Layout type | `mindmap` | - | ✓ |\n| direction | Layout direction, [options](#direction) | `H` \\| `LR` \\| `RL` \\| `TB` \\| `BT` | `LR` | |\n| getHeight | Function to calculate the height of each node | (d?: Node) => number | | ✓ |\n| getWidth | Function to calculate the width of each node | (d?: Node) => number | | ✓ |\n| getVGap | Vertical gap for each node. Note: the actual vertical gap between two nodes is twice the vgap | (d?: Node) => number | | |\n| getHGap | Horizontal gap for each node. Note: the actual horizontal gap between two nodes is twice the hgap | (d?: Node) => number | | |\n| getSide | Set whether the node is placed on the left or right of the root. Only effective when `direction` is `H` | (d?: Node) => string | | |\n\n### direction\n\n> `H` \\| `LR` \\| `RL` \\| `TB` \\| `BT` **Default:** `'LR'`\n\nTree layout direction\n\n- `'H'`: horizontal — The children of the root node are divided into two parts and placed on the left and right sides of the root node. You can pass the `getSide` method to specify the left/right distribution logic for each node. If not provided, the first half will be placed on the right, and the second half on the left by default.\n\nHorizontal layout\n\n- `'LR' | 'TB'`: Children are placed on the right side of the root node.\n\nVertical layout\n\n- `'RL'`: Children are placed on the left side of the root node.\n\nVertical layout\n\n- `BT`: Children are placed on the right side of the root node, then the entire graph is rotated 180° along the X axis.\n\nVertical layout\n\n### getWidth\n\n> _(d?: Node) => number_\n\nWidth of each node\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getHeight\n\n> _(d?: Node) => number_\n\nHeight of each node\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getHGap\n\n> _(d?: Node) => number_\n\nHorizontal gap for each node\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getVGap\n\n> _(d?: Node) => number_\n\nVertical gap for each node\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getSide\n\n> _(d?: Node) => string_\n\nSet whether the node is placed on the left or right of the root. Note: only effective when `direction` is `H`. If not set, the first half of the children will be placed on the right, and the second half on the left by default. See [getSide auto calculation logic](https://github.com/antvis/hierarchy/blob/d786901874f59d96c47e2a5dfe17b373eefd72e3/src/layout/separate-root.js#L11).\n\nExample:\n\n```javascript\n(d) => {\n // d is a node\n if (d.id === 'test-child-id') return 'right';\n return 'left';\n};\n```\n\n### Suitable Scenarios\n\n- Data lineage graph: `direction='H'` is suitable for rendering upstream and downstream lineage of a specified node, with upstream on the left and downstream on the right of the central node.\n- Mind map: Build custom mind map components.\n\n\n\n---\ntitle: 脑图树 Mindmap\norder: 18\n---\n\n## 概述\n\n脑图树布局适用于树状结构的层次化布局,支持左右两侧展开,深度相同的节点将会被放置在同一层。需要注意:布局**会**考虑节点的大小。参考更多脑图布局[样例](/examples#layout-mindmap)或[源码](https://github.com/antvis/hierarchy/blob/master/src/mindmap.js)。\n\nimg\n\n## 配置方式\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'mindmap',\n direction: 'H',\n preLayout: false,\n getHeight: () => 32,\n getWidth: () => 32,\n getVGap: () => 16,\n getHGap: () => 72,\n },\n});\n```\n\n## 配置项\n\n\"脑图树配置项图解\"\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------- | ------ | ---- |\n| type | 布局类型 | `mindmap` | - | ✓ |\n| direction | 布局方向,[可选值](#direction) | `H` \\| `LR` \\| `RL` \\| `TB` \\| `BT` | `LR` | |\n| getHeight | 计算每个节点的高度 | (d?: Node) => number | | ✓ |\n| getWidth | 计算每个节点的宽度 | (d?: Node) => number | | ✓ |\n| getVGap | 每个节点的垂直间隙,注意实际两个节点间的垂直间隙是2倍的vgap | (d?: Node) => number | | |\n| getHGap | 每个节点的水平间隙,注意实际两个节点间的水平间隙是2倍的hgap | (d?: Node) => number | | |\n| getSide | 设置节点排布在根节点的左侧/右侧,如未设置,则算法自动分配左侧/右侧。注意:该参数仅在`H`布局方向上生效 | (d?: Node) => string | | |\n\n### direction\n\n> `H` \\| `LR` \\| `RL` \\| `TB` \\| `BT` **Default:** `'LR'`\n\n树布局的方向\n\n- `'H'`:horizontal(水平)—— 根节点的子节点分成两部分横向放置在根节点左右两侧。可传入`getSide`方法指定每个节点的左右分布逻辑,不传则默认将前半部分放置在右侧,后半部分放置在左侧。\n\n水平布局\n\n- `'LR' | 'TB'`:将子节点排布在根节点的右侧;\n\n竖直布局\n\n- `'RL'`:将子节点排布在根节点的左侧;\n\n竖直布局\n\n- `BT`:将子节点排布在根节点右侧,然后将整个图沿X轴旋转180°;\n\n竖直布局\n\n### getWidth\n\n> _(d?: Node) => number_\n\n每个节点的宽度\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getHeight\n\n> _(d?: Node) => number_\n\n每个节点的高度\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getHGap\n\n> _(d?: Node) => number_\n\n每个节点的水平间隙\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getVGap\n\n> _(d?: Node) => number_\n\n每个节点的垂直间隙\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n if (d.id === 'testId') return 50;\n return 100;\n};\n```\n\n### getSide\n\n> _(d?: Node) => string_\n\n设置节点排布在根节点的左侧/右侧。注意:该参数仅在`direction`为`H`时生效。如未设置,会默认将子节点前半部分放置在右侧,后半部分放置在左侧,参考[getSide自动计算逻辑](https://github.com/antvis/hierarchy/blob/d786901874f59d96c47e2a5dfe17b373eefd72e3/src/layout/separate-root.js#L11)。\n\n示例:\n\n```javascript\n(d) => {\n // d 是一个节点\n if (d.id === 'test-child-id') return 'right';\n return 'left';\n};\n```\n\n### 布局适用场景\n\n- 数据血缘图:`direction='H'`很适合渲染血缘图中查看指定节点的上下游血缘的场景,上游分布在中心节点的左侧,下游分布在右侧;\n- 思维导图:构建自定义的思维导图组件。\n\n\n\n---\ntitle: Layout Overview\norder: 0\n---\n\n## Overview\n\nGraph layout refers to the process of arranging elements in a graph according to certain rules, such as force-directed layout based on charge elasticity models, grid layout with sequential arrangement, and tree layout based on hierarchical structures.\n\n\n\n## Layout Types\n\nG6 provides a variety of layout algorithms, allowing users to choose the appropriate one based on their needs:\n\n- [AntVDagreLayout](/en/manual/layout/antv-dagre-layout): Custom layout based on dagre\n- [CircularLayout](/en/manual/layout/circular-layout): Circular layout\n- [ComboCombinedLayout](/en/manual/layout/combo-combined-layout): Layout suitable for combinations\n- [ConcentricLayout](/en/manual/layout/concentric-layout): Concentric layout\n- [D3Force3DLayout](/en/manual/layout/d3-force3-d-layout): [3D Force-directed](https://github.com/vasturiano/d3-force-3d) layout\n- [D3ForceLayout](/en/manual/layout/d3-force-layout): Force-directed layout based on [D3](https://d3js.org/d3-force)\n- [DagreLayout](/en/manual/layout/dagre-layout): [dagre](https://github.com/dagrejs/dagre) layout\n- [FishboneLayout](/en/manual/layout/fishbone): Fishbone layout\n- [ForceAtlas2Layout](/en/manual/layout/force-atlas2-layout): [ForceAtlas2](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0098679) layout\n- [ForceLayout](/en/manual/layout/force-layout): Force-directed layout\n- [FruchtermanLayout](/en/manual/layout/fruchterman-layout): [Fruchterman](https://www.sciencedirect.com/topics/computer-science/reingold-layout) layout\n- [GridLayout](/en/manual/layout/grid-layout): Grid layout\n- [MDSLayout](/en/manual/layout/mds-layout): High-dimensional data dimensionality reduction layout\n- [RadialLayout](/en/manual/layout/radial-layout): Radial layout\n- [RandomLayout](/en/manual/layout/random-layout): Random layout\n- [SnakeLayout](/en/manual/layout/snake): Snake layout\n- [CompactBoxLayout](/en/manual/layout/compact-box-layout): Compact tree layout\n- [DendrogramLayout](/en/manual/layout/dendrogram-layout): Dendrogram layout\n- [MindmapLayout](/en/manual/layout/mindmap-layout): Mindmap layout\n- [IndentedLayout](/en/manual/layout/indented-layout): Indented tree layout\n\nAmong them, `CompactBox Layout`, `Dendrogram Layout`, `Mindmap Layout`, and `Indented Layout` are types of tree layouts suitable for tree-structured graphs.\n\n## Register Layout\n\nYou can directly use built-in layouts, but if you want to use other layouts, you need to register them first:\n\n```typescript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { CustomLayout } from 'package-name/or/path-to-your-custom-layout';\n\nregister(ExtensionCategory.LAYOUT, 'custom-layout', CustomLayout);\n```\n\n## Configure Layout\n\nThe `layout` configuration item can specify the graph's layout algorithm, for example:\n\n```typescript\n{\n layout: {\n // Specify the layout algorithm to use\n type: 'force',\n // Configuration items for the layout algorithm\n gravity: 10\n // ...\n }\n}\n```\n\nYou can also use `graph.setLayout` to update the layout configuration after the graph is instantiated.\n\nStarting from `5.1`, layout docs align their shared fields with `@antv/layout`. Besides each layout's own algorithm parameters, you should also pay attention to common options such as `width`, `height`, `center`, `enableWorker`, `node`, and `edge`.\n\n## Layout Acceleration\n\nG6 provides accelerated versions for some layout algorithms, including executing layout algorithms in Web Workers, providing [WASM](https://webassembly.org/) versions of layout algorithms, and GPU-accelerated layout algorithms. They can be used as follows:\n\n### Execute Layout Algorithms in Web Workers\n\nExcept for tree layouts, all built-in layout algorithms in G6 support execution in Web Workers. Simply set `enableWorker` to `true`:\n\n```typescript\n{\n layout: {\n type: 'force',\n enableWorker: true,\n // ...\n }\n}\n```\n\n### Use WASM Version Layout Algorithms\n\nCurrently supported WASM version layout algorithms include: `Fruchterman Layout`, `ForceAtlas Layout`, `Force Layout`, `Dagre Layout`.\n\nFirst, install `@antv/layout-wasm`:\n\n```bash\nnpm install @antv/layout-wasm --save\n```\n\nImport and register the layout algorithm:\n\n```typescript\nimport { register, Graph, ExtensionCategory } from '@antv/g6';\nimport { FruchtermanLayout, initThreads, supportsThreads } from '@antv/layout-wasm';\n\nregister(ExtensionCategory.LAYOUT, 'fruchterman-wasm', FruchtermanLayout);\n```\n\nInitialize threads:\n\n```typescript\nconst supported = await supportsThreads();\nconst threads = await initThreads(supported);\n```\n\nInitialize the graph and pass in the layout configuration:\n\n```typescript\nconst graph = new Graph({\n // ... other configurations\n layout: {\n type: 'fruchterman-wasm',\n threads,\n // ... other configurations\n },\n});\n```\n\n### Use GPU-Accelerated Layout\n\nCurrently supported GPU-accelerated layout algorithms include: `Fruchterman Layout`, `GForce Layout`.\n\nFirst, install `@antv/layout-gpu`:\n\n```bash\nnpm install @antv/layout-gpu --save\n```\n\nImport and register the layout algorithm:\n\n```typescript\nimport { register, Graph, ExtensionCategory } from '@antv/g6';\nimport { FruchtermanLayout } from '@antv/layout-gpu';\n\nregister(ExtensionCategory.LAYOUT, 'fruchterman-gpu', FruchtermanLayout);\n```\n\nInitialize the graph and pass in the layout configuration:\n\n```typescript\nconst graph = new Graph({\n // ... other configurations\n layout: {\n type: 'fruchterman-gpu',\n // ... other configurations\n },\n});\n```\n\n## Execute Layout\n\nUsually, after calling `graph.render()`, G6 will automatically execute the layout algorithm.\n\nIf you need to manually execute the layout algorithm, G6 provides the following APIs:\n\n- [layout](/api/layout#graphlayoutlayoutoptions): Execute layout algorithm\n- [setLayout](/api/layout#graphsetlayoutlayout): Set layout algorithm\n- [stopLayout](/api/layout#graphstoplayout): Stop layout algorithm\n\n## Custom Layout\n\nIf the built-in layout algorithms cannot meet your needs, you can customize layout algorithms. For details, please refer to [Custom Layout](/manual/layout/custom-layout).\n\nIf you are migrating layout configuration from G6 `5.0` to `5.1`, see [Upgrade from 5.0 to 5.1 (Layout)](/en/manual/whats-new/upgrade-to-5-1).\n\n\n\n---\ntitle: 布局总览\norder: 0\n---\n\n## 概述\n\n图布局是指将图中的元素按照一定的规则进行排列的过程,例如基于电荷弹性模型的力导向布局、逐次排布的网格布局、基于层次结构的树布局等。\n\n\n\n## 布局类型\n\nG6 提供了多种布局算法,用户可以根据自己的需求选择合适的布局算法:\n\n- [AntVDagreLayout](/manual/layout/antv-dagre-layout):基于 dagre 定制的布局\n- [CircularLayout](/manual/layout/circular-layout):环形布局\n- [ComboCombinedLayout](/manual/layout/combo-combined-layout):适用于存在组合的布局\n- [ConcentricLayout](/manual/layout/concentric-layout):同心圆布局\n- [D3Force3DLayout](/manual/layout/d3-force3-d-layout):[3D 力导向](https://github.com/vasturiano/d3-force-3d)布局\n- [D3ForceLayout](/manual/layout/d3-force-layout):基于 [D3](https://d3js.org/d3-force) 的力导向布局\n- [DagreLayout](/manual/layout/dagre-layout):[dagre](https://github.com/dagrejs/dagre) 布局\n- [FishboneLayout](/manual/layout/fishbone):鱼骨布局\n- [ForceAtlas2Layout](/manual/layout/force-atlas2-layout):[ForceAtlas2](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0098679) 布局\n- [ForceLayout](/manual/layout/force-layout):力导向布局\n- [FruchtermanLayout](/manual/layout/fruchterman-layout):[Fruchterman](https://www.sciencedirect.com/topics/computer-science/reingold-layout) 布局\n- [GridLayout](/manual/layout/grid-layout):网格布局\n- [MDSLayout](/manual/layout/mds-layout):高维数据降维算法布局\n- [RadialLayout](/manual/layout/radial-layout):径向布局\n- [RandomLayout](/manual/layout/random-layout):随机布局\n- [SnakeLayout](/manual/layout/snake):蛇形布局\n- [CompactBoxLayout](/manual/layout/compact-box-layout): 紧凑树布局\n- [DendrogramLayout](/manual/layout/dendrogram-layout): 树状布局\n- [MindmapLayout](/manual/layout/mindmap-layout): 思维导图布局\n- [IndentedLayout](/manual/layout/indented-layout): 缩进树布局\n\n其中 `CompactBox Layout`、`Dendrogram Layout`、`Mindmap Layout`、`Indented Layout` 是树布局的一种,适用于树状结构的图。\n\n## 注册布局\n\n你可以直接使用内置布局,如果想要使用其他布局,需要先进行注册:\n\n```typescript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { CustomLayout } from 'package-name/or/path-to-your-custom-layout';\n\nregister(ExtensionCategory.LAYOUT, 'custom-layout', CustomLayout);\n```\n\n## 配置布局\n\n通过 `layout` 配置项可以指定图的布局算法,例如:\n\n```typescript\n{\n layout: {\n // 指定要使用的布局算法\n type: 'force',\n // 布局算法的配置项\n gravity: 10\n // ...\n }\n}\n```\n\n也可在图实例化之后使用 `graph.setLayout` 来更新布局配置。\n\n5.1 开始,布局文档中的通用字段已与 `@antvis/layout` 对齐。除了各布局自己的算法参数外,也建议同时关注 `width`、`height`、`center`、`enableWorker`、`node`、`edge` 等公共配置。\n\n## 布局加速\n\nG6 对一些布局算法提供了加速版本,包括:在 Web Worker 中执行布局算法、提供 [WASM](https://webassembly.org/) 版本的布局算法、GPU 加速的布局算法等。可按照下列方式使用:\n\n### 在 Web Worker 中执行布局算法\n\n除树布局外,G6 的所有内置布局算法都支持在 Web Worker 中执行。只需将 `enableWorker` 设置为 `true` 即可:\n\n```typescript\n{\n layout: {\n type: 'force',\n enableWorker: true,\n // ...\n }\n}\n```\n\n### 使用 WASM 版本布局算法\n\n目前支持 WASM 版本的布局算法有:`Fruchterman Layout` `ForceAtlas Layout` `Force Layout` `Dagre Layout`。\n\n首先安装 `@antv/layout-wasm`:\n\n```bash\nnpm install @antv/layout-wasm --save\n```\n\n引入并注册布局算法:\n\n```typescript\nimport { register, Graph, ExtensionCategory } from '@antv/g6';\nimport { FruchtermanLayout, initThreads, supportsThreads } from '@antv/layout-wasm';\n\nregister(ExtensionCategory.LAYOUT, 'fruchterman-wasm', FruchtermanLayout);\n```\n\n初始化线程:\n\n```typescript\nconst supported = await supportsThreads();\nconst threads = await initThreads(supported);\n```\n\n初始化图并传入布局配置:\n\n```typescript\nconst graph = new Graph({\n // ... 其他配置\n layout: {\n type: 'fruchterman-wasm',\n threads,\n // ... 其他配置\n },\n});\n```\n\n### 使用 GPU 加速布局\n\n目前支持 GPU 加速的布局算法有:`Fruchterman Layout` `GForce Layout`。\n\n首先安装 `@antv/layout-gpu`:\n\n```bash\nnpm install @antv/layout-gpu --save\n```\n\n引入并注册布局算法:\n\n```typescript\nimport { register, Graph, ExtensionCategory } from '@antv/g6';\nimport { FruchtermanLayout } from '@antv/layout-gpu';\n\nregister(ExtensionCategory.LAYOUT, 'fruchterman-gpu', FruchtermanLayout);\n```\n\n初始化图并传入布局配置:\n\n```typescript\nconst graph = new Graph({\n // ... 其他配置\n layout: {\n type: 'fruchterman-gpu',\n // ... 其他配置\n },\n});\n```\n\n## 执行布局\n\n通常,在调用 `graph.render()` 后,G6 会自动执行布局算法。\n\n如果需要手动执行布局算法,G6 提供了以下 API:\n\n- [layout](/api/layout#graphlayoutlayoutoptions):执行布局算法\n- [setLayout](/api/layout#graphsetlayoutlayout):设置布局算法\n- [stopLayout](/api/layout#graphstoplayout):停止布局算法\n\n## 自定义布局\n\n如果内置布局算法无法满足需求,可以自定义布局算法,具体请参考[自定义布局](/manual/layout/custom-layout)。\n\n如果你正在从 G6 `5.0` 的布局配置迁移到 `5.1`,可继续阅读 [从 5.0 升级到 5.1(布局)](/manual/whats-new/upgrade-to-5-1)。\n\n\n\n---\ntitle: Radial Layout\norder: 19\n---\n\n## Overview\n\nRadial layout is a graph layout algorithm that arranges nodes in concentric circles by layers. It is commonly used to display hierarchical relationships, community structures, and more. This layout supports advanced features such as node overlap prevention and group sorting, making it suitable for visualizing various network structures.\n\n## Use Cases\n\n- Displaying hierarchical structures (e.g., organizational charts, family trees)\n- Community structure analysis\n- Scenarios that need to highlight the central node and its radiating relationships\n- Complex networks requiring node grouping and sorting\n\n## Online Demo\n\n\n\n## Configuration\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'radial',\n nodeSize: 32,\n unitRadius: 100,\n linkDistance: 200,\n },\n // other configurations...\n});\n```\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| -------------------------- | --------------------------------------------------------------- | ------------------------------------------------ | -------- | -------- |\n| type | Layout type | string | `radial` | ✓ |\n| center | Center coordinates | [number, number] | - | |\n| focusNode | Radiating center node | string \\| Node \\| null | null | |\n| height | Canvas height | number | - | |\n| width | Canvas width | number | - | |\n| nodeSize | Node size (diameter) | number \\| number[] \\| ((nodeData: Node) => Size) | - | |\n| nodeSpacing | Minimum node spacing (effective when preventing overlap) | number \\| (nodeData: Node) => number | 10 | |\n| linkDistance | Edge length | number | 50 | |\n| unitRadius | Radius per circle; when null, automatically computed from space | number \\| null | 100 | |\n| maxIteration | Maximum number of iterations | number | 1000 | |\n| maxPreventOverlapIteration | Max iterations for overlap prevention | number | 200 | |\n| preventOverlap | Whether to prevent node overlap | boolean | false | |\n| sortBy | Field or sorting function for nodes in the same layer | string \\| ((nodeData: Node) => number \\| string) | - | |\n| sortStrength | Sorting strength for nodes in the same layer | number | 10 | |\n| strictRadial | Strictly place nodes in the same layer on the same ring | boolean | true | |\n\n## Code Example\n\n### Basic Usage\n\n```js\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/radial.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n data,\n autoFit: 'center',\n layout: {\n type: 'radial',\n nodeSize: 32,\n unitRadius: 100,\n linkDistance: 200,\n },\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n },\n },\n behaviors: ['drag-canvas', 'drag-element'],\n });\n graph.render();\n });\n```\n\nResult:\n\n\"Basic\n\n## Real Cases\n\n- [Basic Radial Layout](/en/examples/layout/radial/#basic)\n- [Strict Overlap Prevention Radial Layout](/en/examples/layout/radial/#strict-prevent-overlap)\n- [Non-strict Overlap Prevention Radial Layout](/en/examples/layout/radial/#non-strict-prevent-overlap)\n- [Cluster Sorting](/en/examples/layout/radial/#cluster-sort)\n\n\n\n---\ntitle: 径向布局 Radial\norder: 19\n---\n\n## 概述\n\n径向(Radial)布局是一种将节点以同心圆方式分层排列的图布局算法,常用于展示层级关系、社群结构等。该布局支持节点防重叠、分组排序等高级特性,适用于多种网络结构的可视化。\n\n## 使用场景\n\n- 展示层级结构(如组织架构、家谱等)\n- 社群结构分析\n- 需要突出中心节点及其辐射关系的场景\n- 需要节点分组、排序的复杂网络\n\n## 在线体验\n\n\n\n## 配置方式\n\n```js\nconst graph = new Graph({\n layout: {\n type: 'radial',\n nodeSize: 32,\n unitRadius: 100,\n linkDistance: 200,\n },\n // 其他配置...\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------------------- | -------------------------------------------- | ------------------------------------------------ | -------- | ---- |\n| type | 布局类型 | string | `radial` | ✓ |\n| center | 圆心坐标 | [number, number] | - | |\n| focusNode | 辐射中心节点 | string \\| Node \\| null | null | |\n| height | 画布高度 | number | - | |\n| width | 画布宽度 | number | - | |\n| nodeSize | 节点大小(直径) | number \\| number[] \\| ((nodeData: Node) => Size) | - | |\n| nodeSpacing | 节点最小间距(防重叠时生效) | number \\| (nodeData: Node) => number | 10 | |\n| linkDistance | 边长度 | number | 50 | |\n| unitRadius | 每圈半径;为 null 时按布局空间自动计算 | number \\| null | 100 | |\n| maxIteration | 最大迭代次数 | number | 1000 | |\n| maxPreventOverlapIteration | 防重叠最大迭代次数 | number | 200 | |\n| preventOverlap | 是否防止节点重叠 | boolean | false | |\n| sortBy | 同层节点排序字段或排序函数 | string \\| ((nodeData: Node) => number \\| string) | - | |\n| sortStrength | 同层节点排序强度 | number | 10 | |\n| strictRadial | 是否严格每层节点在同一圆环上(防重叠时生效) | boolean | true | |\n\n## 代码示例\n\n### 基本用法\n\n```js\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/radial.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n data,\n autoFit: 'center',\n layout: {\n type: 'radial',\n nodeSize: 32,\n unitRadius: 100,\n linkDistance: 200,\n },\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n },\n },\n behaviors: ['drag-canvas', 'drag-element'],\n });\n graph.render();\n });\n```\n\n效果如下:\n\n\"基本\n\n## 实际案例\n\n- [基本 Radial 辐射布局](/examples/layout/radial/#basic)\n- [防止节点重叠的严格辐射布局](/examples/layout/radial/#strict-prevent-overlap)\n- [防止节点重叠的非严格辐射布局](/examples/layout/radial/#non-strict-prevent-overlap)\n- [排序聚类](/examples/layout/radial/#cluster-sort)\n\n\n\n---\ntitle: Random Layout\norder: 20\n---\n\n## Overview\n\n**Random Layout** is a layout method based on simple rules. Its core logic is to generate random coordinates for each node within a specified layout area (defined by the layout center, width, and height). The coordinates are completely random, and there is no node collision prevention.\n\n## Use Cases\n\nThe use cases for random layout are very limited. It is only recommended for the following scenarios:\n\n- **Initial Data Display**:\n\n During early development, when debugging data loading logic or quickly verifying data structure, random layout can be used for preliminary validation.\n\nFor final business delivery, it is recommended to use layouts that better reflect business value, such as [AntVDagreLayout](/en/manual/layout/antv-dagre-layout), [ForceLayout](/en/manual/layout/force-layout), or [custom layouts](/en/manual/layout/custom-layout).\n\n## Basic Usage\n\nAll other configurations use defaults (the layout width and height default to the entire canvas container).\n\n```js\nconst graph = new Graph({\n // other configurations\n layout: {\n type: 'random',\n },\n});\n```\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| -------- | ------------- | -------------------------------------------- | ----------------------------------------- | -------- |\n| type | Layout type | random | - | ✓ |\n| center | Layout center | [number, number] \\| [number, number, number] | [`layout width` / 2, `layout height` / 2] | |\n| height | Layout height | number | canvas height | |\n| width | Layout width | number | canvas width | |\n\n## Example\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 300,\n autoFit: 'view',\n data: {\n nodes: Array.from({ length: 50 }).map((_, i) => ({\n id: `${i}`,\n })),\n },\n node: {\n style: {\n labelText: (d) => d.id,\n },\n },\n behaviors: ['drag-canvas', 'zoom-canvas'],\n layout: {\n type: 'random',\n },\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 随机布局 Random\norder: 20\n---\n\n## 概述\n\n**随机布局(Random Layout)** 是一种基于简单规则的布局方式,其核心逻辑是为每个节点在指定布局区域(由布局中心点、宽度和高度定义)内生成随机坐标(完全随机,无节点防碰撞)。\n\n## 使用场景\n\n随机布局的使用场景非常局限,只推荐以下场景使用:\n\n- **数据初步展示**:\n\n 在开发初期调试数据加载逻辑或需要快速验证数据结构的情况下,可使用随机布局先行验证\n\n业务最终交付推荐使用更能体现业务价值的布局方式,比如 [AntVDagreLayout](/manual/layout/antv-dagre-layout) 、[ForceLayout](/manual/layout/force-layout) 或者[自定义布局](/manual/layout/custom-layout)等。\n\n## 基本用法\n\n其余均使用默认配置(布局宽高默认是整个画布容器)\n\n```js\nconst graph = new Graph({\n // 其他配置\n layout: {\n type: 'random',\n },\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------ | ---------- | -------------------------------------------- | -------------------------------- | ---- |\n| type | 布局类型 | random | - | ✓ |\n| center | 布局的中心 | [number, number] \\| [number, number, number] | [`布局宽度` / 2, `布局高度` / 2] | |\n| height | 布局的高度 | number | 画布高度 | |\n| width | 布局的宽度 | number | 画布宽度 | |\n\n## 示例\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 300,\n autoFit: 'view',\n data: {\n nodes: Array.from({ length: 50 }).map((_, i) => ({\n id: `${i}`,\n })),\n },\n node: {\n style: {\n labelText: (d) => d.id,\n },\n },\n behaviors: ['drag-canvas', 'zoom-canvas'],\n layout: {\n type: 'random',\n },\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Snake Layout\norder: 21\n---\n\n## Overview\n\nSnake Layout is a special type of graph layout that can more efficiently display long chain structures in a limited space. Note that the graph data must ensure that nodes are linearly arranged from the source node to the sink node, forming a clear path.\n\nNodes are arranged in an S-shape: the first node is at the start of the first row, subsequent nodes are arranged to the right in the first row until the end. At the end of the row, the next row's nodes are arranged from right to left. This process repeats until all nodes are placed.\n\n## Use Cases\n\nSuitable for scenarios that require compact presentation of linear relationships:\n\n- **Long process visualization**\n\n Perfect for scenarios with many process steps, such as approval flows, production line procedures, logistics routes, etc.\n\n- **Hierarchical structures in limited space**\n\n When the hierarchy is too long but the canvas is limited, rows can be folded to save space. For example, API call dependencies (client → gateway → serviceA → serviceB → database, snake layout compresses 5 layers into 2 rows), or file directory trees (deeply nested folder structures, e.g., src/components/utils/helpers/..., using snake layout to fold subdirectories horizontally).\n\n## Online Demo\n\n\n\n## Options\n\n> If the layout has specific properties, they are listed below. For common layout options, see [Base Layout Options](/en/manual/layout/base-layout)\n\n| Property | Description | Type | Default | Required |\n| ----------------------- | ----------------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------- | -------- |\n| type | Layout type | snake | - | ✓ |\n| [clockwise](#clockwise) | Whether nodes are arranged clockwise | boolean | true | |\n| colGap | Gap size between columns | number | Automatically calculated by canvas width and total columns | |\n| cols | Number of columns | number | 5 | |\n| nodeSize | Node size | Size \\| ((node: NodeData) => Size) | - | |\n| padding | Padding, i.e., distance from layout area to canvas boundary | number \\| number[] | 0 | |\n| rowGap | Gap size between rows | number | Automatically calculated by canvas height and total rows | |\n| sortBy | Node sorting method | (nodeA: NodeData, nodeB: NodeData) => -1 \\| 0 \\| 1 | Default is the path order in the graph | |\n\n### clockwise\n\n- When arranged clockwise, nodes start from the top left, the first row is arranged left to right, the second row right to left, and so on, forming an S-shaped path.\n- When arranged counterclockwise, nodes start from the top right, the first row is arranged right to left, the second row left to right, and so on, forming a reversed S-shaped path.\n\n## Real Cases\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: new Array(16).fill(0).map((_, i) => ({ id: `${i}` })),\n edges: new Array(15).fill(0).map((_, i) => ({ source: `${i}`, target: `${i + 1}` })),\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n },\n },\n layout: {\n type: 'snake',\n padding: 50,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 蛇形布局 Snake\norder: 21\n---\n\n## 概览\n\n蛇形布局(Snake Layout)是一种特殊的图形布局方式,能够在较小的空间内更有效地展示长链结构。需要注意的是,其图数据需要确保节点按照从源节点到汇节点的顺序进行线性排列,形成一条明确的路径。\n\n节点按 S 字型排列,第一个节点位于第一行的起始位置,接下来的节点在第一行向右排列,直到行末尾。到达行末尾后,下一行的节点从右向左反向排列。这个过程重复进行,直到所有节点排列完毕。\n\n## 使用场景\n\n适合需要紧凑呈现线性关系的场景:\n\n- **长流程可视化**\n\n 完美适配流程步骤过多的场景,如审批流程、生产线工序、物流运输路径等。\n\n- **有限空间内的层级结构**\n\n 层级结构过长但画布受限,需通过折叠行节省空间,比如 API 调用依赖(客户端 → 网关 → 服务A → 服务B → 数据库,蛇形布局将 5 层压缩为 2 行)、文件目录树(深度嵌套的文件夹结构,如 src/components/utils/helpers/... ,用蛇形布局横向折叠子目录)。\n\n## 在线体验\n\n\n\n## 配置项\n\n> 如果布局有其特定的属性,我们将在下面列出。对于所有布局的通用属性,见[布局通用配置项](/manual/layout/base-layout)\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ----------------------- | ---------------------------------- | -------------------------------------------------- | -------------------------------------- | ---- |\n| type | 布局类型 | snake | - | ✓ |\n| [clockwise](#clockwise) | 节点排布方向是否顺时针 | boolean | true | |\n| colGap | 节点列之间的间隙大小 | number | 默认将根据画布宽度和节点总列数自动计算 | |\n| cols | 节点列数 | number | 5 | |\n| nodeSize | 节点尺寸 | Size \\| ((node: NodeData) => Size) | - | |\n| padding | 内边距,即布局区域与画布边界的距离 | number \\| number[] | 0 | |\n| rowGap | 节点行之间的间隙大小 | number | 默认将根据画布高度和节点总行数自动计算 | |\n| sortBy | 节点排序方法 | (nodeA: NodeData, nodeB: NodeData) => -1 \\| 0 \\| 1 | 默认按照在图中的路径顺序进行展示 | |\n\n### clockwise\n\n- 在顺时针排布时,节点从左上角开始,第一行从左到右排列,第二行从右到左排列,依次类推,形成 S 型路径。\n\n- 在逆时针排布时,节点从右上角开始,第一行从右到左排列,第二行从左到右排列,依次类推,形成反向 S 型路径。\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: new Array(16).fill(0).map((_, i) => ({ id: `${i}` })),\n edges: new Array(15).fill(0).map((_, i) => ({ source: `${i}`, target: `${i + 1}` })),\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: {\n labelFill: '#fff',\n labelPlacement: 'center',\n labelText: (d) => d.id,\n },\n },\n layout: {\n type: 'snake',\n padding: 50,\n },\n behaviors: ['drag-canvas', 'drag-element'],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Background\norder: 1\n---\n\n## Overview\n\nSupport setting a background image for the canvas to make the canvas more hierarchical and narrative.\n\n## Use Cases\n\nThis plugin is mainly used for:\n\n- Setting a unified brand background color or image for charts\n- Distinguishing different functional areas through the background\n- Enhancing the visual hierarchy and aesthetics of charts\n\n## Basic Usage\n\nBelow is a simple example of initializing the Background plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'background',\n key: 'my-background', // Specify an identifier for the plugin for dynamic updates\n backgroundColor: '#f0f2f5', // Set background color\n backgroundImage: 'url(https://example.com/bg.png)', // Set background image\n },\n ],\n});\n```\n\n## Configuration Options\n\nThe configuration options for the Background plugin inherit all CSS style properties ([CSSStyleDeclaration](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration)), so you can use any valid CSS property to configure the background. Here are some common configurations:\n\n| Property | Description | Type | Default Value | Required |\n| ------------------ | ----------------------------- | ------ | ----------------- | -------- |\n| type | Plugin type | string | `background` | ✓ |\n| key | Unique identifier for updates | string | - | |\n| width | Background width | string | `100%` | |\n| height | Background height | string | `100%` | |\n| backgroundColor | Background color | string | - | |\n| backgroundImage | Background image | string | - | |\n| backgroundSize | Background size | string | `cover` | |\n| backgroundPosition | Background position | string | - | |\n| backgroundRepeat | Background repeat | string | - | |\n| opacity | Background opacity | string | - | |\n| transition | Transition animation | string | `background 0.5s` | |\n| zIndex | Stacking order | string | -1 | |\n\n> Note: The `zIndex` is set to -1 by default to prevent the background from covering other plugin DOM elements, such as grid lines.\n\n## Code Examples\n\n### Basic Background Color\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 200,\n plugins: [\n {\n type: 'background',\n width: '300px',\n height: '200px',\n backgroundColor: '#f0f2f5',\n },\n ],\n});\n\ngraph.render();\n```\n\n### Using a Background Image\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 200,\n plugins: [\n {\n type: 'background',\n width: '300px',\n height: '200px',\n backgroundImage:\n 'url(https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*0Qq0ToQm1rEAAAAAAAAAAAAADmJ7AQ/original)',\n backgroundRepeat: 'no-repeat',\n backgroundSize: 'cover',\n opacity: 0.2,\n },\n ],\n});\n\ngraph.render();\n```\n\n### Gradient Background\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 200,\n plugins: [\n {\n type: 'background',\n width: '300px',\n height: '200px',\n background: 'linear-gradient(45deg, #1890ff, #722ed1)',\n opacity: '0.8',\n },\n ],\n});\n\ngraph.render();\n```\n\n### Dynamically Updating the Background\n\n```js\n// Initial configuration\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'background',\n key: 'my-background',\n backgroundColor: '#f0f2f5',\n },\n ],\n});\n\n// Subsequent updates\ngraph.updatePlugin({\n key: 'my-background',\n backgroundColor: '#e6f7ff',\n transition: 'background 1s ease',\n});\n```\n\n## FAQs\n\n### 1. Background conflicts with other plugins?\n\nBy default, the `zIndex` of the background plugin is set to `-1` to ensure it is below other elements. If there are still conflicts, you can adjust the `zIndex` value:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'background',\n zIndex: '-2', // Lower z-index to avoid conflicts\n },\n ],\n});\n```\n\n\n\n---\ntitle: 背景 Background\norder: 1\n---\n\n## 概述\n\n支持为图画布设置一个背景图片,让画布更有层次感、叙事性。\n\n## 使用场景\n\n这一插件主要用于:\n\n- 为图表设置统一的品牌背景色或图片\n- 通过背景区分不同的功能区域\n- 增强图表的视觉层次感和美观度\n\n## 基本用法\n\n以下是一个简单的 Background 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'background',\n key: 'my-background', // 为插件指定标识符,方便动态更新\n backgroundColor: '#f0f2f5', // 设置背景色\n backgroundImage: 'url(https://example.com/bg.png)', // 设置背景图\n },\n ],\n});\n```\n\n## 配置项\n\nBackground 插件的配置项继承了所有 CSS 样式属性([CSSStyleDeclaration](https://developer.mozilla.org/zh-CN/docs/Web/API/CSSStyleDeclaration)),因此你可以使用任何合法的 CSS 属性来配置背景。以下是一些常用配置:\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------------ | ---------------------------- | ------ | ----------------- | ---- |\n| type | 插件类型 | string | `background` | ✓ |\n| key | 插件唯一标识符,用于后续更新 | string | - | |\n| width | 背景宽度 | string | `100%` | |\n| height | 背景高度 | string | `100%` | |\n| backgroundColor | 背景颜色 | string | - | |\n| backgroundImage | 背景图片 | string | - | |\n| backgroundSize | 背景尺寸 | string | `cover` | |\n| backgroundPosition | 背景位置 | string | - | |\n| backgroundRepeat | 背景重复方式 | string | - | |\n| opacity | 背景透明度 | string | - | |\n| transition | 过渡动画 | string | `background 0.5s` | |\n| zIndex | 层叠顺序 | string | -1 | |\n\n> 注意:`zIndex` 默认为 -1,这是为了避免背景覆盖其他插件的 DOM 元素,如网格线。\n\n## 代码示例\n\n### 基础背景色\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 200,\n plugins: [\n {\n type: 'background',\n width: '300px',\n height: '200px',\n backgroundColor: '#f0f2f5',\n },\n ],\n});\n\ngraph.render();\n```\n\n### 使用背景图片\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 200,\n plugins: [\n {\n type: 'background',\n width: '300px',\n height: '200px',\n backgroundImage:\n 'url(https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*0Qq0ToQm1rEAAAAAAAAAAAAADmJ7AQ/original)',\n backgroundRepeat: 'no-repeat',\n backgroundSize: 'cover',\n opacity: 0.2,\n },\n ],\n});\n\ngraph.render();\n```\n\n### 渐变背景\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 200,\n plugins: [\n {\n type: 'background',\n width: '300px',\n height: '200px',\n background: 'linear-gradient(45deg, #1890ff, #722ed1)',\n opacity: '0.8',\n },\n ],\n});\n\ngraph.render();\n```\n\n### 动态更新背景\n\n```js\n// 初始化时配置\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'background',\n key: 'my-background',\n backgroundColor: '#f0f2f5',\n },\n ],\n});\n\n// 后续更新\ngraph.updatePlugin({\n key: 'my-background',\n backgroundColor: '#e6f7ff',\n transition: 'background 1s ease',\n});\n```\n\n## 常见问题\n\n### 1. 背景与其他插件冲突?\n\n默认情况下,背景插件的 `zIndex` 设为 `-1`,以确保它位于其他元素之下。如果仍有冲突,可以调整 `zIndex` 值:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'background',\n zIndex: '-2', // 降低 z-index 避免冲突\n },\n ],\n});\n```\n\n\n\n---\ntitle: BubbleSets\norder: 2\n---\n\n## Overview\n\nThe BubbleSets plugin represents sets and their relationships by creating bubble shapes, helping users intuitively understand logical relationships such as intersections and unions between sets. It is a tool to enhance data visualization effects, especially suitable for displaying complex data set relationships.\n\n## Use Cases\n\nThe BubbleSets plugin is mainly suitable for the following scenarios:\n\n- Displaying relationships between sets (e.g., intersections, unions)\n- Enhancing the expressive ability of data visualization\n- Identifying specific sets of nodes or edges in complex network graphs\n\n## Basic Usage\n\nBelow is a simple example of initializing the BubbleSets plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node1', 'node2'], // List of node IDs to be enclosed\n label: true, // Whether to display labels\n },\n ],\n});\n```\n\n## Live Demo\n\n\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| ------------------------ | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------- | -------- |\n| type | Plugin type | string | `bubble-sets` | ✓ |\n| key | Unique identifier for the plugin for subsequent updates | string | - | |\n| members | Member elements, including nodes and edges, [example](#members) | string[] | - | ✓ |\n| avoidMembers | Elements to avoid, not included when drawing contours (currently supports setting nodes) | string[] | - | |\n| label | Whether to display labels | boolean | true | |\n| labelPlacement | Label position | `left` \\| `right` \\| `top` \\| `bottom` \\| `center` \\| `bottom` | `bottom` | |\n| labelBackground | Whether to display background | boolean | false | |\n| labelPadding | Label padding | number \\| number[] | 0 | |\n| labelCloseToPath | Whether the label is close to the contour, [example](#labelclosetopath) | boolean | true | |\n| labelAutoRotate | Whether the label rotates with the contour, [example](#labelautorotate) | boolean | true | |\n| labelOffsetX | Label x-axis offset | number | 0 | |\n| labelOffsetY | Label y-axis offset | number | 0 | |\n| labelMaxWidth | Maximum width of the text, automatically ellipsized if exceeded | number | - | |\n| maxRoutingIterations | Maximum number of iterations for calculating paths between members | number | 100 | |\n| maxMarchingIterations | Maximum number of iterations for calculating contours | number | 20 | |\n| pixelGroup | Number of pixels per potential area group, used to improve speed | number | 4 | |\n| edgeR0 | Edge radius parameter R0 | number | - | |\n| edgeR1 | Edge radius parameter R1 | number | - | |\n| nodeR0 | Node radius parameter R0 | number | - | |\n| nodeR1 | Node radius parameter R1 | number | - | |\n| morphBuffer | Morph buffer size | number | | |\n| threshold | Threshold | number | - | |\n| memberInfluenceFactor | Member influence factor | number | - | |\n| edgeInfluenceFactor | Edge influence factor | number | - | |\n| nonMemberInfluenceFactor | Non-member influence factor | number | - | |\n| virtualEdges | Whether to use virtual edges | boolean | - | |\n\n### members\n\nMember elements, including nodes and edges.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2'],\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n behaviors: ['drag-canvas', 'zoom-canvas'],\n plugins: [\n {\n type: 'bubble-sets',\n key: 'bubble-sets-a',\n members: ['node-0', 'node-1', 'node-2'],\n },\n ],\n});\n\ngraph.render();\n```\n\n### labelCloseToPath\n\nExample: Do not let the label stick to the contour\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // Display label\n labelText: 'cluster-a',\n labelCloseToPath: false,\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n key: 'bubble-sets-a',\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // Display label\n labelText: 'cluster-a',\n labelCloseToPath: false,\n },\n ],\n behaviors: ['drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n### labelAutoRotate\n\nExample: Do not let the label rotate with the contour\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // Display label\n labelText: 'cluster-a',\n labelAutoRotate: false,\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n key: 'bubble-sets-a',\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // Display label\n labelText: 'cluster-a',\n labelAutoRotate: false,\n },\n ],\n behaviors: ['drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n## Usage Examples\n\n### Basic BubbleSets\n\nThe simplest way is to use the preset configuration directly:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'center',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n behaviors: ['drag-canvas', 'zoom-canvas'],\n plugins: [\n {\n type: 'bubble-sets',\n key: 'bubble-sets-a',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n },\n ],\n});\n\ngraph.render();\n```\n\n### Custom BubbleSets Style\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n fill: '#7e3feb', // Bubble fill color\n fillOpacity: 0.1, // Fill opacity\n stroke: '#7e3feb', // Border color\n strokeOpacity: 1, // Border opacity\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'center',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n fill: '#7e3feb', // Bubble fill color\n fillOpacity: 0.1, // Fill opacity\n stroke: '#7e3feb', // Border color\n strokeOpacity: 1, // Border opacity\n },\n ],\n behaviors: ['drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n### Label Configuration\n\nYou can configure the position, background, offset, and other properties of the label to enhance the visualization effect.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // Display label\n labelPlacement: 'top', // Label position\n labelBackground: true, // Display label background\n labelPadding: 5, // Label padding\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'center',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n key: 'bubble-sets-a',\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // Display label\n labelText: 'cluster-a',\n labelPlacement: 'top', // Label position\n labelBackground: true, // Display label background\n labelPadding: 5, // Label padding\n },\n ],\n behaviors: ['drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n## Practical Examples\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/collection.json')\n .then((res) => res.json())\n .then((data) => {\n const groupedNodesByCluster = data.nodes.reduce((acc, node) => {\n const cluster = node.data.cluster;\n acc[cluster] ||= [];\n acc[cluster].push(node.id);\n return acc;\n }, {});\n\n const createStyle = (baseColor) => ({\n fill: baseColor,\n stroke: baseColor,\n labelFill: '#fff',\n labelPadding: 2,\n labelBackgroundFill: baseColor,\n labelBackgroundRadius: 5,\n });\n\n const graph = new Graph({\n container: 'container',\n data,\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n node: {\n palette: { field: 'cluster' },\n },\n layout: {\n type: 'force',\n preventOverlap: true,\n linkDistance: (d) => {\n if (d.source === 'node0' || d.target === 'node0') {\n return 200;\n }\n return 80;\n },\n },\n plugins: [\n {\n key: 'bubble-sets-a',\n type: 'bubble-sets',\n members: groupedNodesByCluster['a'],\n labelText: 'cluster-a',\n ...createStyle('#1783FF'),\n },\n {\n key: 'bubble-sets-b',\n type: 'bubble-sets',\n members: groupedNodesByCluster['b'],\n labelText: 'cluster-b',\n ...createStyle('#00C9C9'),\n },\n {\n key: 'bubble-sets-c',\n type: 'bubble-sets',\n members: groupedNodesByCluster['c'],\n labelText: 'cluster-c',\n ...createStyle('#F08F56'),\n },\n {\n key: 'bubble-sets-d',\n type: 'bubble-sets',\n members: groupedNodesByCluster['d'],\n labelText: 'cluster-d',\n ...createStyle('#D580FF'),\n },\n ],\n autoFit: 'center',\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: 气泡集 BubbleSets\norder: 2\n---\n\n## 概述\n\nBubbleSets 插件通过创建气泡形状来表示集合及其关系,帮助用户直观地理解集合间的交集、并集等逻辑关系。它是一种增强数据可视化效果的工具,特别适用于展示复杂的数据集合关系。\n\n## 使用场景\n\nBubbleSets 插件主要适用于以下场景:\n\n- 展示集合间的关系(如交集、并集)\n- 增强数据可视化的表达能力\n- 在复杂网络图中标识特定节点或边的集合\n\n## 基本用法\n\n以下是一个简单的 BubbleSets 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node1', 'node2'], // 需要包裹的节点 ID 列表\n label: true, // 是否显示标签\n },\n ],\n});\n```\n\n## 在线体验\n\n\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------- | ------------- | ---- |\n| type | 插件类型 | string | `bubble-sets` | ✓ |\n| key | 插件唯一标识符,用于后续更新 | string | - | |\n| members | 成员元素,包括节点和边,[示例](#members) | string[] | - | ✓ |\n| avoidMembers | 需要避开的元素,在绘制轮廓时不会包含这些元素(目前支持设置节点) | string[] | - | |\n| label | 是否显示标签 | boolean | true | |\n| labelPlacement | 标签位置 | `left` \\| `right` \\| `top` \\| `bottom` \\| `center` \\| `bottom` | `bottom` | |\n| labelBackground | 是否显示背景 | boolean | false | |\n| labelPadding | 标签内边距 | number \\| number[] | 0 | |\n| labelCloseToPath | 标签是否贴合轮廓,[示例](#labelclosetopath) | boolean | true | |\n| labelAutoRotate | 标签是否跟随轮廓旋转,[示例](#labelautorotate) | boolean | true | |\n| labelOffsetX | 标签 x 轴偏移量 | number | 0 | |\n| labelOffsetY | 标签 y 轴偏移量 | number | 0 | |\n| labelMaxWidth | 文本的最大宽度,超出会自动省略 | number | - | |\n| maxRoutingIterations | 计算成员之间路径的最大迭代次数 | number | 100 | |\n| maxMarchingIterations | 计算轮廓的最大迭代次数 | number | 20 | |\n| pixelGroup | 每个潜在区域组的像素数,用于提高速度 | number | 4 | |\n| edgeR0 | 边的半径参数 R0 | number | - | |\n| edgeR1 | 边的半径参数 R1 | number | - | |\n| nodeR0 | 节点的半径参数 R0 | number | - | |\n| nodeR1 | 节点的半径参数 R1 | number | - | |\n| morphBuffer | 形态缓冲区大小 | number | | |\n| threshold | 阈值 | number | - | |\n| memberInfluenceFactor | 成员影响因子 | number | - | |\n| edgeInfluenceFactor | 边影响因子 | number | - | |\n| nonMemberInfluenceFactor | 非成员影响因子 | number | - | |\n| virtualEdges | 是否使用虚拟边 | boolean | - | |\n\n### members\n\n成员元素,包括节点和边。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2'],\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n behaviors: ['drag-canvas', 'zoom-canvas'],\n plugins: [\n {\n type: 'bubble-sets',\n key: 'bubble-sets-a',\n members: ['node-0', 'node-1', 'node-2'],\n },\n ],\n});\n\ngraph.render();\n```\n\n### labelCloseToPath\n\n示例:不让 label 贴合轮廓\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // 显示标签\n labelText: 'cluster-a',\n labelCloseToPath: false,\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n key: 'bubble-sets-a',\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // 显示标签\n labelText: 'cluster-a',\n labelCloseToPath: false,\n },\n ],\n behaviors: ['drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n### labelAutoRotate\n\n示例:不让label标签跟随轮廓旋转\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // 显示标签\n labelText: 'cluster-a',\n labelAutoRotate: false,\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n key: 'bubble-sets-a',\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // 显示标签\n labelText: 'cluster-a',\n labelAutoRotate: false,\n },\n ],\n behaviors: ['drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n## 使用示例\n\n### 基础 BubbleSets\n\n最简单的方式是直接使用预设配置:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'center',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n behaviors: ['drag-canvas', 'zoom-canvas'],\n plugins: [\n {\n type: 'bubble-sets',\n key: 'bubble-sets-a',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n },\n ],\n});\n\ngraph.render();\n```\n\n### 自定义 BubbleSets 样式\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n fill: '#7e3feb', // 气泡填充颜色\n fillOpacity: 0.1, // 填充透明度\n stroke: '#7e3feb', // 边框颜色\n strokeOpacity: 1, // 边框透明度\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'center',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n fill: '#7e3feb', // 气泡填充颜色\n fillOpacity: 0.1, // 填充透明度\n stroke: '#7e3feb', // 边框颜色\n strokeOpacity: 1, // 边框透明度\n },\n ],\n behaviors: ['drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n### 标签配置\n\n您可以配置标签的位置、背景、偏移量等属性,以增强可视化效果。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // 显示标签\n labelPlacement: 'top', // 标签位置\n labelBackground: true, // 显示标签背景\n labelPadding: 5, // 标签内边距\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'center',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n id: 'edge-0',\n source: 'node-0',\n target: 'node-2',\n },\n {\n id: 'edge-1',\n source: 'node-1',\n target: 'node-2',\n },\n {\n id: 'edge-2',\n source: 'node-2',\n target: 'node-3',\n },\n {\n id: 'edge-3',\n source: 'node-3',\n target: 'node-4',\n },\n {\n id: 'edge-4',\n source: 'node-3',\n target: 'node-5',\n },\n {\n id: 'edge-5',\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n key: 'bubble-sets-a',\n type: 'bubble-sets',\n members: ['node-0', 'node-1', 'node-2', 'node-3'],\n label: true, // 显示标签\n labelText: 'cluster-a',\n labelPlacement: 'top', // 标签位置\n labelBackground: true, // 显示标签背景\n labelPadding: 5, // 标签内边距\n },\n ],\n behaviors: ['drag-canvas', 'zoom-canvas'],\n});\n\ngraph.render();\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/collection.json')\n .then((res) => res.json())\n .then((data) => {\n const groupedNodesByCluster = data.nodes.reduce((acc, node) => {\n const cluster = node.data.cluster;\n acc[cluster] ||= [];\n acc[cluster].push(node.id);\n return acc;\n }, {});\n\n const createStyle = (baseColor) => ({\n fill: baseColor,\n stroke: baseColor,\n labelFill: '#fff',\n labelPadding: 2,\n labelBackgroundFill: baseColor,\n labelBackgroundRadius: 5,\n });\n\n const graph = new Graph({\n container: 'container',\n data,\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n node: {\n palette: { field: 'cluster' },\n },\n layout: {\n type: 'force',\n preventOverlap: true,\n linkDistance: (d) => {\n if (d.source === 'node0' || d.target === 'node0') {\n return 200;\n }\n return 80;\n },\n },\n plugins: [\n {\n key: 'bubble-sets-a',\n type: 'bubble-sets',\n members: groupedNodesByCluster['a'],\n labelText: 'cluster-a',\n ...createStyle('#1783FF'),\n },\n {\n key: 'bubble-sets-b',\n type: 'bubble-sets',\n members: groupedNodesByCluster['b'],\n labelText: 'cluster-b',\n ...createStyle('#00C9C9'),\n },\n {\n key: 'bubble-sets-c',\n type: 'bubble-sets',\n members: groupedNodesByCluster['c'],\n labelText: 'cluster-c',\n ...createStyle('#F08F56'),\n },\n {\n key: 'bubble-sets-d',\n type: 'bubble-sets',\n members: groupedNodesByCluster['d'],\n labelText: 'cluster-d',\n ...createStyle('#D580FF'),\n },\n ],\n autoFit: 'center',\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: Contextmenu\norder: 3\n---\n\n## Overview\n\nThe context menu, also known as the right-click menu, is a menu that appears when a user clicks on a specific area. It supports triggering custom events before and after clicking. Through the context menu, specific element operations can be integrated, making it convenient to control a particular item when needed.\n\n## Use Cases\n\nThis plugin is mainly used for:\n\n- Various interactions with elements: viewing nodes, viewing edges, deleting nodes, etc.\n\n## Basic Usage\n\nBelow is a simple example of initializing the Contextmenu plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'contextmenu',\n // Enable right-click menu only on nodes, by default all elements are enabled\n enable: (e) => e.targetType === 'node',\n getItems: () => {\n return [{ name: 'View Details', value: 'detail' }];\n },\n onClick: (value) => {\n if (value === 'detail') console.log('Display node details');\n },\n },\n ],\n});\n```\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| -------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ---------------- | -------- |\n| className | Additional class name for the menu DOM | string | `g6-contextmenu` | |\n| trigger | How to trigger the right-click menu: `contextmenu` for right-click, `click` for click | `click` \\| `contextmenu` | `contextmenu` | |\n| offset | Offset of the menu display in X and Y directions | [number, number] | [4, 4] | |\n| onClick | Callback method triggered after the menu is clicked, [example](#onclick) | (value: string, target: HTMLElement, current: Element) => void | - | |\n| getItems | Returns the list of menu items, supports `Promise` type return value. It is a shortcut configuration for `getContent` | (event: IElementEvent) => [Item](#item)[] \\| Promise<[Item](#item)[]> | - | |\n| getContent | Returns the content of the menu, supports `Promise` type return value, can also use `getItems` for shortcut configuration | (event: IElementEvent) => HTMLElement \\| string \\| Promise | - | |\n| loadingContent | Menu content used when `getContent` returns a `Promise` | HTMLElement \\| string | - | |\n| enable | Whether it is available, determines whether the right-click menu is supported by parameters, by default all are available | boolean \\| (event: IElementEvent) => boolean | true | |\n\n### Item\n\nEach menu item (Item) contains the following properties:\n\n| Property | Description | Type | Required |\n| -------- | ------------------------------------ | -------- | -------- |\n| name | Name displayed for the menu item | `string` | ✓ |\n| value | Value corresponding to the menu item | `string` | ✓ |\n\n### onClick\n\nThis function is triggered after clicking a menu item, and the function has three parameters:\n\n- value: Corresponds to the value of the menu item\n- target: The DOM node of the menu item container\n- current: The element that triggered the menu item, for example, if it is a node, you can use `current` to get the node information (id), or to modify the element\n\n## Code Examples\n\n### Basic Right-click Menu\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n layout: { type: 'grid' },\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu', // 'click' or 'contextmenu'\n onClick: (value, target, current) => {\n alert('You have clicked the「' + value + '」item');\n },\n getItems: () => {\n return [\n { name: 'View Details', value: 'detail' },\n { name: 'Delete', value: 'delete' },\n ];\n },\n },\n ],\n});\n```\n\n### Edge Right-click Menu\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n layout: { type: 'grid' },\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu',\n getItems: () => {\n return [{ name: 'Change Start Point', value: 'change' }];\n },\n onClick: (value) => {\n if (value === 'change') console.log('Execute change start point operation here');\n },\n // Enable right-click menu only on edges\n enable: (e) => e.targetType === 'edge',\n },\n ],\n});\n```\n\n### Asynchronous Loading of Menu Items\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n layout: { type: 'grid' },\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu',\n getItems: async () => {\n // Toolbar configuration can be obtained from the server or other asynchronous sources\n const response = await fetch('/api/contextmenu-config');\n const items = await response.json();\n return items;\n },\n // Enable right-click menu only on nodes\n enable: (e) => e.targetType === 'node',\n },\n ],\n});\n```\n\n### Dynamic Control of Menu Items\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n layout: { type: 'grid' },\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu',\n getItems: (e) => {\n if (e.target.id === 'node-1') {\n return [\n {\n name: 'Delete Node',\n value: 'delete',\n },\n ];\n }\n if (e.target.type === 'edge') {\n return [\n {\n name: 'Move Edge',\n value: 'move',\n },\n ];\n }\n return [];\n },\n },\n ],\n});\n```\n\n## Practical Examples\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: {\n type: 'grid',\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu', // 'click' or 'contextmenu'\n onClick: (v) => {\n alert('You have clicked the「' + v + '」item');\n },\n getItems: () => {\n return [\n { name: '展开一度关系', value: 'spread' },\n { name: '查看详情', value: 'detail' },\n ];\n },\n enable: (e) => e.targetType === 'node',\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 上下文菜单 Contextmenu\norder: 3\n---\n\n## 概述\n\n上下文菜单(Contextmenu),也被称为右键菜单,是当用户在某个特定区域上点击后出现的一个菜单。支持在点击前后,触发自定义事件。通过上下文菜单,可以将一些具体元素的操作集成在其中,方便对某一项来进行需要时的单独控制。\n\n## 使用场景\n\n这一插件主要用于:\n\n- 元素的各种交互:查看节点、查看边、删除节点等等交互操作\n\n## 基本用法\n\n以下是一个简单的 Contextmenu 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'contextmenu',\n // 只在节点上开启右键菜单,默认全部元素都开启\n enable: (e) => e.targetType === 'node',\n getItems: () => {\n return [{ name: '查看详情', value: 'detail' }];\n },\n onClick: (value) => {\n if (value === 'detail') console.log('展示节点详情');\n },\n },\n ],\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ---------------- | ---- |\n| className | 给菜单的 DOM 追加的类名 | string | `g6-contextmenu` | |\n| trigger | 如何触发右键菜单:`contextmenu` 表示右键触发,`click` 表示点击触发 | `click` \\| `contextmenu` | `contextmenu` | |\n| offset | 菜单显式 X、Y 方向的偏移量 | [number, number] | [4, 4] | |\n| onClick | 当菜单被点击后,触发的回调方法,[示例](#onclick) | (value: string, target: HTMLElement, current: Element) => void | - | |\n| getItems | 返回菜单的项目列表,支持 `Promise` 类型的返回值。是 `getContent` 的快捷配置 | (event: IElementEvent) => [Item](#item)[] \\| Promise<[Item](#item)[]> | - | |\n| getContent | 返回菜单的内容,支持 `Promise` 类型的返回值,也可以使用 `getItems` 进行快捷配置 | (event: IElementEvent) => HTMLElement \\| string \\| Promise | - | |\n| loadingContent | 当 `getContent` 返回一个 `Promise` 时,使用的菜单内容 | HTMLElement \\| string | - | |\n| enable | 是否可用,通过参数判断是否支持右键菜单,默认是全部可用 | boolean \\| (event: IElementEvent) => boolean | true | |\n\n### Item\n\n每个菜单项目 (Item) 包含以下属性:\n\n| 属性 | 描述 | 类型 | 必选 |\n| ----- | ---------------- | -------- | ---- |\n| name | 菜单项显示的名字 | `string` | ✓ |\n| value | 菜单项对应的值 | `string` | ✓ |\n\n### onClick\n\n点击菜单项后会触发该函数,函数有三个参数:\n\n- value: 对应菜单项的 value\n- target: 对应菜单项容器的 dom 节点\n- current: 对应触发菜单项的元素,例如是节点则可以通过 `current` 来获取到节点的信息(id)等,或者来对元素进行修改\n\n## 代码示例\n\n### 基础右键菜单\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n layout: { type: 'grid' },\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu', // 'click' or 'contextmenu'\n onClick: (value, target, current) => {\n alert('You have clicked the「' + v + '」item');\n },\n getItems: () => {\n return [\n { name: '查看详情', value: 'detail' },\n { name: '删除', value: 'delete' },\n ];\n },\n },\n ],\n});\n```\n\n### 边的右键菜单\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n layout: { type: 'grid' },\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu',\n getItems: () => {\n return [{ name: '变更起点', value: 'change' }];\n },\n onClick: (value) => {\n if (value === 'change') console.log('这里执行变更起点操作');\n },\n // 仅在边上开启右键菜单\n enable: (e) => e.targetType === 'edge',\n },\n ],\n});\n```\n\n### 异步加载菜单项\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n layout: { type: 'grid' },\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu',\n getItems: async () => {\n // 可以从服务器或其他异步源获取工具栏配置\n const response = await fetch('/api/contextmenu-config');\n const items = await response.json();\n return items;\n },\n // 仅在边上开启右键菜单\n enable: (e) => e.targetType === 'node',\n },\n ],\n});\n```\n\n### 动态控制菜单项\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n layout: { type: 'grid' },\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu',\n getItems: (e) => {\n if (e.target.id === 'node-1') {\n return [\n {\n name: '删除节点',\n value: 'delete',\n },\n ];\n }\n if (e.target.type === 'edge') {\n return [\n {\n name: '移动边',\n value: 'move',\n },\n ];\n }\n return [];\n },\n },\n ],\n});\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: {\n type: 'grid',\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'contextmenu',\n trigger: 'contextmenu', // 'click' or 'contextmenu'\n onClick: (v) => {\n alert('You have clicked the「' + v + '」item');\n },\n getItems: () => {\n return [\n { name: '展开一度关系', value: 'spread' },\n { name: '查看详情', value: 'detail' },\n ];\n },\n enable: (e) => e.targetType === 'node',\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Custom Plugin\norder: 20\n---\n\n## Overview\n\nCustom plugins can implement additional features, such as adding extra components, rendering logic, etc. Custom plugins can effectively achieve functional decoupling, better manage and orchestrate code, and facilitate subsequent maintenance.\n\n## Use Cases\n\n### Add extra components, rendering logic, etc.\n\n- **Extra components**: Such as built-in plugins like `Tooltip`, `Minimap`, `Snapline`, `Grid`, `Context Menu`, `Watermark`, etc.\n- **Rendering logic**: Such as built-in plugins like `Edge Bundling`, and `Remote Data Loading` ([Example](#remote-data-loading)), etc.\n\n### When built-in plugins cannot meet the requirements\n\nWhen built-in plugins cannot fully meet business needs, users can also make adjustments and modifications through custom plugins (inheriting built-in plugins).\n\n_(If the features supported by built-in plugins are more general, or if there are bugs in built-in plugins, you are welcome to submit issues or PRs on [Github](https://github.com/antvis/G6))_\n\n## Custom Plugin Examples\n\nLike interactions, the implementation of plugins is also quite flexible, and you can implement your plugin in your preferred style.\n\nHere are a few simple custom plugin implementations:\n\n### Remote Data Loading\n\nAutomatically load remote data during graph instantiation:\n\n```typescript\nimport { BasePlugin } from '@antv/g6';\nimport type { BasePluginOptions, RuntimeContext } from '@antv/g6';\n\ninterface RemoteDataSourceOptions extends BasePluginOptions {}\n\nclass RemoteDataSource extends BasePlugin {\n constructor(context: RuntimeContext, options: RemoteDataSourceOptions) {\n super(context, options);\n this.loadData();\n }\n\n private async loadData() {\n // mock remote data\n const data = {\n nodes: [\n { id: 'node-1', x: 100, y: 100 },\n { id: 'node-2', x: 200, y: 200 },\n ],\n edges: [{ source: 'node-1', target: 'node-2' }],\n };\n\n const { graph } = this.context;\n graph.setData(data);\n await graph.render();\n }\n}\n```\n\n- In this example, we simulate a data loading plugin. After using this plugin, there is no need to pass data when instantiating the Graph, as the plugin will automatically load remote data.\n\n- `BasePlugin` is the base class for all plugins, and each custom plugin needs to inherit this base class.\n\n\n\n### Automatically Enable or Disable Animation Based on Node Count\n\n```typescript\nimport type { BasePluginOptions, RuntimeContext } from '@antv/g6';\nimport { BasePlugin, GraphEvent } from '@antv/g6';\n\ninterface AutoSwitchAnimationOptions extends BasePluginOptions {\n maxLength: number; // Disable global animation when the number of nodes reaches this value\n}\n\nclass AutoSwitchAnimation extends BasePlugin {\n static defaultOptions: Partial = {\n maxLength: 1000,\n };\n constructor(context: RuntimeContext, options: AutoSwitchAnimationOptions) {\n super(context, options);\n this.bindEvents();\n }\n private bindEvents() {\n const { graph } = this.context;\n graph.on(GraphEvent.BEFORE_RENDER, this.switchAnimation);\n }\n private switchAnimation() {\n const { graph } = this.context;\n graph.setOptions({\n animation: graph.getNodeData().length < this.options.maxLength,\n });\n }\n private unbindEvents() {\n const { graph } = this.context;\n graph.off(GraphEvent.BEFORE_RENDER, this.switchAnimation);\n }\n destroy() {\n this.unbindEvents();\n super.destroy();\n }\n}\n```\n\n- In this example, we listen to the `GraphEvent.BEFORE_RENDER` event and determine whether the current number of nodes exceeds a specified value. If so, global animation is disabled; otherwise, it is enabled.\n- `maxLength` is a defined configuration item that can be passed in when initializing the graph instance. [Plugin Configuration](#configure-plugin)\n\n## Register Plugin\n\nRegister using the method provided by G6\n\n```typescript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { MyCustomPlugin } from './my-custom-plugin';\n\nregister(ExtensionCategory.PLUGIN, 'my-custom-plugin', MyCustomPlugin);\n```\n\n## Configure Plugin\n\n- You can pass the plugin type name or configuration parameter object in `plugins`, see [Configure Plugin](/manual/plugin/overview#configuration-method)\n\n- For example, the previous [Automatically Enable or Disable Animation Based on Node Count](#automatically-enable-or-disable-animation-based-on-node-count) is configured as follows:\n\n ```typescript\n const graph = new Graph({\n // Other configurations\n plugins: [\n {\n type: 'auto-switch-animation',\n maxLength: 500,\n },\n ],\n });\n ```\n\n\n\n---\ntitle: 自定义插件\norder: 20\n---\n\n## 概述\n\n自定义插件可以实现一些额外的功能,例如添加额外的组件、渲染逻辑等。通过自定义插件可以很好地实现功能解耦,更好地进行管理、编排代码以及后续维护。\n\n## 使用场景\n\n### 添加额外的组件、渲染逻辑等\n\n- **额外的组件**:如内置插件的`提示框`、`小地图`、`对齐线`、`网格线`、`上下文菜单`、`水印`等\n- **渲染逻辑**:如内置插件的`边绑定`,以及`远程数据加载`([示例](#远程数据加载))等\n\n### 内置插件无法满足需求\n\n当内置插件无法完全满足业务需求时,用户也可以通过自定义插件(继承内置插件)进行调整和修改。\n\n_(如果需要内置插件支持的特性是较通用的,或者内置插件存在 Bug ,这种时候欢迎大家到 [Github](https://github.com/antvis/G6) 提 Issue 或者 PR )_\n\n## 自定义插件示例\n\n与交互类似,插件的实现也相当灵活,你可以以你喜欢的风格实现你的插件。\n\n下面列举几个简单的自定义插件实现:\n\n### 远程数据加载\n\n在图实例化过程中自动加载远程数据:\n\n```typescript\nimport { BasePlugin } from '@antv/g6';\nimport type { BasePluginOptions, RuntimeContext } from '@antv/g6';\n\ninterface RemoteDataSourceOptions extends BasePluginOptions {}\n\nclass RemoteDataSource extends BasePlugin {\n constructor(context: RuntimeContext, options: RemoteDataSourceOptions) {\n super(context, options);\n this.loadData();\n }\n\n private async loadData() {\n // mock remote data\n const data = {\n nodes: [\n { id: 'node-1', x: 100, y: 100 },\n { id: 'node-2', x: 200, y: 200 },\n ],\n edges: [{ source: 'node-1', target: 'node-2' }],\n };\n\n const { graph } = this.context;\n graph.setData(data);\n await graph.render();\n }\n}\n```\n\n- 在这个例子中,我们模拟实现了一个数据加载插件,在使用该插件后,实例化 Graph 时不用再传入数据,该插件会自动加载远程数据。\n\n- `BasePlugin` 是所有插件的基类,每个自定义插件都需要继承这个基类实现。\n\n\n\n### 自动判断节点数量开启或关闭动画\n\n```typescript\nimport type { BasePluginOptions, RuntimeContext } from '@antv/g6';\nimport { BasePlugin, GraphEvent } from '@antv/g6';\n\ninterface AutoSwitchAnimationOptions extends BasePluginOptions {\n maxLength: number; // 节点数量达到这个值后关闭全局动画\n}\n\nclass AutoSwitchAnimation extends BasePlugin {\n static defaultOptions: Partial = {\n maxLength: 1000,\n };\n constructor(context: RuntimeContext, options: AutoSwitchAnimationOptions) {\n super(context, options);\n this.bindEvents();\n }\n private bindEvents() {\n const { graph } = this.context;\n graph.on(GraphEvent.BEFORE_RENDER, this.switchAnimation);\n }\n private switchAnimation() {\n const { graph } = this.context;\n graph.setOptions({\n animation: graph.getNodeData().length < this.options.maxLength,\n });\n }\n private unbindEvents() {\n const { graph } = this.context;\n graph.off(GraphEvent.BEFORE_RENDER, this.switchAnimation);\n }\n destroy() {\n this.unbindEvents();\n super.destroy();\n }\n}\n```\n\n- 在这个例子中,我们监听 `GraphEvent.BEFORE_RENDER` 事件,在响应中判断当前节点数量是否大于指定值,是的话则关闭全局动画,否则开启\n- `maxLength` 是定义的配置项,可在初始化画布实例时传入具体配置,[插件配置](#配置插件)\n\n## 注册插件\n\n通过 G6 提供的 register 方法注册即可\n\n```typescript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { MyCustomPlugin } from './my-custom-plugin';\n\nregister(ExtensionCategory.PLUGIN, 'my-custom-plugin', MyCustomPlugin);\n```\n\n## 配置插件\n\n- 可在 `plugins` 中传入插件类型名称或配置参数对象,详见[配置插件](/manual/plugin/overview#配置方式)\n\n- 比如前面的[自动判断节点数量开启或关闭动画](#自动判断节点数量开启或关闭动画),配置如下:\n\n ```typescript\n const graph = new Graph({\n // 其他配置\n plugins: [\n {\n type: 'auto-switch-animation',\n maxLength: 500,\n },\n ],\n });\n ```\n\n\n\n---\ntitle: EdgeBundling\norder: 4\n---\n\n## Overview\n\nEdge bundling is a graph visualization technique used to reduce visual clutter in complex network graphs and to reveal high-level patterns and structures in the graph. Its purpose is to bundle adjacent edges together.\n\nThe edge bundling plugin provided in G6 is based on the implementation of the [FEDB (Force-Directed Edge Bundling for Graph Visualization)](https://classes.engineering.wustl.edu/cse557/readings/holten-edgebundling.pdf) paper: modeling edges as flexible springs that can attract each other and bundling them through a self-organizing process.\n\n## Use Cases\n\nThe edge bundling plugin is mainly suitable for the following scenarios:\n\n- Reducing visual clutter in complex network graphs\n- Revealing high-level patterns and structures in the graph\n- Improving the readability and aesthetics of large-scale graph data\n\n## Basic Usage\n\nBelow is a simple example of initializing the EdgeBundling plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n bundleThreshold: 0.6,\n cycles: 6,\n divisions: 3,\n divRate: 2,\n iterations: 90,\n iterRate: 2 / 3,\n K: 0.1,\n lambda: 0.1,\n },\n ],\n});\n```\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ | --------------- | -------- |\n| type | Plugin type, used to identify the plugin as an edge bundling plugin | string | `edge-bundling` | ✓ |\n| key | Unique identifier for the plugin, can be used to get the plugin instance or update plugin options | string | - | |\n| bundleThreshold | Edge compatibility threshold, determines which edges should be bundled together, the larger the value, the fewer edges are bundled, [example](#bundlethreshold) | number | 0.6 | |\n| cycles | Number of simulation cycles, controls the number of execution rounds of the edge bundling simulation | number | 6 | |\n| divisions | Initial number of cut points, in subsequent cycles, the number of cut points will gradually increase according to divRate, affecting the degree of edge subdivision | number | 1 | |\n| divRate | Growth rate of cut points, determines the growth rate of cut points in each cycle | number | 2 | |\n| iterations | Specifies the number of iterations executed in the first cycle, in subsequent cycles, the number of iterations will gradually decrease according to iterRate, affecting the accuracy of the simulation | number | 90 | |\n| iterRate | Iteration decrement rate, controls the reduction ratio of iterations in each cycle | number | 2/3 | |\n| K | Edge strength, affects the attraction and repulsion between edges, [example](#k) | number | 0.1 | |\n| lambda | Initial step size, in subsequent cycles, the step size will double increment, affecting the magnitude of node movement during edge bundling | number | 0.1 | |\n\n### bundleThreshold\n\nEdge compatibility threshold, determines which edges should be bundled together. The larger the value, the fewer edges are bundled, and vice versa.\n\n- A lower bundleThreshold value (e.g., 0.4) will cause more edges to be bundled together, forming a more pronounced bundling effect.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n bundleThreshold: 0.4, // Lower edge compatibility threshold\n },\n ],\n});\n```\n\nThe effect is as follows:\n\"Lower\n\n- A higher bundleThreshold value (e.g., 0.8) will cause fewer edges to be bundled together, maintaining more independent edges.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n bundleThreshold: 0.8, // Higher edge compatibility threshold\n },\n ],\n});\n```\n\nThe effect is as follows:\n\"Higher\n\n### K\n\nEdge strength, affects the attraction and repulsion between edges. A higher K value will make the attraction between edges stronger, resulting in a tighter bundling effect.\n\n- A lower K value (e.g., 0.05) will make the attraction between edges weaker, resulting in a weaker bundling effect.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n K: 0.05, // Lower edge strength\n },\n ],\n});\n```\n\nThe effect is as follows:\n\"Lower\n\n- A higher K value (e.g., 0.2) will make the attraction between edges stronger, resulting in a more pronounced bundling effect.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n K: 0.2, // Higher edge strength\n },\n ],\n});\n```\n\nThe effect is as follows:\n\"Higher\n\n## Code Examples\n\n### Basic Edge Bundling\n\nThe simplest way is to use the preset configuration directly:\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: ['edge-bundling'],\n});\n```\n\n### Custom Styles\n\nYou can customize the parameters of edge bundling as needed:\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'edge-bundling',\n bundleThreshold: 0.8, // Higher edge compatibility threshold\n cycles: 8, // More simulation cycles\n K: 0.2, // Stronger edge strength\n },\n ],\n});\n```\n\n### Dynamic Update of Edge Bundling\n\nUse the key identifier to dynamically update edge bundling properties at runtime:\n\n```js\n// Initial configuration\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'edge-bundling',\n key: 'my-edge-bundling',\n bundleThreshold: 0.6,\n },\n ],\n});\n\n// Subsequent dynamic update\ngraph.updatePlugin({\n key: 'my-edge-bundling',\n bundleThreshold: 0.8, // Update edge compatibility threshold\n cycles: 10, // Update number of simulation cycles\n});\n```\n\n## Practical Examples\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/circular.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data,\n layout: {\n type: 'circular',\n },\n node: { style: { size: 20 } },\n behaviors: ['drag-canvas', 'drag-element'],\n plugins: [\n {\n key: 'edge-bundling',\n type: 'edge-bundling',\n bundleThreshold: 0.1,\n },\n ],\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: 边绑定 EdgeBundling\norder: 4\n---\n\n## 概述\n\n边绑定(Edge Bundling)是一种图可视化技术,用于减少复杂网络图中的视觉混乱,并展示图中的高级别模式和结构。其目的是将相邻的边捆绑在一起。\n\nG6 中提供的边绑定插件是基于 [FEDB(Force-Directed Edge Bundling for Graph Visualization)](https://classes.engineering.wustl.edu/cse557/readings/holten-edgebundling.pdf)论文的实现:将边建模为可以相互吸引的柔性弹簧,通过自组织的方式进行捆绑。\n\n## 使用场景\n\n边绑定插件主要适用于以下场景:\n\n- 减少复杂网络图中的视觉混乱\n- 揭示图中的高级别模式和结构\n- 提高大规模图数据的可读性和美观性\n\n## 基本用法\n\n以下是一个简单的 EdgeBundling 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n bundleThreshold: 0.6,\n cycles: 6,\n divisions: 3,\n divRate: 2,\n iterations: 90,\n iterRate: 2 / 3,\n K: 0.1,\n lambda: 0.1,\n },\n ],\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | -------------------------------------------------------------------------------------------------- | ------ | --------------- | ---- |\n| type | 插件类型,用于标识该插件为边捆绑插件 | string | `edge-bundling` | ✓ |\n| key | 插件的唯一标识,可用于获取插件实例或更新插件选项 | string | - | |\n| bundleThreshold | 边兼容性阈值,该值决定了哪些边应该被绑定在一起,值越大,绑定的边越少,[示例](#bundlethreshold) | number | 0.6 | |\n| cycles | 模拟周期数,控制边捆绑模拟的执行轮数 | number | 6 | |\n| divisions | 初始切割点数,在后续的周期中,切割点数将根据 divRate 逐步递增,影响边的细分程度 | number | 1 | |\n| divRate | 切割点数增长率,决定了每一轮周期中切割点数的增长幅度 | number | 2 | |\n| iterations | 指定在第一个周期中执行的迭代次数,在后续的周期中,迭代次数将根据 iterRate 逐步递减,影响模拟的精度 | number | 90 | |\n| iterRate | 迭代次数递减率,控制每一轮周期中迭代次数的减少比例 | number | 2\\/3 | |\n| K | 边的强度,影响边之间的吸引力和排斥力,[示例](#k) | number | 0.1 | |\n| lambda | 初始步长,在后续的周期中,步长将双倍递增,影响边捆绑过程中节点移动的幅度 | number | 0.1 | |\n\n### bundleThreshold\n\n边兼容性阈值,该值决定了哪些边应该被绑定在一起。值越大,绑定的边越少,反之则绑定的边越多。\n\n- 较低的 bundleThreshold 值(如 0.4)会使更多的边被绑定在一起,形成更明显的捆绑效果。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n bundleThreshold: 0.4, // 较低的边兼容性阈值\n },\n ],\n});\n```\n\n效果如下:\n\"较低的边兼容性阈值\"\n\n- 较高的 bundleThreshold 值(如 0.8)会使较少的边被绑定在一起,保持更多的独立边。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n bundleThreshold: 0.8, // 较高的边兼容性阈值\n },\n ],\n});\n```\n\n效果如下:\n\"较高的边兼容性阈值\"\n\n### K\n\n边的强度,影响边之间的吸引力和排斥力。较高的 K 值会使边之间的吸引力更强,从而形成更紧密的捆绑效果。\n\n- 较低的 K 值(如 0.05)会使边之间的吸引力较弱,边的捆绑效果较弱。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n K: 0.05, // 较低的边强度\n },\n ],\n});\n```\n\n效果如下:\n\"较低的边强度\"\n\n- 较高的 K 值(如 0.2)会使边之间的吸引力较强,边的捆绑效果更明显。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-bundling',\n K: 0.2, // 较高的边强度\n },\n ],\n});\n```\n\n效果如下:\n\"较高的边强度\"\n\n## 代码示例\n\n### 基础边绑定\n\n最简单的方式是直接使用预设配置:\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: ['edge-bundling'],\n});\n```\n\n### 自定义样式\n\n您可以根据需要自定义边绑定的参数:\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'edge-bundling',\n bundleThreshold: 0.8, // 更高的边兼容性阈值\n cycles: 8, // 更多模拟周期\n K: 0.2, // 更强的边强度\n },\n ],\n});\n```\n\n### 动态更新边绑定\n\n使用 key 标识符可以在运行时动态更新边绑定属性:\n\n```js\n// 初始化配置\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'edge-bundling',\n key: 'my-edge-bundling',\n bundleThreshold: 0.6,\n },\n ],\n});\n\n// 后续动态更新\ngraph.updatePlugin({\n key: 'my-edge-bundling',\n bundleThreshold: 0.8, // 更新边兼容性阈值\n cycles: 10, // 更新模拟周期数\n});\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/circular.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data,\n layout: {\n type: 'circular',\n },\n node: { style: { size: 20 } },\n behaviors: ['drag-canvas', 'drag-element'],\n plugins: [\n {\n key: 'edge-bundling',\n type: 'edge-bundling',\n bundleThreshold: 0.1,\n },\n ],\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: EdgeFilterLens\norder: 5\n---\n\n## Overview\n\nThe Edge Filter Lens plugin allows you to keep the edges of interest within the lens range, while other edges will not be displayed in that range. This is an important visualization exploration tool that can help users focus on edge relationships in specific areas.\n\n## Use Cases\n\n- Need to focus on viewing edge relationships in local areas\n- Highlight connections between specific nodes in complex networks\n\n## Basic Usage\n\nBelow is a simple example of initializing the EdgeFilterLens plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-filter-lens',\n trigger: 'pointermove', // Follow mouse movement\n r: 60, // Set lens radius\n nodeType: 'both', // Edge display condition\n },\n ],\n});\n```\n\n## Online Experience\n\n\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | -------- |\n| type | Plugin type | string | `edge-filter-lens` | ✓ |\n| key | Unique identifier for the plugin, can be used to get the plugin instance or update plugin options | string | - | |\n| trigger | Method to move the lens:
- `pointermove`: The lens always follows the mouse movement
- `click`: Move the lens to the click position when clicking on the canvas
- `drag`: Move the lens by dragging | `pointermove` \\| `click` \\| `drag` | `pointermove` | |\n| r | Radius of the lens | number | 60 | |\n| maxR | Maximum radius of the lens | number | Half of the smaller dimension of the canvas | |\n| minR | Minimum radius of the lens | number | 0 | |\n| scaleRBy | Method to scale the lens radius: `wheel`: Scale the lens radius by the wheel | `wheel` | - | |\n| nodeType | Edge display condition:
- `both`: The edge is displayed only when both the source and target nodes are in the lens
- `source`: The edge is displayed only when the source node is in the lens
- `target`: The edge is displayed only when the target node is in the lens
- `either`: The edge is displayed as long as either the source or target node is in the lens | `both` \\| `source` \\| `target` \\| `either` | `both` | |\n| filter | Filter out elements that are never displayed in the lens | (id: string, elementType: `node` \\| `edge` \\| `combo`) => boolean | () => true | |\n| style | Style of the lens, [configuration options](#style) | object | | |\n| nodeStyle | Style of nodes in the lens | [NodeStyle](/en/manual/element/node/base-node#style) \\| ((datum: [NodeData](/en/manual/data#节点数据nodedata)) => [NodeStyle](/en/manual/element/node/base-node#style)) | `{ label: false }` | |\n| edgeStyle | Style of edges in the lens | [EdgeStyle](/en/manual/element/edge/base-edge#style) \\| ((datum: [EdgeData](/en/manual/data#边数据edgedata)) => [EdgeStyle](/en/manual/element/edge/base-edge#style)) | `{ label: true }` | |\n| preventDefault | Whether to prevent default events | boolean | true | |\n\n### style\n\nStyle properties of the circular lens.\n\n| Property | Description | Type | Default Value |\n| ------------- | ------------------ | ----------------------------- | ------------- |\n| fill | Fill color | string \\| Pattern \\| null | `#fff` |\n| stroke | Stroke color | string \\| Pattern \\| null | `#000` |\n| opacity | Overall opacity | number \\| string | 1 |\n| fillOpacity | Fill opacity | number \\| string | 0.8 |\n| strokeOpacity | Stroke opacity | number \\| string | - |\n| lineWidth | Line width | number \\| string | 2 |\n| lineCap | Line cap style | `butt` \\| `round` \\| `square` | - |\n| lineJoin | Line join style | `miter` \\| `round` \\| `bevel` | - |\n| shadowColor | Shadow color | string | - |\n| shadowBlur | Shadow blur degree | number | - |\n| shadowOffsetX | Shadow X offset | number | - |\n| shadowOffsetY | Shadow Y offset | number | - |\n\nFor complete style properties, refer to [Element - Node - Built-in Node - General Style Properties - style](/en/manual/element/node/base-node#style)\n\n## Code Examples\n\n### Basic Usage\n\nThe simplest configuration method:\n\n```js\nconst graph = new Graph({\n plugins: ['edge-filter-lens'],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 300,\n data: {\n nodes: [\n // Upper evacuation area\n { id: 'node1', style: { x: 150, y: 60, label: 'Node 1' } },\n { id: 'node2', style: { x: 100, y: 40, label: 'Node 2' } },\n { id: 'node3', style: { x: 200, y: 35, label: 'Node 3' } },\n { id: 'node4', style: { x: 150, y: 30, label: 'Node 4' } },\n\n // Middle area\n { id: 'node5', style: { x: 220, y: 140, label: 'Node 5' } },\n { id: 'node6', style: { x: 280, y: 160, label: 'Node 6' } },\n { id: 'node7', style: { x: 220, y: 120, label: 'Node 7' } },\n { id: 'node8', style: { x: 260, y: 100, label: 'Node 8' } },\n { id: 'node9', style: { x: 240, y: 130, label: 'Node 9' } },\n { id: 'node10', style: { x: 300, y: 110, label: 'Node 10' } },\n\n // Lower area\n { id: 'node11', style: { x: 240, y: 200, label: 'Node 11' } },\n { id: 'node12', style: { x: 280, y: 220, label: 'Node 12' } },\n { id: 'node13', style: { x: 300, y: 190, label: 'Node 13' } },\n { id: 'node14', style: { x: 320, y: 210, label: 'Node 14' } },\n ],\n edges: [\n // Upper connections\n { id: 'edge1', source: 'node1', target: 'node2' },\n { id: 'edge2', source: 'node2', target: 'node3' },\n { id: 'edge3', source: 'node3', target: 'node4' },\n\n // Middle connections\n { id: 'edge4', source: 'node5', target: 'node6' },\n { id: 'edge5', source: 'node6', target: 'node7' },\n { id: 'edge6', source: 'node7', target: 'node8' },\n { id: 'edge7', source: 'node8', target: 'node9' },\n { id: 'edge8', source: 'node9', target: 'node10' },\n\n // Lower connections\n { id: 'edge9', source: 'node11', target: 'node12' },\n { id: 'edge10', source: 'node12', target: 'node13' },\n { id: 'edge11', source: 'node13', target: 'node14' },\n\n // Cross-region connections\n { id: 'edge12', source: 'node4', target: 'node8' },\n { id: 'edge13', source: 'node7', target: 'node11' },\n { id: 'edge14', source: 'node10', target: 'node13' },\n ],\n },\n node: {\n style: {\n size: 20,\n },\n },\n plugins: ['edge-filter-lens'],\n});\n\ngraph.render();\n```\n\n### Custom Styles\n\nYou can customize the appearance and behavior of the lens:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-filter-lens',\n r: 80,\n style: {\n fill: '#f0f5ff', // Fill color of the lens area\n fillOpacity: 0.6, // Opacity of the fill area\n stroke: '#7e3feb', // Change lens border to purple\n strokeOpacity: 0.8, // Opacity of the border\n lineWidth: 1.5, // Line width of the border\n },\n nodeStyle: {\n size: 24, // Enlarge nodes\n fill: '#7e3feb', // Purple fill\n stroke: '#5719c9', // Dark purple stroke\n lineWidth: 1, // Thin border\n label: true, // Show label\n labelFill: '#ffffff', // White text\n labelFontSize: 14, // Enlarge text\n labelFontWeight: 'bold', // Bold text\n },\n edgeStyle: {\n stroke: '#8b9baf', // Gray edge\n lineWidth: 2, // Thicken edge line\n label: true, // Show label\n labelFill: '#5719c9', // Dark purple text\n opacity: 0.8, // Appropriate opacity\n },\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 300,\n data: {\n nodes: [\n // Upper evacuation area\n { id: 'node1', style: { x: 150, y: 60, label: 'Node 1' } },\n { id: 'node2', style: { x: 100, y: 40, label: 'Node 2' } },\n { id: 'node3', style: { x: 200, y: 35, label: 'Node 3' } },\n { id: 'node4', style: { x: 150, y: 30, label: 'Node 4' } },\n\n // Middle area\n { id: 'node5', style: { x: 220, y: 140, label: 'Node 5' } },\n { id: 'node6', style: { x: 280, y: 160, label: 'Node 6' } },\n { id: 'node7', style: { x: 220, y: 120, label: 'Node 7' } },\n { id: 'node8', style: { x: 260, y: 100, label: 'Node 8' } },\n { id: 'node9', style: { x: 240, y: 130, label: 'Node 9' } },\n { id: 'node10', style: { x: 300, y: 110, label: 'Node 10' } },\n\n // Lower area\n { id: 'node11', style: { x: 240, y: 200, label: 'Node 11' } },\n { id: 'node12', style: { x: 280, y: 220, label: 'Node 12' } },\n { id: 'node13', style: { x: 300, y: 190, label: 'Node 13' } },\n { id: 'node14', style: { x: 320, y: 210, label: 'Node 14' } },\n ],\n edges: [\n // Upper connections\n { id: 'edge1', source: 'node1', target: 'node2' },\n { id: 'edge2', source: 'node2', target: 'node3' },\n { id: 'edge3', source: 'node3', target: 'node4' },\n\n // Middle connections\n { id: 'edge4', source: 'node5', target: 'node6' },\n { id: 'edge5', source: 'node6', target: 'node7' },\n { id: 'edge6', source: 'node7', target: 'node8' },\n { id: 'edge7', source: 'node8', target: 'node9' },\n { id: 'edge8', source: 'node9', target: 'node10' },\n\n // Lower connections\n { id: 'edge9', source: 'node11', target: 'node12' },\n { id: 'edge10', source: 'node12', target: 'node13' },\n { id: 'edge11', source: 'node13', target: 'node14' },\n\n // Cross-region connections\n { id: 'edge12', source: 'node4', target: 'node8' },\n { id: 'edge13', source: 'node7', target: 'node11' },\n { id: 'edge14', source: 'node10', target: 'node13' },\n ],\n },\n node: {\n style: {\n size: 20,\n },\n },\n edge: {\n style: {\n stroke: '#91d5ff',\n lineWidth: 1,\n },\n },\n plugins: [\n {\n type: 'edge-filter-lens',\n r: 80,\n style: {\n fill: '#f0f5ff', // Fill color of the lens area\n fillOpacity: 0.6, // Opacity of the fill area\n stroke: '#7e3feb', // Change lens border to purple\n strokeOpacity: 0.8, // Opacity of the border\n lineWidth: 1.5, // Line width of the border\n },\n nodeStyle: {\n size: 24, // Enlarge nodes\n fill: '#7e3feb', // Purple fill\n stroke: '#5719c9', // Dark purple stroke\n lineWidth: 1, // Thin border\n label: true, // Show label\n labelFill: '#ffffff', // White text\n labelFontSize: 14, // Enlarge text\n labelFontWeight: 'bold', // Bold text\n },\n edgeStyle: {\n stroke: '#8b9baf', // Gray edge\n lineWidth: 2, // Thicken edge line\n label: true, // Show label\n labelFill: '#5719c9', // Dark purple text\n opacity: 0.8, // Appropriate opacity\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n## Practical Examples\n\n- [Edge Filter Lens](/en/examples/plugin/edge-filter-lens/#basic)\n\n\n\n---\ntitle: 边过滤镜 EdgeFilterLens\norder: 5\n---\n\n## 概述\n\n边过滤镜插件可以将关注的边保留在过滤镜范围内,其他边将在该范围内不显示。这是一个重要的可视化探索工具,可以帮助用户聚焦于特定区域的边关系。\n\n## 使用场景\n\n- 需要聚焦查看局部区域的边关系\n- 在复杂网络中突出显示特定节点之间的连接\n\n## 基本用法\n\n以下是一个简单的 EdgeFilterLens 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-filter-lens',\n trigger: 'pointermove', // 跟随鼠标移动\n r: 60, // 设置透镜半径\n nodeType: 'both', // 边的显示条件\n },\n ],\n});\n```\n\n## 在线体验\n\n\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ---- |\n| type | 插件类型 | string | `edge-filter-lens` | ✓ |\n| key | 插件的唯一标识,可用于获取插件实例或更新插件选项 | string | - | |\n| trigger | 移动透镜的方式:
- `pointermove`:透镜始终跟随鼠标移动
- `click`:点击画布时移动透镜到点击位置
- `drag`:通过拖拽方式移动透镜 | `pointermove` \\| `click` \\| `drag` | `pointermove` | |\n| r | 透镜的半径 | number | 60 | |\n| maxR | 透镜的最大半径 | number | 画布宽高最小值的一半 | |\n| minR | 透镜的最小半径 | number | 0 | |\n| scaleRBy | 缩放透镜半径的方式:`wheel`:通过滚轮缩放透镜的半径 | `wheel` | - | |\n| nodeType | 边显示的条件:
- `both`:只有起始节点和目标节点都在透镜中时,边才会显示
- `source`:只有起始节点在透镜中时,边才会显示
- `target`:只有目标节点在透镜中时,边才会显示
- `either`:只要起始节点或目标节点有一个在透镜中时,边就会显示 | `both` \\| `source` \\| `target` \\| `either` | `both` | |\n| filter | 过滤出始终不在透镜中显示的元素 | (id: string, elementType: `node` \\| `edge` \\| `combo`) => boolean | () => true | |\n| style | 透镜的样式,[配置项](#style) | object | | |\n| nodeStyle | 在透镜中节点的样式 | [NodeStyle](/manual/element/node/base-node#style) \\| ((datum: [NodeData](/manual/data#节点数据nodedata)) => [NodeStyle](/manual/element/node/base-node#style)) | `{ label: false }` | |\n| edgeStyle | 在透镜中边的样式 | [EdgeStyle](/manual/element/edge/base-edge#style) \\| ((datum: [EdgeData](/manual/data#边数据edgedata)) => [EdgeStyle](/manual/element/edge/base-edge#style)) | `{ label: true }` | |\n| preventDefault | 是否阻止默认事件 | boolean | true | |\n\n### style\n\n圆形透镜的样式属性。\n\n| 属性 | 描述 | 类型 | 默认值 |\n| ------------- | --------------- | ----------------------------- | ------ |\n| fill | 填充颜色 | string \\| Pattern \\| null | `#fff` |\n| stroke | 描边颜色 | string \\| Pattern \\| null | `#000` |\n| opacity | 整体透明度 | number \\| string | 1 |\n| fillOpacity | 填充透明度 | number \\| string | 0.8 |\n| strokeOpacity | 描边透明度 | number \\| string | - |\n| lineWidth | 线宽度 | number \\| string | 2 |\n| lineCap | 线段端点样式 | `butt` \\| `round` \\| `square` | - |\n| lineJoin | 线段连接处样式 | `miter` \\| `round` \\| `bevel` | - |\n| shadowColor | 阴影颜色 | string | - |\n| shadowBlur | 阴影模糊程度 | number | - |\n| shadowOffsetX | 阴影 X 方向偏移 | number | - |\n| shadowOffsetY | 阴影 Y 方向偏移 | number | - |\n\n完整样式属性参考 [元素 -节点 - 内置节点 - 通用样式属性 - style](/manual/element/node/base-node#style)\n\n## 代码示例\n\n### 基础用法\n\n最简单的配置方式:\n\n```js\nconst graph = new Graph({\n plugins: ['edge-filter-lens'],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 300,\n data: {\n nodes: [\n // 上部疏散区域\n { id: 'node1', style: { x: 150, y: 60, label: 'Node 1' } },\n { id: 'node2', style: { x: 100, y: 40, label: 'Node 2' } },\n { id: 'node3', style: { x: 200, y: 35, label: 'Node 3' } },\n { id: 'node4', style: { x: 150, y: 30, label: 'Node 4' } },\n\n // 中部区域\n { id: 'node5', style: { x: 220, y: 140, label: 'Node 5' } },\n { id: 'node6', style: { x: 280, y: 160, label: 'Node 6' } },\n { id: 'node7', style: { x: 220, y: 120, label: 'Node 7' } },\n { id: 'node8', style: { x: 260, y: 100, label: 'Node 8' } },\n { id: 'node9', style: { x: 240, y: 130, label: 'Node 9' } },\n { id: 'node10', style: { x: 300, y: 110, label: 'Node 10' } },\n\n // 下部区域\n { id: 'node11', style: { x: 240, y: 200, label: 'Node 11' } },\n { id: 'node12', style: { x: 280, y: 220, label: 'Node 12' } },\n { id: 'node13', style: { x: 300, y: 190, label: 'Node 13' } },\n { id: 'node14', style: { x: 320, y: 210, label: 'Node 14' } },\n ],\n edges: [\n // 上部连接\n { id: 'edge1', source: 'node1', target: 'node2' },\n { id: 'edge2', source: 'node2', target: 'node3' },\n { id: 'edge3', source: 'node3', target: 'node4' },\n\n // 中部连接\n { id: 'edge4', source: 'node5', target: 'node6' },\n { id: 'edge5', source: 'node6', target: 'node7' },\n { id: 'edge6', source: 'node7', target: 'node8' },\n { id: 'edge7', source: 'node8', target: 'node9' },\n { id: 'edge8', source: 'node9', target: 'node10' },\n\n // 下部连接\n { id: 'edge9', source: 'node11', target: 'node12' },\n { id: 'edge10', source: 'node12', target: 'node13' },\n { id: 'edge11', source: 'node13', target: 'node14' },\n\n // 跨区域连接\n { id: 'edge12', source: 'node4', target: 'node8' },\n { id: 'edge13', source: 'node7', target: 'node11' },\n { id: 'edge14', source: 'node10', target: 'node13' },\n ],\n },\n node: {\n style: {\n size: 20,\n },\n },\n plugins: ['edge-filter-lens'],\n});\n\ngraph.render();\n```\n\n### 自定义样式\n\n可以自定义透镜的外观和行为:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'edge-filter-lens',\n r: 80,\n style: {\n fill: '#f0f5ff', // 透镜区域的填充颜色\n fillOpacity: 0.6, // 填充区域的透明度\n stroke: '#7e3feb', // 透镜边框改为紫色\n strokeOpacity: 0.8, // 边框的透明度\n lineWidth: 1.5, // 边框的线宽\n },\n nodeStyle: {\n size: 24, // 放大节点\n fill: '#7e3feb', // 紫色填充\n stroke: '#5719c9', // 深紫色描边\n lineWidth: 1, // 细边框\n label: true, // 显示标签\n labelFill: '#ffffff', // 白色文字\n labelFontSize: 14, // 放大文字\n labelFontWeight: 'bold', // 文字加粗\n },\n edgeStyle: {\n stroke: '#8b9baf', // 灰色边\n lineWidth: 2, // 加粗边线\n label: true, // 显示标签\n labelFill: '#5719c9', // 深紫色文字\n opacity: 0.8, // 适当的透明度\n },\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 300,\n data: {\n nodes: [\n // 上部疏散区域\n { id: 'node1', style: { x: 150, y: 60, label: 'Node 1' } },\n { id: 'node2', style: { x: 100, y: 40, label: 'Node 2' } },\n { id: 'node3', style: { x: 200, y: 35, label: 'Node 3' } },\n { id: 'node4', style: { x: 150, y: 30, label: 'Node 4' } },\n\n // 中部区域\n { id: 'node5', style: { x: 220, y: 140, label: 'Node 5' } },\n { id: 'node6', style: { x: 280, y: 160, label: 'Node 6' } },\n { id: 'node7', style: { x: 220, y: 120, label: 'Node 7' } },\n { id: 'node8', style: { x: 260, y: 100, label: 'Node 8' } },\n { id: 'node9', style: { x: 240, y: 130, label: 'Node 9' } },\n { id: 'node10', style: { x: 300, y: 110, label: 'Node 10' } },\n\n // 下部区域\n { id: 'node11', style: { x: 240, y: 200, label: 'Node 11' } },\n { id: 'node12', style: { x: 280, y: 220, label: 'Node 12' } },\n { id: 'node13', style: { x: 300, y: 190, label: 'Node 13' } },\n { id: 'node14', style: { x: 320, y: 210, label: 'Node 14' } },\n ],\n edges: [\n // 上部连接\n { id: 'edge1', source: 'node1', target: 'node2' },\n { id: 'edge2', source: 'node2', target: 'node3' },\n { id: 'edge3', source: 'node3', target: 'node4' },\n\n // 中部连接\n { id: 'edge4', source: 'node5', target: 'node6' },\n { id: 'edge5', source: 'node6', target: 'node7' },\n { id: 'edge6', source: 'node7', target: 'node8' },\n { id: 'edge7', source: 'node8', target: 'node9' },\n { id: 'edge8', source: 'node9', target: 'node10' },\n\n // 下部连接\n { id: 'edge9', source: 'node11', target: 'node12' },\n { id: 'edge10', source: 'node12', target: 'node13' },\n { id: 'edge11', source: 'node13', target: 'node14' },\n\n // 跨区域连接\n { id: 'edge12', source: 'node4', target: 'node8' },\n { id: 'edge13', source: 'node7', target: 'node11' },\n { id: 'edge14', source: 'node10', target: 'node13' },\n ],\n },\n node: {\n style: {\n size: 20,\n },\n },\n edge: {\n style: {\n stroke: '#91d5ff',\n lineWidth: 1,\n },\n },\n plugins: [\n {\n type: 'edge-filter-lens',\n r: 80,\n style: {\n fill: '#f0f5ff', // 透镜区域的填充颜色\n fillOpacity: 0.6, // 填充区域的透明度\n stroke: '#7e3feb', // 透镜边框改为紫色\n strokeOpacity: 0.8, // 边框的透明度\n lineWidth: 1.5, // 边框的线宽\n },\n nodeStyle: {\n size: 24, // 放大节点\n fill: '#7e3feb', // 紫色填充\n stroke: '#5719c9', // 深紫色描边\n lineWidth: 1, // 细边框\n label: true, // 显示标签\n labelFill: '#ffffff', // 白色文字\n labelFontSize: 14, // 放大文字\n labelFontWeight: 'bold', // 文字加粗\n },\n edgeStyle: {\n stroke: '#8b9baf', // 灰色边\n lineWidth: 2, // 加粗边线\n label: true, // 显示标签\n labelFill: '#5719c9', // 深紫色文字\n opacity: 0.8, // 适当的透明度\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n## 实际案例\n\n- [边过滤镜](/examples/plugin/edge-filter-lens/#basic)\n\n\n\n---\ntitle: Fisheye\norder: 6\n---\n\n## Overview\n\nThe Fisheye plugin is designed for focus+context exploration scenarios. It can magnify the area of interest while ensuring that the context and the relationship between the context and the focus center are not lost. It is an important visualization exploration tool.\n\n## Use Cases\n\n- Highlight certain areas during presentations\n- Magnify details locally without losing the overall view\n\n## Basic Usage\n\nBelow is a simple example of initializing the Fisheye plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fisheye',\n trigger: 'drag', // Move fisheye by dragging\n d: 1.5, // Set distortion factor\n r: 120, // Set fisheye radius\n showDPercent: true, // Show distortion degree\n },\n ],\n});\n```\n\n## Online Experience\n\n\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | -------- |\n| type | Plugin type | string | `fisheye` | ✓ |\n| key | Unique identifier for the plugin, can be used to get the plugin instance or update plugin options | string | - | |\n| trigger | Method to move the fisheye:
- `pointermove`: The fisheye always follows the mouse movement
- `click`: Move the fisheye to the click position when clicking on the canvas
- `drag`: Move the fisheye by dragging | `pointermove` \\| `drag` \\| `click` | `pointermove` | |\n| r | Radius of the fisheye | number | 120 | |\n| maxR | Maximum adjustable radius of the fisheye | number | Half of the smaller dimension of the canvas | |\n| minR | Minimum adjustable radius of the fisheye | number | 0 | |\n| d | Distortion factor | number | 1.5 | |\n| maxD | Maximum adjustable distortion factor of the fisheye | number | 5 | |\n| minD | Minimum adjustable distortion factor of the fisheye | number | 0 | |\n| scaleRBy | Method to adjust the fisheye radius:
- `'wheel'`: Adjust by wheel
- `'drag'`: Adjust by dragging | `wheel` \\| `drag` | - | |\n| scaleDBy | Method to adjust the fisheye distortion factor:
- `'wheel'`: Adjust by wheel
- `'drag'`: Adjust by dragging | `wheel` \\| `drag` | - | |\n| showDPercent | Whether to show the distortion factor value in the fisheye | boolean | true | |\n| style | Style of the fisheye, [configuration options](#style) | object | - | |\n| nodeStyle | Style of nodes in the fisheye | [NodeStyle](/en/manual/element/node/base-node#style) \\| ((datum: [NodeData](/en/manual/data#节点数据nodedata)) => [NodeStyle](/en/manual/element/node/base-node#style)) | `{ label: true }` | |\n| preventDefault | Whether to prevent default events | boolean | true | |\n\n### style\n\nCircular style properties for configuring the appearance of the fisheye.\n\n| Property | Description | Type | Default Value |\n| ------------- | ------------------ | ----------------------------- | ------------- |\n| fill | Fill color | string \\| Pattern \\| null | `#ccc` |\n| stroke | Stroke color | string \\| Pattern \\| null | `#000` |\n| opacity | Overall opacity | number \\| string | - |\n| fillOpacity | Fill opacity | number \\| string | 0.1 |\n| strokeOpacity | Stroke opacity | number \\| string | - |\n| lineWidth | Line width | number \\| string | 2 |\n| lineCap | Line cap style | `butt` \\| `round` \\| `square` | - |\n| lineJoin | Line join style | `miter` \\| `round` \\| `bevel` | - |\n| shadowColor | Shadow color | string | - |\n| shadowBlur | Shadow blur degree | number | - |\n| shadowOffsetX | Shadow X offset | number | - |\n| shadowOffsetY | Shadow Y offset | number | - |\n\nFor complete style properties, refer to [Element - Node - Built-in Node - General Style Properties - style](/en/manual/element/node/base-node#style)\n\n### Zoom Control\n\n`scaleRBy` and `scaleDBy` can be used to control the adjustment method of the fisheye's radius and distortion factor respectively:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fisheye',\n // Adjust radius by wheel\n scaleRBy: 'wheel',\n // Adjust distortion factor by dragging\n scaleDBy: 'drag',\n // Set range for radius and distortion factor\n minR: 50,\n maxR: 200,\n minD: 1,\n maxD: 3,\n },\n ],\n});\n```\n\nNote: When `trigger`, `scaleRBy`, and `scaleDBy` are all set to `'drag'`, the priority order is `trigger` > `scaleRBy` > `scaleDBy`, and only the highest priority configuration item will bind the drag event. Similarly, if `scaleRBy` and `scaleDBy` are both set to `'wheel'`, only `scaleRBy` will bind the wheel event.\n\n## Code Examples\n\n### Basic Usage\n\nThe simplest configuration method:\n\n```js\nconst graph = new Graph({\n plugins: ['fisheye'],\n});\n```\n\n### Custom Styles\n\nYou can customize the appearance and behavior of the fisheye:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fisheye',\n r: 150,\n d: 2,\n style: {\n fill: '#2f54eb', // Fill color of the fisheye area\n fillOpacity: 0.2, // Opacity of the fill area\n stroke: '#1d39c4', // Color of the fisheye border\n strokeOpacity: 0.8, // Opacity of the border\n lineWidth: 1.5, // Line width of the border\n shadowColor: '#1d39c4', // Shadow color\n shadowBlur: 10, // Shadow blur radius\n shadowOffsetX: 0, // Horizontal shadow offset\n shadowOffsetY: 0, // Vertical shadow offset\n cursor: 'pointer', // Cursor style when hovering\n },\n nodeStyle: {\n // Basic node style\n size: 40, // Node size\n fill: '#d6e4ff', // Node fill color\n stroke: '#2f54eb', // Node border color\n lineWidth: 2, // Node border width\n shadowColor: '#2f54eb', // Node shadow color\n shadowBlur: 5, // Node shadow blur radius\n cursor: 'pointer', // Cursor style when hovering\n\n // Label style\n label: true, // Show label\n labelFontSize: 14, // Label font size\n labelFontWeight: 'bold', // Label font weight\n labelFill: '#1d39c4', // Label text color\n labelBackground: true, // Show label background\n labelBackgroundFill: '#fff', // Label background fill color\n labelBackgroundStroke: '#1d39c4', // Label background border color\n labelBackgroundOpacity: 0.8, // Label background opacity\n labelBackgroundPadding: [4, 8, 4, 8], // Label background padding [top, right, bottom, left]\n\n // Icon style\n icon: true, // Show icon\n iconFontFamily: 'iconfont', // Icon font\n iconText: '\\ue6f6', // Icon Unicode\n iconFill: '#1d39c4', // Icon color\n iconSize: 16, // Icon size\n iconFontWeight: 'normal', // Icon font weight\n },\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 300,\n data: {\n nodes: [\n { id: 'node-1', style: { x: 150, y: 100 } },\n { id: 'node-2', style: { x: 250, y: 100 } },\n { id: 'node-3', style: { x: 200, y: 180 } },\n { id: 'node-4', style: { x: 120, y: 180 } },\n { id: 'node-5', style: { x: 280, y: 180 } },\n ],\n edges: [\n { id: 'edge-1', source: 'node-1', target: 'node-2' },\n { id: 'edge-2', source: 'node-1', target: 'node-3' },\n { id: 'edge-3', source: 'node-2', target: 'node-3' },\n { id: 'edge-4', source: 'node-3', target: 'node-4' },\n { id: 'edge-5', source: 'node-3', target: 'node-5' },\n ],\n },\n node: {\n style: {\n size: 30,\n fill: '#e6f7ff',\n stroke: '#1890ff',\n lineWidth: 1,\n label: false,\n icon: false,\n },\n },\n edge: {\n style: {\n stroke: '#91d5ff',\n lineWidth: 1,\n },\n },\n plugins: [\n {\n type: 'fisheye',\n key: 'fisheye',\n r: 100,\n d: 2,\n style: {\n fill: '#2f54eb', // Fill color of the fisheye area\n fillOpacity: 0.2, // Opacity of the fill area\n stroke: '#1d39c4', // Color of the fisheye border\n strokeOpacity: 0.8, // Opacity of the border\n lineWidth: 1.5, // Line width of the border\n shadowColor: '#1d39c4', // Shadow color\n shadowBlur: 10, // Shadow blur radius\n shadowOffsetX: 0, // Horizontal shadow offset\n shadowOffsetY: 0, // Vertical shadow offset\n cursor: 'pointer', // Cursor style when hovering\n },\n nodeStyle: {\n // Basic node style\n size: 40, // Node size\n fill: '#d6e4ff', // Node fill color\n stroke: '#2f54eb', // Node border color\n lineWidth: 2, // Node border width\n shadowColor: '#2f54eb', // Node shadow color\n shadowBlur: 5, // Node shadow blur radius\n cursor: 'pointer', // Cursor style when hovering\n\n // Label style\n label: true, // Show label\n labelFontSize: 14, // Label font size\n labelFontWeight: 'bold', // Label font weight\n labelFill: '#1d39c4', // Label text color\n labelBackground: true, // Show label background\n labelBackgroundFill: '#fff', // Label background fill color\n labelBackgroundStroke: '#1d39c4', // Label background border color\n labelBackgroundOpacity: 0.8, // Label background opacity\n labelBackgroundPadding: [4, 8, 4, 8], // Label background padding [top, right, bottom, left]\n\n // Icon style\n icon: true, // Show icon\n iconFontFamily: 'iconfont', // Icon font\n iconText: '\\ue6f6', // Icon Unicode\n iconFill: '#1d39c4', // Icon color\n iconSize: 16, // Icon size\n iconFontWeight: 'normal', // Icon font weight\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n## Practical Examples\n\n```js | ob { inject: true }\nimport { Graph, iconfont } from '@antv/g6';\n\nconst style = document.createElement('style');\nstyle.innerHTML = `@import url('${iconfont.css}');`;\ndocument.head.appendChild(style);\n\nfetch('https://assets.antv.antgroup.com/g6/relations.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data,\n node: {\n style: {\n size: (datum) => datum.id.length * 2 + 10,\n label: false,\n labelText: (datum) => datum.id,\n labelBackground: true,\n icon: false,\n iconFontFamily: 'iconfont',\n iconText: '\\ue6f6',\n iconFill: '#fff',\n },\n palette: {\n type: 'group',\n field: (datum) => datum.id,\n color: ['#1783FF', '#00C9C9', '#F08F56', '#D580FF'],\n },\n },\n edge: {\n style: {\n stroke: '#e2e2e2',\n },\n },\n plugins: [{ key: 'fisheye', type: 'fisheye', nodeStyle: { label: true, icon: true } }],\n });\n graph.render();\n });\n```\n\n\n\n---\ntitle: 鱼眼放大镜 Fisheye\norder: 6\n---\n\n## 概述\n\n鱼眼放大镜插件是为 focus+context 的探索场景设计的,它能够在放大关注区域的同时,保证上下文以及上下文与关注中心的关系不丢失,是一个重要的可视化探索工具。\n\n## 使用场景\n\n- 在演示过程中需要突出展示某些区域内容\n- 需要局部放大查看细节时,同时又不想失去整体视图\n\n## 基本用法\n\n以下是一个简单的 Fisheye 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fisheye',\n trigger: 'drag', // 通过拖拽移动鱼眼\n d: 1.5, // 设置畸变因子\n r: 120, // 设置鱼眼半径\n showDPercent: true, // 显示畸变程度\n },\n ],\n});\n```\n\n## 在线体验\n\n\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ---- |\n| type | 插件类型 | string | `fisheye` | ✓ |\n| key | 插件的唯一标识,可用于获取插件实例或更新插件选项 | string | - | |\n| trigger | 用于控制鱼眼放大镜的移动方式,支持以下三种配置:
- `pointermove`:鱼眼放大镜始终跟随鼠标移动
- `click`:点击画布时移动鱼眼放大镜到点击位置
- `drag`:通过拖拽方式移动鱼眼放大镜 | `pointermove` \\| `drag` \\| `click` | `pointermove` | |\n| r | 鱼眼放大镜半径 | number | 120 | |\n| maxR | 鱼眼放大镜可调整的最大半径 | number | 画布宽高的最小值的一半 | |\n| minR | 鱼眼放大镜可调整的最小半径 | number | 0 | |\n| d | 畸变因子 | number | 1.5 | |\n| maxD | 鱼眼放大镜可调整的最大畸变因子 | number | 5 | |\n| minD | 鱼眼放大镜可调整的最小畸变因子 | number | 0 | |\n| scaleRBy | 调整鱼眼放大镜范围半径的方式:
- `'wheel'`:滚轮调整
- `'drag'`:拖拽调整 | `wheel` \\| `drag` | - | |\n| scaleDBy | 调整鱼眼放大镜畸变因子的方式:
- `'wheel'`:滚轮调整
- `'drag'`:拖拽调整 | `wheel` \\| `drag` | - | |\n| showDPercent | 是否在鱼眼放大镜中显示畸变因子数值 | boolean | true | |\n| style | 鱼眼放大镜样式,[配置项](#style) | object | - | |\n| nodeStyle | 在鱼眼放大镜中的节点样式 | [NodeStyle](/manual/element/node/base-node#style) \\| ((datum: [NodeData](/manual/data#节点数据nodedata)) => [NodeStyle](/manual/element/node/base-node#style)) | `{ label: true }` | |\n| preventDefault | 是否阻止默认事件 | boolean | true | |\n\n### style\n\n圆形样式属性,用于配置鱼眼放大镜的外观。\n\n| 属性 | 描述 | 类型 | 默认值 |\n| ------------- | --------------- | ----------------------------- | ------ |\n| fill | 填充颜色 | string \\| Pattern \\| null | `#ccc` |\n| stroke | 描边颜色 | string \\| Pattern \\| null | `#000` |\n| opacity | 整体透明度 | number \\| string | - |\n| fillOpacity | 填充透明度 | number \\| string | 0.1 |\n| strokeOpacity | 描边透明度 | number \\| string | - |\n| lineWidth | 线宽度 | number \\| string | 2 |\n| lineCap | 线段端点样式 | `butt` \\| `round` \\| `square` | - |\n| lineJoin | 线段连接处样式 | `miter` \\| `round` \\| `bevel` | - |\n| shadowColor | 阴影颜色 | string | - |\n| shadowBlur | 阴影模糊程度 | number | - |\n| shadowOffsetX | 阴影 X 方向偏移 | number | - |\n| shadowOffsetY | 阴影 Y 方向偏移 | number | - |\n\n完整样式属性参考 [元素 -节点 - 内置节点 - 通用样式属性 - style](/manual/element/node/base-node#style)\n\n### 缩放控制\n\n通过 `scaleRBy` 和 `scaleDBy` 可以分别控制鱼眼放大镜的半径和畸变因子的调整方式:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fisheye',\n // 通过滚轮调整半径\n scaleRBy: 'wheel',\n // 通过拖拽调整畸变因子\n scaleDBy: 'drag',\n // 设置半径和畸变因子的范围\n minR: 50,\n maxR: 200,\n minD: 1,\n maxD: 3,\n },\n ],\n});\n```\n\n注意:当 `trigger`、`scaleRBy` 和 `scaleDBy` 同时设置为 `'drag'` 时,优先级顺序为 `trigger` > `scaleRBy` > `scaleDBy`,只会为优先级最高的配置项绑定拖拽事件。同理,如果 `scaleRBy` 和 `scaleDBy` 同时设置为 `'wheel'`,只会为 `scaleRBy` 绑定滚轮事件。\n\n## 代码示例\n\n### 基础用法\n\n最简单的配置方式:\n\n```js\nconst graph = new Graph({\n plugins: ['fisheye'],\n});\n```\n\n### 自定义样式\n\n可以自定义鱼眼放大镜的外观和行为:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fisheye',\n r: 150,\n d: 2,\n style: {\n fill: '#2f54eb', // 鱼眼区域的填充颜色\n fillOpacity: 0.2, // 填充区域的透明度\n stroke: '#1d39c4', // 鱼眼边框的颜色\n strokeOpacity: 0.8, // 边框的透明度\n lineWidth: 1.5, // 边框的线宽\n shadowColor: '#1d39c4', // 阴影颜色\n shadowBlur: 10, // 阴影的模糊半径\n shadowOffsetX: 0, // 阴影的水平偏移\n shadowOffsetY: 0, // 阴影的垂直偏移\n cursor: 'pointer', // 鼠标悬停时的指针样式\n },\n nodeStyle: {\n // 节点基础样式\n size: 40, // 节点大小\n fill: '#d6e4ff', // 节点填充颜色\n stroke: '#2f54eb', // 节点边框颜色\n lineWidth: 2, // 节点边框宽度\n shadowColor: '#2f54eb', // 节点阴影颜色\n shadowBlur: 5, // 节点阴影模糊半径\n cursor: 'pointer', // 鼠标悬停时的指针样式\n\n // 标签样式\n label: true, // 是否显示标签\n labelFontSize: 14, // 标签字体大小\n labelFontWeight: 'bold', // 标签字体粗细\n labelFill: '#1d39c4', // 标签文字颜色\n labelBackground: true, // 是否显示标签背景\n labelBackgroundFill: '#fff', // 标签背景填充颜色\n labelBackgroundStroke: '#1d39c4', // 标签背景边框颜色\n labelBackgroundOpacity: 0.8, // 标签背景透明度\n labelBackgroundPadding: [4, 8, 4, 8], // 标签背景内边距 [上,右,下,左]\n\n // 图标样式\n icon: true, // 是否显示图标\n iconFontFamily: 'iconfont', // 图标字体\n iconText: '\\ue6f6', // 图标的 Unicode 编码\n iconFill: '#1d39c4', // 图标颜色\n iconSize: 16, // 图标大小\n iconFontWeight: 'normal', // 图标字体粗细\n },\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 300,\n data: {\n nodes: [\n { id: 'node-1', style: { x: 150, y: 100 } },\n { id: 'node-2', style: { x: 250, y: 100 } },\n { id: 'node-3', style: { x: 200, y: 180 } },\n { id: 'node-4', style: { x: 120, y: 180 } },\n { id: 'node-5', style: { x: 280, y: 180 } },\n ],\n edges: [\n { id: 'edge-1', source: 'node-1', target: 'node-2' },\n { id: 'edge-2', source: 'node-1', target: 'node-3' },\n { id: 'edge-3', source: 'node-2', target: 'node-3' },\n { id: 'edge-4', source: 'node-3', target: 'node-4' },\n { id: 'edge-5', source: 'node-3', target: 'node-5' },\n ],\n },\n node: {\n style: {\n size: 30,\n fill: '#e6f7ff',\n stroke: '#1890ff',\n lineWidth: 1,\n label: false,\n icon: false,\n },\n },\n edge: {\n style: {\n stroke: '#91d5ff',\n lineWidth: 1,\n },\n },\n plugins: [\n {\n type: 'fisheye',\n key: 'fisheye',\n r: 100,\n d: 2,\n style: {\n fill: '#2f54eb', // 鱼眼区域的填充颜色\n fillOpacity: 0.2, // 填充区域的透明度\n stroke: '#1d39c4', // 鱼眼边框的颜色\n strokeOpacity: 0.8, // 边框的透明度\n lineWidth: 1.5, // 边框的线宽\n shadowColor: '#1d39c4', // 阴影颜色\n shadowBlur: 10, // 阴影的模糊半径\n shadowOffsetX: 0, // 阴影的水平偏移\n shadowOffsetY: 0, // 阴影的垂直偏移\n cursor: 'pointer', // 鼠标悬停时的指针样式\n },\n nodeStyle: {\n // 节点基础样式\n size: 40, // 节点大小\n fill: '#d6e4ff', // 节点填充颜色\n stroke: '#2f54eb', // 节点边框颜色\n lineWidth: 2, // 节点边框宽度\n shadowColor: '#2f54eb', // 节点阴影颜色\n shadowBlur: 5, // 节点阴影模糊半径\n cursor: 'pointer', // 鼠标悬停时的指针样式\n\n // 标签样式\n label: true, // 是否显示标签\n labelFontSize: 14, // 标签字体大小\n labelFontWeight: 'bold', // 标签字体粗细\n labelFill: '#1d39c4', // 标签文字颜色\n labelBackground: true, // 是否显示标签背景\n labelBackgroundFill: '#fff', // 标签背景填充颜色\n labelBackgroundStroke: '#1d39c4', // 标签背景边框颜色\n labelBackgroundOpacity: 0.8, // 标签背景透明度\n labelBackgroundPadding: [4, 8, 4, 8], // 标签背景内边距 [上,右,下,左]\n\n // 图标样式\n icon: true, // 是否显示图标\n iconFontFamily: 'iconfont', // 图标字体\n iconText: '\\ue6f6', // 图标的 Unicode 编码\n iconFill: '#1d39c4', // 图标颜色\n iconSize: 16, // 图标大小\n iconFontWeight: 'normal', // 图标字体粗细\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph, iconfont } from '@antv/g6';\n\nconst style = document.createElement('style');\nstyle.innerHTML = `@import url('${iconfont.css}');`;\ndocument.head.appendChild(style);\n\nfetch('https://assets.antv.antgroup.com/g6/relations.json')\n .then((res) => res.json())\n .then((data) => {\n const graph = new Graph({\n container: 'container',\n autoFit: 'view',\n data,\n node: {\n style: {\n size: (datum) => datum.id.length * 2 + 10,\n label: false,\n labelText: (datum) => datum.id,\n labelBackground: true,\n icon: false,\n iconFontFamily: 'iconfont',\n iconText: '\\ue6f6',\n iconFill: '#fff',\n },\n palette: {\n type: 'group',\n field: (datum) => datum.id,\n color: ['#1783FF', '#00C9C9', '#F08F56', '#D580FF'],\n },\n },\n edge: {\n style: {\n stroke: '#e2e2e2',\n },\n },\n plugins: [{ key: 'fisheye', type: 'fisheye', nodeStyle: { label: true, icon: true } }],\n });\n graph.render();\n });\n```\n\n\n\n---\ntitle: Fullscreen\norder: 7\n---\n\n## Overview\n\nThe Fullscreen plugin allows users to expand the graph visualization content to the entire screen, providing a broader view and a better immersive experience.\n\n## Use Cases\n\nThe Fullscreen plugin is mainly suitable for the following scenarios:\n\n- Provide a broader view for viewing complex graph data\n- Enhance immersive experience, focusing on graph visualization content\n- Display graph data in presentations or reports\n\n## Basic Usage\n\nBelow is a simple example of initializing the Fullscreen plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n autoFit: true,\n trigger: {\n request: 'F', // Use shortcut key F to enter fullscreen\n exit: 'Esc', // Use shortcut key Esc to exit fullscreen\n },\n onEnter: () => {\n console.log('Entered fullscreen mode');\n },\n onExit: () => {\n console.log('Exited fullscreen mode');\n },\n },\n ],\n});\n```\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| -------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------------- | -------- |\n| type | Plugin type | string | `fullscreen` | ✓ |\n| key | Unique identifier for the plugin, can be used to get the plugin instance or update plugin options | string | - | |\n| autoFit | Whether to auto-fit the canvas size, the canvas size will automatically adapt to the screen size when fullscreen | boolean | true | |\n| trigger | Method to trigger fullscreen, [example](#trigger) | { request?: string; exit?: string; } | - | |\n| onEnter | Callback after entering fullscreen | () => void | - | |\n| onExit | Callback after exiting fullscreen | () => void | - | |\n\n### trigger\n\nThe trigger property is used to control the method of triggering fullscreen. It supports two configuration methods:\n\n#### Shortcut Key Configuration\n\nUse keyboard shortcuts to trigger fullscreen and exit fullscreen.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n trigger: {\n request: 'F', // Use shortcut key F to enter fullscreen\n exit: 'Esc', // Use shortcut key Esc to exit fullscreen\n },\n },\n ],\n});\n```\n\n#### Custom Trigger\n\nControl fullscreen by calling the request and exit methods.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n key: 'my-fullscreen',\n },\n ],\n});\n\n// Enter fullscreen\ngraph.getPluginInstance('my-fullscreen').request();\n\n// Exit fullscreen\ngraph.getPluginInstance('my-fullscreen').exit();\n```\n\n### autoFit\n\nWhether to auto-fit the canvas size, the canvas size will automatically adapt to the screen size when fullscreen.\n\n- When set to true, the canvas will automatically resize to fit the entire screen.\n- When set to false, the canvas size remains unchanged.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n autoFit: true,\n },\n ],\n});\n```\n\n## API\n\n### Fullscreen.request()\n\nThis method is used to enter fullscreen mode programmatically. It can be called on the plugin instance to expand the graph visualization to the entire screen.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n key: 'my-fullscreen',\n },\n ],\n});\n\n// Enter fullscreen\ngraph.getPluginInstance('my-fullscreen').request();\n```\n\n### Fullscreen.exit()\n\nThis method is used to exit fullscreen mode programmatically. It can be called on the plugin instance to revert the graph visualization back to its original size.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n key: 'my-fullscreen',\n },\n ],\n});\n\n// Exit fullscreen\ngraph.getPluginInstance('my-fullscreen').exit();\n```\n\n## Practical Examples\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n data: { nodes: Array.from({ length: 20 }).map((_, i) => ({ id: `node${i}` })) },\n autoFit: 'center',\n background: '#fff',\n plugins: [\n {\n type: 'fullscreen',\n key: 'fullscreen',\n },\n function () {\n const graph = this;\n return {\n type: 'toolbar',\n key: 'toolbar',\n position: 'top-left',\n onClick: (item) => {\n const fullscreenPlugin = graph.getPluginInstance('fullscreen');\n if (item === 'request-fullscreen') {\n fullscreenPlugin.request();\n }\n if (item === 'exit-fullscreen') {\n fullscreenPlugin.exit();\n }\n },\n getItems: () => {\n return [\n { id: 'request-fullscreen', value: 'request-fullscreen' },\n { id: 'exit-fullscreen', value: 'exit-fullscreen' },\n ];\n },\n };\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 全屏展示 Fullscreen\norder: 7\n---\n\n## 概述\n\n全屏展示插件允许用户将图可视化内容扩展到整个屏幕,提供更广阔的视图和更好的沉浸式体验。\n\n## 使用场景\n\n全屏展示插件主要适用于以下场景:\n\n- 提供更广阔的视图,便于查看复杂图数据\n- 增强沉浸式体验,专注于图可视化内容\n- 在演示或报告中展示图数据\n\n## 基本用法\n\n以下是一个简单的 Fullscreen 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n autoFit: true,\n trigger: {\n request: 'F', // 使用快捷键 F 进入全屏\n exit: 'Esc', // 使用快捷键 Esc 退出全屏\n },\n onEnter: () => {\n console.log('进入全屏模式');\n },\n onExit: () => {\n console.log('退出全屏模式');\n },\n },\n ],\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------- | ---------------------------------------------------- | ------------------------------------ | ------------ | ---- |\n| type | 插件类型 | string | `fullscreen` | ✓ |\n| key | 插件的唯一标识,可用于获取插件实例或更新插件选项 | string | - | |\n| autoFit | 是否自适应画布尺寸,全屏后画布尺寸会自动适应屏幕尺寸 | boolean | true | |\n| trigger | 触发全屏的方式,[示例](#trigger) | { request?: string; exit?: string; } | - | |\n| onEnter | 进入全屏后的回调 | () => void | - | |\n| onExit | 退出全屏后的回调 | () => void | - | |\n\n### trigger\n\ntrigger 属性用于控制触发全屏的方式。它支持两种配置方式:\n\n#### 快捷键配置\n\n使用键盘快捷键来触发全屏和退出全屏。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n trigger: {\n request: 'F', // 使用快捷键 F 进入全屏\n exit: 'Esc', // 使用快捷键 Esc 退出全屏\n },\n },\n ],\n});\n```\n\n#### 自定义触发\n\n通过调用 request 和 exit 方法来控制全屏。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n key: 'my-fullscreen',\n },\n ],\n});\n\n// 进入全屏\ngraph.getPluginInstance('my-fullscreen').request();\n\n// 退出全屏\ngraph.getPluginInstance('my-fullscreen').exit();\n```\n\n### autoFit\n\n是否自适应画布尺寸,全屏后画布尺寸会自动适应屏幕尺寸。\n\n- 设置为 true 时,画布会自动调整大小以适应整个屏幕。\n- 设置为 false 时,画布大小保持不变。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n autoFit: true,\n },\n ],\n});\n```\n\n## API\n\n### Fullscreen.request()\n\n这个方法可以让你通过代码进入全屏模式。调用插件实例上的这个方法,就能把图形内容扩展到整个屏幕。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n key: 'my-fullscreen',\n },\n ],\n});\n\n// 进入全屏\ngraph.getPluginInstance('my-fullscreen').request();\n```\n\n### Fullscreen.exit()\n\n这个方法可以让你通过代码退出全屏模式。调用插件实例上的这个方法,就能把图形内容恢复到原来的大小。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'fullscreen',\n key: 'my-fullscreen',\n },\n ],\n});\n\n// 退出全屏\ngraph.getPluginInstance('my-fullscreen').exit();\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n data: { nodes: Array.from({ length: 20 }).map((_, i) => ({ id: `node${i}` })) },\n autoFit: 'center',\n background: '#fff',\n plugins: [\n {\n type: 'fullscreen',\n key: 'fullscreen',\n },\n function () {\n const graph = this;\n return {\n type: 'toolbar',\n key: 'toolbar',\n position: 'top-left',\n onClick: (item) => {\n const fullscreenPlugin = graph.getPluginInstance('fullscreen');\n if (item === 'request-fullscreen') {\n fullscreenPlugin.request();\n }\n if (item === 'exit-fullscreen') {\n fullscreenPlugin.exit();\n }\n },\n getItems: () => {\n return [\n { id: 'request-fullscreen', value: 'request-fullscreen' },\n { id: 'exit-fullscreen', value: 'exit-fullscreen' },\n ];\n },\n };\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: GridLine\norder: 8\n---\n\n## Overview\n\nThe GridLine plugin provides visual auxiliary lines for the canvas, helping users precisely position and align graphic elements. It is an indispensable tool in graphic drawing.\n\n## Use Cases\n\nThe GridLine plugin is mainly suitable for the following scenarios:\n\n- Assisting users in precise drawing and element alignment\n- Providing visual references to enhance spatial awareness\n- Building a structured reference system when designing and editing graphics\n\n## Basic Usage\n\nBelow is a simple example of initializing the GridLine plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'grid-line',\n key: 'my-grid-line', // Specify a unique identifier for dynamic updates\n size: 20,\n stroke: '#0001',\n follow: true,\n },\n ],\n});\n```\n\n## Online Experience\n\n\n\n## Configuration Options\n\n| Property | Description | Type | Default | Required |\n| --------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ----------- | -------- |\n| type | Plugin type | string | `grid-line` | ✓ |\n| key | Unique identifier for the plugin, used to get the plugin instance or update plugin options | string | - | |\n| border | Whether to display the border | boolean | true | |\n| borderLineWidth | Border line width | number | 1 | |\n| borderStroke | Border color, see [CSS border-color](https://developer.mozilla.org/en-US/docs/Web/CSS/border-color) | string | `#eee` | |\n| borderStyle | Border style, see [CSS border-style](https://developer.mozilla.org/en-US/docs/Web/CSS/border-style) | string | `solid` | |\n| follow | Whether to follow canvas movements | boolean \\| {translate ?: boolean, zoom?: boolean} | false | |\n| lineWidth | Grid line width | number \\| string | 1 | |\n| size | Grid unit size in pixels | number | 20 | |\n| stroke | Grid line color | string | `#eee` | |\n\n### follow\n\nThe `follow` property controls whether the grid lines follow the canvas transformations. It supports two configuration methods:\n\n1. **Boolean Configuration**: When set to `true`, the grid lines follow both canvas translation and zoom; when set to `false`, they remain static.\n\n```js\n// Enable both translation and zoom following\nconst graph = new Graph({\n plugins: [\n {\n type: 'grid-line',\n follow: true,\n },\n ],\n});\n```\n\n2. **Object Configuration**: Allows more precise control over the grid line following behavior.\n\n```js\n// Follow translation only, not zoom\nconst graph = new Graph({\n plugins: [\n {\n type: 'grid-line',\n follow: {\n translate: true, // Follow translation\n zoom: false, // Do not follow zoom\n },\n },\n ],\n});\n\n// Follow zoom only, not translation\nconst graph = new Graph({\n plugins: [\n {\n type: 'grid-line',\n follow: {\n translate: false, // Do not follow translation\n zoom: true, // Follow zoom\n },\n },\n ],\n});\n```\n\nWhen grid lines follow zoom, they maintain a relative position to the canvas content, making alignment references more precise. Following translation allows the grid to move with the canvas content, enhancing the visual experience of spatial continuity.\n\n## Code Examples\n\n### Basic Grid Line\n\nThe simplest way is to use the preset configuration directly:\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: ['grid-line'],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n data: { nodes: [{ id: 'node-1', style: { x: 150, y: 75 } }] },\n behaviors: ['drag-canvas'],\n plugins: ['grid-line'],\n});\n\ngraph.render();\n```\n\n### Custom Style\n\nYou can customize the grid line style as needed:\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'grid-line',\n stroke: '#1890ff33', // Blue semi-transparent grid line\n lineWidth: 2,\n size: 40, // Larger grid unit\n borderStroke: '#1890ff', // Blue border\n borderLineWidth: 2,\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n data: { nodes: [{ id: 'node-1', style: { x: 150, y: 75 } }] },\n behaviors: ['drag-canvas'],\n plugins: [\n {\n type: 'grid-line',\n stroke: '#1890ff33', // Blue semi-transparent grid line\n lineWidth: 2,\n size: 40, // Larger grid\n borderStroke: '#1890ff', // Blue border\n borderLineWidth: 2,\n },\n ],\n});\n\ngraph.render();\n```\n\n### Follow Movement\n\nEnabling the follow option allows the grid to move with the canvas, enhancing user experience:\n\n```js\nconst graph = new Graph({\n // Other configurations...\n behaviors: ['drag-canvas', 'zoom-canvas'],\n plugins: [\n {\n type: 'grid-line',\n follow: true, // Grid follows canvas movement\n },\n ],\n});\n```\n\nTry dragging/zooming the canvas to observe the grid following effect:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n data: { nodes: [{ id: 'node-1', style: { x: 150, y: 75 } }] },\n behaviors: ['drag-canvas', 'zoom-canvas'],\n plugins: [\n {\n type: 'grid-line',\n follow: true, // Grid follows canvas movement\n },\n ],\n});\n\ngraph.render();\n```\n\n### Dynamic Grid Update\n\nUse the key identifier to dynamically update grid properties at runtime:\n\n```js\n// Initial configuration\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'grid-line',\n key: 'my-grid',\n size: 20,\n },\n ],\n});\n\n// Subsequent dynamic updates\ngraph.updatePlugin({\n key: 'my-grid',\n size: 40, // Update grid size\n stroke: '#ff4d4f', // Update grid color\n});\n```\n\n## Cases\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: { type: 'grid' },\n behaviors: ['drag-canvas'],\n plugins: [{ key: 'grid-line', type: 'grid-line', follow: false }],\n});\n\ngraph.render();\n\nwindow.addPanel((gui) => {\n gui\n .add({ follow: false }, 'follow')\n .name('Follow')\n .onChange((value) => {\n graph.updatePlugin({\n key: 'grid-line',\n follow: value,\n });\n });\n});\n```\n\n\n\n---\ntitle: 网格线 GridLine\norder: 8\n---\n\n## 概述\n\n网格线插件为画布提供可视化辅助线,帮助用户精确定位和对齐图形元素,是图形绘制中不可或缺的辅助工具。\n\n## 使用场景\n\n网格线插件主要适用于以下场景:\n\n- 辅助用户精确绘图和元素对齐\n- 提供视觉参考,增强空间感知\n- 在设计和编辑图形时构建结构化的参考系统\n\n## 基本用法\n\n以下是一个简单的 GridLine 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'grid-line',\n key: 'my-grid-line', // 指定唯一标识符,便于后续动态更新\n size: 20,\n stroke: '#0001',\n follow: true,\n },\n ],\n});\n```\n\n## 在线体验\n\n\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ----------- | ---- |\n| type | 插件类型 | string | `grid-line` | ✓ |\n| key | 插件的唯一标识,可用于获取插件实例或更新插件选项 | string | - | |\n| border | 是否显示边框 | boolean | true | |\n| borderLineWidth | 边框线宽 | number | 1 | |\n| borderStroke | 边框颜色,详细属性参考 [CSS border-color](https://developer.mozilla.org/zh-CN/docs/Web/CSS/border-color) | string | `#eee` | |\n| borderStyle | 边框样式,详细属性参考 [CSS border-style](https://developer.mozilla.org/zh-CN/docs/Web/CSS/border-style) | string | `solid` | |\n| follow | 是否跟随画布移动 | boolean \\| {translate ?: boolean, zoom?: boolean} | false | |\n| lineWidth | 网格线宽度 | number \\| string | 1 | |\n| size | 网格单元大小,单位为像素 | number | 20 | |\n| stroke | 网格线颜色 | string | `#eee` | |\n\n### follow\n\n`follow` 属性用于控制网格线是否跟随画布的变换操作。它支持两种配置方式:\n\n1. **布尔值配置**:当设置为 `true` 时,网格线会同时跟随画布的平移和缩放;设置为 `false` 时则保持静态。\n\n```js\n// 同时启用跟随平移和缩放\nconst graph = new Graph({\n plugins: [\n {\n type: 'grid-line',\n follow: true,\n },\n ],\n});\n```\n\n2. **对象配置**:可以更精细地控制网格线的跟随行为。\n\n```js\n// 仅跟随平移,不跟随缩放\nconst graph = new Graph({\n plugins: [\n {\n type: 'grid-line',\n follow: {\n translate: true, // 跟随平移\n zoom: false, // 不跟随缩放\n },\n },\n ],\n});\n\n// 仅跟随缩放,不跟随平移\nconst graph = new Graph({\n plugins: [\n {\n type: 'grid-line',\n follow: {\n translate: false, // 不跟随平移\n zoom: true, // 跟随缩放\n },\n },\n ],\n});\n```\n\n当网格线跟随缩放时,它会保持与画布内容的相对位置关系,使得对齐参考更加精准。跟随平移则让网格随着画布内容一起移动,增强空间连续性的视觉体验。\n\n## 代码示例\n\n### 基础网格线\n\n最简单的方式是直接使用预设配置:\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: ['grid-line'],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n data: { nodes: [{ id: 'node-1', style: { x: 150, y: 75 } }] },\n behaviors: ['drag-canvas'],\n plugins: ['grid-line'],\n});\n\ngraph.render();\n```\n\n### 自定义样式\n\n您可以根据需要自定义网格线的样式:\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'grid-line',\n stroke: '#1890ff33', // 蓝色半透明网格线\n lineWidth: 2,\n size: 40, // 更大的网格单元\n borderStroke: '#1890ff', // 蓝色边框\n borderLineWidth: 2,\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n data: { nodes: [{ id: 'node-1', style: { x: 150, y: 75 } }] },\n behaviors: ['drag-canvas'],\n plugins: [\n {\n type: 'grid-line',\n stroke: '#1890ff33', // 蓝色半透明网格线\n lineWidth: 2,\n size: 40, // 更大的网格\n borderStroke: '#1890ff', // 蓝色边框\n borderLineWidth: 2,\n },\n ],\n});\n\ngraph.render();\n```\n\n### 跟随移动\n\n启用 follow 选项可以让网格跟随画布移动,增强用户体验:\n\n```js\nconst graph = new Graph({\n // 其他配置...\n behaviors: ['drag-canvas', 'zoom-canvas'],\n plugins: [\n {\n type: 'grid-line',\n follow: true, // 网格跟随画布移动\n },\n ],\n});\n```\n\n试着拖拽/缩放画布,观察网格的跟随效果:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n data: { nodes: [{ id: 'node-1', style: { x: 150, y: 75 } }] },\n behaviors: ['drag-canvas', 'zoom-canvas'],\n plugins: [\n {\n type: 'grid-line',\n follow: true, // 网格跟随画布移动\n },\n ],\n});\n\ngraph.render();\n```\n\n### 动态更新网格\n\n使用 key 标识符可以在运行时动态更新网格属性:\n\n```js\n// 初始化配置\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'grid-line',\n key: 'my-grid',\n size: 20,\n },\n ],\n});\n\n// 后续动态更新\ngraph.updatePlugin({\n key: 'my-grid',\n size: 40, // 更新网格大小\n stroke: '#ff4d4f', // 更新网格颜色\n});\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: { type: 'grid' },\n behaviors: ['drag-canvas'],\n plugins: [{ key: 'grid-line', type: 'grid-line', follow: false }],\n});\n\ngraph.render();\n\nwindow.addPanel((gui) => {\n gui\n .add({ follow: false }, 'follow')\n .name('Follow')\n .onChange((value) => {\n graph.updatePlugin({\n key: 'grid-line',\n follow: value,\n });\n });\n});\n```\n\n\n\n---\ntitle: History\norder: 9\n---\n\n## Overview\n\nThis plugin is used to implement the **Undo** and **Redo** functions in graph editing. By recording the historical state stack of user operations, it supports backtracking or restoring operations during graph interactions. The plugin provides users with comprehensive configuration options and APIs.\n\n## Usage Scenarios\n\nThe history plugin is suitable for all scenarios involving graph editing.\n\n## Online Experience\n\n\n\n## Basic Usage\n\nAdd this plugin in the graph configuration:\n\n**1. Quick Configuration (Static)**\n\nDeclare directly using a string. This method is simple but only supports default configurations and cannot be dynamically modified after configuration:\n\n```javascript\nconst graph = new Graph({\n // Other configurations...\n plugins: ['history'],\n});\n```\n\n**2. Object Configuration (Recommended)**\n\nConfigure using an object form, supporting custom parameters, and allowing dynamic updates at runtime:\n\n```javascript\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'history',\n key: 'history-1',\n stackSize: 10,\n },\n ],\n});\n```\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------- | -------- |\n| afterAddCommand | Called after a command is added to the `Undo/Redo` queue. `revert` is `true` for undo operations and `false` for redo operations | (cmd: [Command](#command), revert: boolean) => void | - | |\n| beforeAddCommand | Called before a command is added to the `Undo/Redo` queue. If this method returns `false`, the command will not be added to the queue. `revert` is `true` for undo operations and `false` for redo operations | (cmd: [Command](#command), revert: boolean) => boolean \\| void | - | |\n| executeCommand | Callback function when executing a command | (cmd: [Command](#command)) => void | - | |\n| stackSize | Maximum length of history records to be recorded | number | 0 (unlimited) | |\n\n### Parameter Type Description\n\n#### `Command`\n\n```typescript\n// Single history command\ninterface Command {\n current: CommandData; // Current data\n original: CommandData; // Original data\n animation: boolean; // Whether to enable animation\n}\n// Single history command data\ninterface CommandData {\n add: GraphData; // Added data\n update: GraphData; // Updated data\n remove: GraphData; // Removed data\n}\n// Graph data\ninterface GraphData {\n nodes?: NodeData[]; // Node data\n edges?: EdgeData[]; // Edge data\n combos?: ComboData[]; // Combo data\n}\n```\n\n## API\n\nThe history plugin provides the following APIs for users to use as needed. For how to call plugin methods, please refer to the [Plugin Overview Document](/en/manual/plugin/overview#calling-plugin-methods)\n\n### History.canRedo()\n\nDetermines whether a **redo** operation can be performed. If there are records in the redo stack, it returns `true`; otherwise, it returns `false`.\n\n```typescript\ncanRedo(): boolean;\n```\n\n**Example:**\n\n```typescript\nconst canRedo = historyInstance.canRedo();\nif (canRedo) {\n console.log('Redo operation can be performed');\n} else {\n console.log('Redo stack is empty, cannot redo');\n}\n```\n\n### History.canUndo()\n\nDetermines whether an **undo** operation can be performed. If there are records in the undo stack, it returns `true`; otherwise, it returns `false`.\n\n```typescript\ncanUndo(): boolean;\n```\n\n**Example:**\n\n```typescript\nconst canUndo = historyInstance.canUndo();\nif (canUndo) {\n console.log('Undo operation can be performed');\n} else {\n console.log('Undo stack is empty, cannot undo');\n}\n```\n\n### History.clear()\n\nClears the history records, including the undo and redo stacks.\n\n```typescript\nclear(): void;\n```\n\n**Example:**\n\n```typescript\nhistoryInstance.clear();\nconsole.log('History records cleared');\n```\n\n### History.on()\n\nListens to history events, allowing users to execute custom logic when specific events occur.\n\n```typescript\non(event: Loosen, handler: (e: { cmd?: Command | null }) => void): void;\n```\n\nParameter Type Description:\n\n- HistoryEvent\n\n ```typescript\n enum HistoryEvent {\n UNDO = 'undo', // When a command is undone\n REDO = 'redo', // When a command is redone\n CANCEL = 'cancel', // When a command is canceled\n ADD = 'add', // When a command is added to the queue\n CLEAR = 'clear', // When the history queue is cleared\n CHANGE = 'change', // When the history queue changes\n }\n ```\n\n- Command\n\n Please refer to the previous [Command](#parameter-type-description) type description\n\nExample:\n\n```typescript\nhistoryInstance.on(HistoryEvent.UNDO, () => {\n console.log('Undo operation executed');\n});\n```\n\n### History.redo()\n\nPerforms a **redo** operation and returns the plugin instance. If the redo stack is empty, no operation is performed.\n\n```typescript\nredo(): History;\n```\n\n**Example:**\n\n```typescript\nhistoryInstance.redo();\nconsole.log('Redo operation executed');\n```\n\n### History.undo()\n\nPerforms an **undo** operation and returns the plugin instance. If the undo stack is empty, no operation is performed.\n\n```typescript\nundo(): History;\n```\n\n**Example:**\n\n```typescript\nhistoryInstance.undo();\nconsole.log('Undo operation executed');\n```\n\n### History.undoAndCancel()\n\nPerforms an undo operation without recording it in the history and returns the plugin instance. Note that this operation will clear the **redo** stack.\n\n```typescript\nundoAndCancel(): History;\n```\n\n**Example:**\n\n```typescript\nhistoryInstance.undoAndCancel();\nconsole.log('Undo and cancel operation executed');\n```\n\n## History Modes\n\nThis plugin supports two history modes:\n\n### Default Mode\n\nIn default mode, every time a **render** is triggered (for example, after updating element data, the user actively executes the `graph.draw()` method to trigger rendering), the plugin records the data **before** and **after** rendering and stacks it as an operation record.\n\n### Custom Mode\n\n#### Scenario Description\n\nIn actual needs, a user's graph editing operation may involve **multiple renders**. For example, in one editing operation, first display nodes A and B, then display the connection from A to B. This involves two renders (i.e., the user needs to perform `graph.draw()` twice). In this scenario, the default mode will stack two history records, which are:\n\n- Display nodes A and B\n- Display the connection from A to B\n\nObviously, in actual business, one operation should only require one undo.\n\nBut here, when undoing this operation, the user needs to call the `undo` method twice, which means two undos are required.\n\n#### Scenario Support\n\nTo support such scenarios, G6 provides a batch controller (`BatchController`, [refer to the source code](https://github.com/antvis/G6/blob/v5/packages/g6/src/runtime/batch.ts)), which is provided in the graph instance context.\n\nThe history plugin implements custom operation records based on this batch controller. The code example is as follows:\n\n```typescript\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'history',\n key: 'history',\n },\n ],\n});\n\ngraph.context.batch.startBatch(); // Start batch operation\ngraph.addNodeData(...); // Display nodes A and B\ngraph.draw(); // First render trigger\ngraph.addEdgeData(...); // Display the connection from A to B\ngraph.draw(); // Second render trigger\ngraph.context.batch.endBatch(); // End batch operation\n```\n\nIn the example:\n\n- By calling the `startBatch` method of the batch controller instance, the history plugin is informed that batch operations are now being performed. Before the batch operation ends, no matter how many renders are triggered, no history records should be stacked (the history plugin will record the change data for each render trigger).\n- After completing the last data change, call the `endBatch()` method. The history plugin listens for the completion of the batch operation and stacks this batch operation as a history record.\n\nFinally, the user only needs to perform one `undo` to undo.\n\n## Code Examples\n\nBelow are some common cases with corresponding code references.\n\n### Undo and Redo Button States\n\nIn actual business scenarios, you may need to customize the toolbar of the canvas, which involves the enable and disable states of the undo and redo buttons.\n\n```typescript\nconst canUndo = false;\nconst canRedo = false;\n\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'history',\n key: 'history',\n },\n ],\n});\nconst historyInstance = graph.getPluginInstance('history');\n\nhistoryInstance.on(HistoryEvent.CHANGE, () => {\n canUndo = historyInstance.canUndo();\n canRedo = historyInstance.canRedo();\n});\n```\n\nIn the example, by listening to the `HistoryEvent.CHANGE` event, which is triggered when the history queue changes, it is determined in real-time whether undo and redo operations can be performed.\n\n### Determine Whether a Command is Allowed to Enter the Queue\n\nHere is a simple scenario: only the operation of removing elements is allowed to enter the history queue.\n\n```typescript\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'history',\n key: 'history',\n beforeAddCommand: (cmd) => {\n return (\n cmd.current.remove?.nodes?.length > 0 ||\n cmd.current.remove?.combos?.length > 0 ||\n cmd.current.remove?.edges?.length > 0\n );\n },\n },\n ],\n});\n```\n\nIn the example, the configuration option [beforeAddCommand](#beforeAddCommand) is used to determine whether there are elements removed in `cmd.current.remove`.\n\n\n\n---\ntitle: 历史记录 History\norder: 9\n---\n\n## 概述\n\n该插件用于实现图编辑的 **撤销(Undo)** 和 **重做(Redo)** 功能,通过记录用户操作的历史状态堆栈,支持在图交互过程中进行回溯或恢复操作。该插件为用户提供了完善的配置项和 API 。\n\n## 使用场景\n\n历史记录插件适用于所有涉及到图编辑的场景。\n\n## 在线体验\n\n\n\n## 基本用法\n\n在图配置中添加这一插件:\n\n**1. 快速配置(静态)**\n\n使用字符串形式直接声明,这种方式简洁但仅支持默认配置,且配置后不可动态修改:\n\n```javascript\nconst graph = new Graph({\n // 其他配置...\n plugins: ['history'],\n});\n```\n\n**2. 对象配置(推荐)**\n\n使用对象形式进行配置,支持自定义参数,且可以在运行时动态更新配置:\n\n```javascript\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'history',\n key: 'history-1',\n stackSize: 10,\n },\n ],\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ----------- | ---- |\n| afterAddCommand | 当一个命令被添加到 `Undo/Redo` 队列后被调用。`revert` 为 `true` 时表示撤销操作,为 `false` 时表示重做操作 | (cmd: [Command](#command), revert: boolean) => void | - | |\n| beforeAddCommand | 当一个命令被添加到 `Undo/Redo` 队列前被调用,如果该方法返回 `false`,那么这个命令将不会被添加到队列中。`revert` 为 `true` 时表示撤销操作,为 `false` 时表示重做操作 | (cmd: [Command](#command), revert: boolean) => boolean \\| void | - | |\n| executeCommand | 执行命令时的回调函数 | (cmd: [Command](#command)) => void | - | |\n| stackSize | 最多记录该数据长度的历史记录 | number | 0(不限制) | |\n\n### 参数类型说明\n\n#### `Command`\n\n```typescript\n// 单条历史记录命令\ninterface Command {\n current: CommandData; // 当前数据\n original: CommandData; // 原始数据\n animation: boolean; // 是否开启动画\n}\n// 单条历史记录命令数据\ninterface CommandData {\n add: GraphData; // 新增的数据\n update: GraphData; // 更新的数据\n remove: GraphData; // 移除的数据\n}\n// 图数据\ninterface GraphData {\n nodes?: NodeData[]; // 节点数据\n edges?: EdgeData[]; // 边数据\n combos?: ComboData[]; // Combo 数据\n}\n```\n\n## API\n\nhistory 插件提供了以下 API 供用户按需使用,调用插件方法的方式请参考 [插件总览文档](/manual/plugin/overview#调用插件方法)\n\n### History.canRedo()\n\n判断是否可以进行**重做**操作。如果重做堆栈中有记录,则返回 `true`,否则返回 `false`。\n\n```typescript\ncanRedo(): boolean;\n```\n\n**示例:**\n\n```typescript\nconst canRedo = historyInstance.canRedo();\nif (canRedo) {\n console.log('可以进行重做操作');\n} else {\n console.log('重做堆栈为空,无法重做');\n}\n```\n\n### History.canUndo()\n\n判断是否可以进行**撤销**操作。如果撤销堆栈中有记录,则返回 `true`,否则返回 `false`。\n\n```typescript\ncanUndo(): boolean;\n```\n\n**示例:**\n\n```typescript\nconst canUndo = historyInstance.canUndo();\nif (canUndo) {\n console.log('可以进行撤销操作');\n} else {\n console.log('撤销堆栈为空,无法撤销');\n}\n```\n\n### History.clear()\n\n清空历史记录,包括撤销和重做堆栈。\n\n```typescript\nclear(): void;\n```\n\n**示例:**\n\n```typescript\nhistoryInstance.clear();\nconsole.log('历史记录已清空');\n```\n\n### History.on()\n\n监听历史记录事件,允许用户在特定事件发生时执行自定义逻辑。\n\n```typescript\non(event: Loosen/, handler: (e: { cmd?: Command | null }) => void): void;\n```\n\n参数类型说明:\n\n- HistoryEvent\n\n ```typescript\n enum HistoryEvent {\n UNDO = 'undo', // 当命令被撤销时\n REDO = 'redo', // 当命令被重做时\n CANCEL = 'cancel', // 当命令被取消时\n ADD = 'add', // 当命令被添加到队列时\n CLEAR = 'clear', // 当历史队列被清空时\n CHANGE = 'change', // 当历史队列发生变化时\n }\n ```\n\n- Command\n\n 请参考前面的 [Command](#参数类型说明) 类型说明\n\n示例:\n\n```typescript\nhistoryInstance.on(HistoryEvent.UNDO, () => {\n console.log('执行了撤销操作');\n});\n```\n\n### History.redo()\n\n执行**重做**操作,并返回插件实例。如果重做堆栈为空,则不执行任何操作。\n\n```typescript\nredo(): History;\n```\n\n**示例:**\n\n```typescript\nhistoryInstance.redo();\nconsole.log('执行了重做操作');\n```\n\n### History.undo()\n\n执行**撤销**操作,并返回插件实例。如果撤销堆栈为空,则不执行任何操作。\n\n```typescript\nundo(): History;\n```\n\n**示例:**\n\n```typescript\nhistoryInstance.undo();\nconsole.log('执行了撤销操作');\n```\n\n### History.undoAndCancel()\n\n执行撤销操作且不计入历史记录,并返回插件实例。注意,执行该操作会清空**重做**栈。\n\n```typescript\nundoAndCancel(): History;\n```\n\n**示例:**\n\n```typescript\nhistoryInstance.undoAndCancel();\nconsole.log('执行了撤销并取消操作');\n```\n\n## 历史记录模式\n\n该插件支持两种历史记录模式:\n\n### 默认模式\n\n默认模式下,每一次触发**渲染后**(比如更新元素数据后,用户主动执行 `graph.draw()` 方法触发渲染),插件会把**渲染前**和**渲染后**的数据记录下来并作为一次操作记录入栈。\n\n### 自定义模式\n\n#### 场景描述\n\n实际需求中,用户的一次图编辑操作可能涉及到**多次渲染**,比如,一次编辑操作中,首先把节点 A、B 展示出来,然后展示 A->B 的连线,这里就涉及到两次渲染(即用户需要进行两次 `graph.draw()` ),这种场景下,默认模式会入栈两次历史记录,分别是:\n\n- 展示节点 A 和 B\n- 展示 A->B 的连线\n\n显然,实际业务中,一次操作,也应该只需一次撤销。\n\n但这里在撤销本次操作时,用户需要调用两次 `undo` 方法,也就是需要进行两次撤销。\n\n#### 场景支持\n\n为了支持这样的场景,G6 提供了一个批量控制器( `BatchController`,[可参考源码](https://github.com/antvis/G6/blob/v5/packages/g6/src/runtime/batch.ts)),在图实例上下文中提供了这个批量控制器实例。\n\n历史记录插件则基于这个批量控制器,来实现自定义操作记录,代码示例如下:\n\n```typescript\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'history',\n key: 'history',\n },\n ],\n});\n\ngraph.context.batch.startBatch(); // 开始批量操作\ngraph.addNodeData(...); // 把节点 A、B 展示出来\ngraph.draw(); // 第一次触发渲染\ngraph.addEdgeData(...); // 把 A->B 连线展示出来\ngraph.draw(); // 第二次触发渲染\ngraph.context.batch.endBatch(); // 结束批量操作\n```\n\n示例中:\n\n- 通过调用批量控制器实例的 `startBatch` 方法,告诉历史记录插件,现在开始进行批量操作,在批量操作没有结束前,不管触发多少次渲染,都不能进行历史记录入栈(历史记录插件会把每次触发渲染的变更数据记录下来)\n- 在完成最后一次数据变更后,调用 `endBatch()` 方法,历史记录插件监听到批量操作完成,则把本次批量操作作为一次历史记录入栈\n\n最终,用户只需要进行一次 `undo` 即可撤销。\n\n## 代码示例\n\n下面列举一些常见的案例,并给出相应的代码参考\n\n### 撤销、重做按钮状态\n\n实际业务场景中,可能需要自定义画布的工具栏,也就涉及到撤销和重做按钮的启禁用状态\n\n```typescript\nconst canUndo = false;\nconst canRedo = false;\n\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'history',\n key: 'history',\n },\n ],\n});\nconst historyInstance = graph.getPluginInstance('history');\n\nhistoryInstance.on(HistoryEvent.CHANGE, () => {\n canUndo = historyInstance.canUndo();\n canRedo = historyInstance.canRedo();\n});\n```\n\n示例中通过监听 `HistoryEvent.CHANGE` 事件,这个事件在历史队列发生变化时会触发,每次发生变化后,实时判断当前是否可以进行撤销和重做操作\n\n### 判断是否允许命令进入队列\n\n这里实现一个简单的场景:只有移除元素的操作才允许进入历史记录队列\n\n```typescript\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'history',\n key: 'history',\n beforeAddCommand: (cmd) => {\n return (\n cmd.current.remove?.nodes?.length > 0 ||\n cmd.current.remove?.combos?.length > 0 ||\n cmd.current.remove?.edges?.length > 0\n );\n },\n },\n ],\n});\n```\n\n示例中通过配置项 [beforeAddCommand](#beforeAddCommand) 来实现,判断 `cmd.current.remove` 里面是否存在被移除的元素\n\n\n\n---\ntitle: Hull\norder: 10\n---\n\n## Overview\n\nHull is used to process and represent the convex or concave polygon bounding box of a set of points. It can wrap a set of nodes in a minimal geometric shape, helping users better understand and analyze datasets.\n\n- **Convex Hull**: This is a convex polygon that contains all the points and has no indentations.\n- **Concave Hull**: This is a concave polygon that also contains all the points but may have indentations. The degree of indentation is controlled by the concavity parameter.\n\n## Usage Scenarios\n\nThe hull plugin is mainly applicable to the following scenarios:\n\n- Wrapping node collections in data visualization\n- Providing visual references to enhance spatial awareness\n- Identifying the collection relationship of specific nodes in complex network graphs\n\n## Basic Usage\n\nBelow is a simple example of initializing the Hull plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n key: 'my-hull', // Specify a unique identifier for subsequent dynamic updates\n members: ['node-1', 'node-2'], // List of node IDs to be wrapped\n concavity: Infinity, // Default to convex hull\n },\n ],\n});\n```\n\n## Online Experience\n\n\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| ---------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------------- | -------- |\n| type | Plugin type | string | `hull` | ✓ |\n| key | Unique identifier for the plugin, used for subsequent updates | string | - | |\n| members | Elements within the Hull, including nodes and edges | string[] | - | ✓ |\n| concavity | Concavity, the larger the value, the smaller the concavity; default is Infinity representing Convex Hull | number | Infinity | |\n| corner | Corner type, options are `rounded` \\| `smooth` \\| `sharp` | string | `rounded` | |\n| padding | Padding | number | `10` | |\n| label | Whether to display the label | boolean | true | |\n| labelPlacement | Label position | `left` \\| `right` \\| `top` \\| `bottom` \\| `center` | `bottom` | |\n| labelBackground | Whether to display the background | boolean | false | |\n| labelPadding | Label padding | number \\| number[] | 0 | |\n| labelCloseToPath | Whether the label is close to the hull | boolean | true | |\n| labelAutoRotate | Whether the label rotates with the hull, effective only when closeToPath is true | boolean | true | |\n| labelOffsetX | X-axis offset | number | 0 | |\n| labelOffsetY | Y-axis offset | number | 0 | |\n| labelMaxWidth | Maximum width of the text, exceeding will automatically ellipsis | number | 0 | |\n\nFor complete label styles, see [this link](https://g6.antv.antgroup.com/manual/element/node/base-node#%E6%A0%87%E7%AD%BE%E6%A0%B7%E5%BC%8F)\n\n### concavity\n\nThe concavity attribute is used to control the concavity of the Hull. When set to Infinity, a convex hull is generated; otherwise, a concave hull is generated.\n\n```js\n// Convex hull example\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n concavity: Infinity, // Convex hull\n members: ['node-1', 'node-2'],\n },\n ],\n});\n\n// Concave hull example\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n concavity: 50, // Concave hull\n members: ['node-1', 'node-2'],\n },\n ],\n});\n```\n\n## Code Examples\n\n### Basic Hull\n\nThe simplest way is to use the preset configuration directly:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2'], // List of node IDs to be wrapped\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n source: 'node-0',\n target: 'node-2',\n },\n {\n source: 'node-1',\n target: 'node-2',\n },\n {\n source: 'node-2',\n target: 'node-3',\n },\n {\n source: 'node-3',\n target: 'node-4',\n },\n {\n source: 'node-3',\n target: 'node-5',\n },\n {\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2'], // List of node IDs to be wrapped\n },\n ],\n behaviors: ['zoom-canvas', 'drag-canvas'],\n});\n\ngraph.render();\n```\n\n### Custom Styles\n\nYou can customize the style of the Hull as needed, such as adjusting color, transparency, and other properties.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2', 'node-3'],\n stroke: '#ff000033', // Red semi-transparent border\n fill: '#7e3feb', // Light purple fill\n fillOpacity: 0.2,\n lineWidth: 2,\n padding: 15, // Larger padding\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n source: 'node-0',\n target: 'node-2',\n },\n {\n source: 'node-1',\n target: 'node-2',\n },\n {\n source: 'node-2',\n target: 'node-3',\n },\n {\n source: 'node-3',\n target: 'node-4',\n },\n {\n source: 'node-3',\n target: 'node-5',\n },\n {\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2', 'node-3'],\n stroke: '#ff000033', // Red semi-transparent border\n fill: '#7e3feb', // Light purple fill\n fillOpacity: 0.2,\n lineWidth: 2,\n padding: 15, // Larger padding\n },\n ],\n behaviors: ['zoom-canvas', 'drag-canvas'],\n});\n\ngraph.render();\n```\n\n### Label Configuration\n\nYou can configure the position, background, offset, and other properties of the label to enhance the visual effect.\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2'],\n label: true, // Display label\n labelText: 'hull-a',\n labelPlacement: 'top', // Label position\n labelBackground: true, // Display label background\n labelPadding: 5, // Label padding\n },\n ],\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'center',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n source: 'node-0',\n target: 'node-2',\n },\n {\n source: 'node-1',\n target: 'node-2',\n },\n {\n source: 'node-2',\n target: 'node-3',\n },\n {\n source: 'node-3',\n target: 'node-4',\n },\n {\n source: 'node-3',\n target: 'node-5',\n },\n {\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2'],\n label: true, // Display label\n labelText: 'hull-a',\n labelPlacement: 'top', // Label position\n labelBackground: true, // Display label background\n labelPadding: 5, // Label padding\n },\n ],\n behaviors: ['zoom-canvas', 'drag-canvas'],\n});\n\ngraph.render();\n```\n\n## Practical Cases\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/collection.json')\n .then((res) => res.json())\n .then((data) => {\n const groupedNodesByCluster = data.nodes.reduce((acc, node) => {\n const cluster = node.data.cluster;\n acc[cluster] ||= [];\n acc[cluster].push(node.id);\n return acc;\n }, {});\n\n const createStyle = (baseColor) => ({\n fill: baseColor,\n stroke: baseColor,\n labelFill: '#fff',\n labelPadding: 2,\n labelBackgroundFill: baseColor,\n labelBackgroundRadius: 5,\n });\n\n const graph = new Graph({\n container: 'container',\n data,\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n node: {\n palette: { field: 'cluster' },\n },\n layout: {\n type: 'force',\n preventOverlap: true,\n linkDistance: (d) => {\n if (d.source === 'node0' || d.target === 'node0') {\n return 200;\n }\n return 80;\n },\n },\n plugins: [\n {\n key: 'hull-a',\n type: 'hull',\n members: groupedNodesByCluster['a'],\n labelText: 'cluster-a',\n ...createStyle('#1783FF'),\n },\n {\n key: 'hull-b',\n type: 'hull',\n members: groupedNodesByCluster['b'],\n labelText: 'cluster-b',\n ...createStyle('#00C9C9'),\n },\n {\n key: 'hull-c',\n type: 'hull',\n members: groupedNodesByCluster['c'],\n labelText: 'cluster-c',\n ...createStyle('#F08F56'),\n },\n {\n key: 'hull-d',\n type: 'hull',\n members: groupedNodesByCluster['d'],\n labelText: 'cluster-d',\n ...createStyle('#D580FF'),\n },\n ],\n autoFit: 'center',\n });\n\n graph.render();\n });\n```\n\n\n\n---\ntitle: 轮廓包围 Hull\norder: 10\n---\n\n## 概述\n\n轮廓包围(Hull)用于处理和表示一组点的凸多边形或凹多边形包围盒。它可以将一组节点包裹在一个最小的几何形状中,帮助用户更好地理解和分析数据集。\n\n- **凸包(Convex Hull)**:这是一个凸多边形,它包含所有的点,并且没有任何凹陷。\n- **凹包(Concave Hull)**:这是一个凹多边形,它同样包含所有的点,但是可能会有凹陷。凹包的凹陷程度由 concavity 参数控制。\n\n## 使用场景\n\n轮廓包围插件主要适用于以下场景:\n\n- 数据可视化中的节点集合包裹\n- 提供视觉参考,增强空间感知\n- 在复杂网络图中标识特定节点的集合关系\n\n## 基本用法\n\n以下是一个简单的 Hull 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n key: 'my-hull', // 指定唯一标识符,便于后续动态更新\n members: ['node-1', 'node-2'], // 需要包裹的节点 ID 列表\n concavity: Infinity, // 默认为凸包\n },\n ],\n});\n```\n\n## 在线体验\n\n\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ---------------- | ---------------------------------------------------------- | -------------------------------------------------- | --------- | ---- |\n| type | 插件类型 | string | `hull` | ✓ |\n| key | 插件唯一标识符,用于后续更新 | string | - | |\n| members | Hull 内的元素,包括节点和边 | string[] | - | ✓ |\n| concavity | 凹度,数值越大凹度越小;默认为 Infinity 代表为 Convex Hull | number | Infinity | |\n| corner | 拐角类型,可选值为 `rounded` \\| `smooth` \\| `sharp` | string | `rounded` | |\n| padding | 内边距 | number | `10` | |\n| label | 是否显示标签 | boolean | true | |\n| labelPlacement | 标签位置 | `left` \\| `right` \\| `top` \\| `bottom` \\| `center` | `bottom` | |\n| labelBackground | 是否显示背景 | boolean | false | |\n| labelPadding | 标签内边距 | number \\| number[] | 0 | |\n| labelCloseToPath | 标签是否贴合轮廓 | boolean | true | |\n| labelAutoRotate | 标签是否跟随轮廓旋转,仅在 closeToPath 为 true 时生效 | boolean | true | |\n| labelOffsetX | x 轴偏移量 | number | 0 | |\n| labelOffsetY | y 轴偏移量 | number | 0 | |\n| labelMaxWidth | 文本的最大宽度,超出会自动省略 | number | 0 | |\n\n完整的标签样式见[此链接](https://g6.antv.antgroup.com/manual/element/node/base-node#%E6%A0%87%E7%AD%BE%E6%A0%B7%E5%BC%8F)\n\n### concavity\n\nconcavity 属性用于控制 Hull 的凹度。当设置为 Infinity 时,生成的是凸包;否则会生成凹包。\n\n```js\n// 凸包示例\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n concavity: Infinity, // 凸包\n members: ['node-1', 'node-2'],\n },\n ],\n});\n\n// 凹包示例\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n concavity: 50, // 凹包\n members: ['node-1', 'node-2'],\n },\n ],\n});\n```\n\n## 代码示例\n\n### 基础 Hull\n\n最简单的方式是直接使用预设配置:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2'], // 需要包裹的节点 ID 列表\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n source: 'node-0',\n target: 'node-2',\n },\n {\n source: 'node-1',\n target: 'node-2',\n },\n {\n source: 'node-2',\n target: 'node-3',\n },\n {\n source: 'node-3',\n target: 'node-4',\n },\n {\n source: 'node-3',\n target: 'node-5',\n },\n {\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2'], // 需要包裹的节点 ID 列表\n },\n ],\n behaviors: ['zoom-canvas', 'drag-canvas'],\n});\n\ngraph.render();\n```\n\n### 自定义样式\n\n您可以根据需要自定义 Hull 的样式,例如调整颜色、透明度等属性。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2', 'node-3'],\n stroke: '#ff000033', // 红色半透明边框\n fill: '#7e3feb', // 浅紫色填充\n fillOpacity: 0.2,\n lineWidth: 2,\n padding: 15, // 更大的内边距\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'view',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n source: 'node-0',\n target: 'node-2',\n },\n {\n source: 'node-1',\n target: 'node-2',\n },\n {\n source: 'node-2',\n target: 'node-3',\n },\n {\n source: 'node-3',\n target: 'node-4',\n },\n {\n source: 'node-3',\n target: 'node-5',\n },\n {\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2', 'node-3'],\n stroke: '#ff000033', // 红色半透明边框\n fill: '#7e3feb', // 浅紫色填充\n fillOpacity: 0.2,\n lineWidth: 2,\n padding: 15, // 更大的内边距\n },\n ],\n behaviors: ['zoom-canvas', 'drag-canvas'],\n});\n\ngraph.render();\n```\n\n### 标签配置\n\n您可以配置标签的位置、背景、偏移量等属性,以增强可视化效果。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2'],\n label: true, // 显示标签\n labelText: 'hull-a',\n labelPlacement: 'top', // 标签位置\n labelBackground: true, // 显示标签背景\n labelPadding: 5, // 标签内边距\n },\n ],\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 300,\n height: 150,\n autoFit: 'center',\n data: {\n nodes: [\n {\n id: 'node-0',\n data: { cluster: 'a' },\n style: { x: 555, y: 151 },\n },\n {\n id: 'node-1',\n data: { cluster: 'a' },\n style: { x: 532, y: 323 },\n },\n {\n id: 'node-2',\n data: { cluster: 'a' },\n style: { x: 473, y: 227 },\n },\n {\n id: 'node-3',\n data: { cluster: 'a' },\n style: { x: 349, y: 212 },\n },\n {\n id: 'node-4',\n data: { cluster: 'b' },\n style: { x: 234, y: 201 },\n },\n {\n id: 'node-5',\n data: { cluster: 'b' },\n style: { x: 338, y: 333 },\n },\n {\n id: 'node-6',\n data: { cluster: 'b' },\n style: { x: 365, y: 91 },\n },\n ],\n edges: [\n {\n source: 'node-0',\n target: 'node-2',\n },\n {\n source: 'node-1',\n target: 'node-2',\n },\n {\n source: 'node-2',\n target: 'node-3',\n },\n {\n source: 'node-3',\n target: 'node-4',\n },\n {\n source: 'node-3',\n target: 'node-5',\n },\n {\n source: 'node-3',\n target: 'node-6',\n },\n ],\n },\n plugins: [\n {\n type: 'hull',\n members: ['node-1', 'node-2'],\n label: true, // 显示标签\n labelText: 'hull-a',\n labelPlacement: 'top', // 标签位置\n labelBackground: true, // 显示标签背景\n labelPadding: 5, // 标签内边距\n },\n ],\n behaviors: ['zoom-canvas', 'drag-canvas'],\n});\n\ngraph.render();\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nfetch('https://assets.antv.antgroup.com/g6/collection.json')\n .then((res) => res.json())\n .then((data) => {\n const groupedNodesByCluster = data.nodes.reduce((acc, node) => {\n const cluster = node.data.cluster;\n acc[cluster] ||= [];\n acc[cluster].push(node.id);\n return acc;\n }, {});\n\n const createStyle = (baseColor) => ({\n fill: baseColor,\n stroke: baseColor,\n labelFill: '#fff',\n labelPadding: 2,\n labelBackgroundFill: baseColor,\n labelBackgroundRadius: 5,\n });\n\n const graph = new Graph({\n container: 'container',\n data,\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n node: {\n palette: { field: 'cluster' },\n },\n layout: {\n type: 'force',\n preventOverlap: true,\n linkDistance: (d) => {\n if (d.source === 'node0' || d.target === 'node0') {\n return 200;\n }\n return 80;\n },\n },\n plugins: [\n {\n key: 'hull-a',\n type: 'hull',\n members: groupedNodesByCluster['a'],\n labelText: 'cluster-a',\n ...createStyle('#1783FF'),\n },\n {\n key: 'hull-b',\n type: 'hull',\n members: groupedNodesByCluster['b'],\n labelText: 'cluster-b',\n ...createStyle('#00C9C9'),\n },\n {\n key: 'hull-c',\n type: 'hull',\n members: groupedNodesByCluster['c'],\n labelText: 'cluster-c',\n ...createStyle('#F08F56'),\n },\n {\n key: 'hull-d',\n type: 'hull',\n members: groupedNodesByCluster['d'],\n labelText: 'cluster-d',\n ...createStyle('#D580FF'),\n },\n ],\n autoFit: 'center',\n });\n\n graph.render();\n });\n```\n\n```\n\n```\n\n\n\n---\ntitle: Legend\norder: 11\n---\n\n## Overview\n\nThe Legend plugin is used to display classification information of elements in the graph, supporting the display of classification information for nodes, edges, and combos. Through the legend, users can quickly perceive the classification information of related elements in the graph and quickly locate elements by clicking on the corresponding legend items, improving user browsing efficiency.\n\n## Usage Scenarios\n\nThis plugin is mainly used for:\n\n- Quickly classifying elements through the legend\n- Quickly highlighting and locating corresponding elements through the legend\n\n## Basic Usage\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n // Other configurations...\n plugins: [\n {\n type: 'legend', // Plugin type is legend\n nodeField: 'cluster', // Array field name for node grouping\n edgeField: 'cluster', // Array field name for edge grouping\n },\n ],\n});\n```\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------- | -------- |\n| type | Plugin type | string | `legend` | ✓ |\n| key | Unique identifier for the plugin, used for subsequent updates | string | - | |\n| trigger | How the legend item triggers the corresponding item highlight:
- `hover`: Triggered when the mouse enters the legend item
- `click`: Triggered when the mouse clicks the legend item | `hover` \\| `click` | `hover` | |\n| position | Relative position of the legend on the canvas, [optional values](#cardinalplacement) | [CardinalPlacement](#cardinalplacement) | `bottom` | |\n| container | Container to which the legend is mounted, if not provided, it is mounted to the container where the Graph is located | HTMLElement \\| string | - | |\n| className | Legend canvas class name, not effective when an external container is passed | string | - | |\n| containerStyle | Style of the legend container, not effective when an external container is passed | [CSSStyleDeclaration](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration) | - | |\n| nodeField | Node classification identifier | string \\| (item: ElementDatum) => string | - | |\n| edgeField | Edge classification identifier | string \\| (item: ElementDatum) => string | - | |\n| comboField | Combo classification identifier | string \\| (item: ElementDatum) => string | - | |\n| orientation | Layout direction of legend items:
- `horizontal`: Horizontal direction
- `vertical`: Vertical direction | `horizontal` \\| `vertical` | 'horizontal' | |\n| layout | Layout method:
- `flex`: Flexible layout
- `grid`: Grid layout | `flex` \\| `grid` | `flex` | |\n| showTitle | Whether to display the title | boolean | false |\n| titleText | Title content | string | \"\" |\n| x | Relative horizontal position of the legend on the canvas, higher priority than position | number | - | |\n| y | Relative vertical position of the legend on the canvas, higher priority than position | number | - | |\n| width | Width of the legend | number | 240 | |\n| height | Height of the legend | number | 160 | |\n| itemSpacing | Spacing between the text of the legend item and the corresponding marker | number | 4 | |\n| rowPadding | Spacing between each row in the legend | number | 10 | |\n| colPadding | Spacing between each column in the legend | number | 10 | |\n| itemMarkerSize | Size of the legend item marker | number | 16 | |\n| itemLabelFontSize | Font size of the legend item text | number | 16 | |\n| gridCol | Maximum number of columns allowed for legend items when width permits | number | - | |\n| gridRow | Maximum number of rows allowed for legend items when height permits | number | - | |\n\n### CardinalPlacement\n\nThe `position` property supports the following values:\n\n- `'top-left'`: Top left corner\n- `'top-right'`: Top right corner\n- `'bottom-left'`: Bottom left corner\n- `'bottom-right'`: Bottom right corner\n- `'left-top'`: Left side near the top\n- `'left-bottom'`: Left side near the bottom\n- `'right-top'`: Right side near the top\n- `'right-bottom'`: Right side near the bottom\n\n## Code Examples\n\n### Basic Legend\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'legend', // Plugin type is legend\n nodeField: 'cluster', // Array field name for node grouping\n edgeField: 'cluster', // Array field name for edge grouping\n },\n ],\n});\n```\n\n### Custom Legend Position\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n // Other configurations...\n plugins: [\n {\n type: 'legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n // You can quickly specify the position through position\n // position: \"top-left\",\n // Or you can more flexibly control the position of the legend through x, y\n x: 20,\n y: 20,\n },\n ],\n});\n```\n\n### Custom Legend Item Layout\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n // Other configurations...\n plugins: [\n {\n type: 'legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n layout: 'flex',\n // Control to display only one row\n gridRow: 1,\n // Control to display 10 columns in one row, a page button will be displayed when the column width is insufficient\n gridCol: 10,\n },\n ],\n});\n```\n\n## Common Issues\n\n### 1. Setting orientation is ineffective?\n\n`orientation` mainly controls the direction of the layout, and the specific display of **multiple columns in one row** or **multiple rows in one column** is mainly controlled by `gridRow` and `gridCol`. For example, if you want it to look like a vertical legend item, you can configure it like this:\n\n```js\n plugins: [\n {\n type: 'legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n layout: \"flex\",\n // Control to display 1 column in one row\n gridCol:1,\n // Control to display up to 20 rows\n gridRow: 20,\n },\n ],\n```\n\nThis way, it becomes a legend with only one column, conforming to the visual vertical arrangement.\n\n### 2. How to dynamically update the toolbar?\n\nYou can use the `updatePlugin` method to dynamically update the toolbar:\n\n```js\nconst graph = new Graph({\n data,\n // Other configurations...\n plugins: [\n {\n type: 'legend',\n key: 'my-legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n },\n ],\n});\n\n// Update legend position\ngraph.updatePlugin({\n key: 'my-legend',\n position: 'bottom-right',\n});\n```\n\n## Practical Cases\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n { id: 'node-3', type: 'triangle', data: { cluster: 'node-type3' } },\n { id: 'node-4', type: 'diamond', data: { cluster: 'node-type4' } },\n ],\n edges: [\n { source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } },\n { source: 'node-1', target: 'node-4', data: { cluster: 'edge-type2' } },\n { source: 'node-3', target: 'node-4' },\n { source: 'node-2', target: 'node-4', data: { cluster: 'edge-type3' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: { size: 32 },\n palette: {\n field: 'cluster',\n },\n },\n layout: {\n type: 'force',\n },\n plugins: [\n {\n type: 'legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 图例 Legend\norder: 11\n---\n\n## 概述\n\n图例(Legend)插件用于展示图中元素的分类信息,支持节点、边、组合的分类信息展示。通过图例,用户可以快速感知到图中相关元素的分类信息,也可以通过点击对应图例项来快速定位到元素,提高用户的浏览效率。\n\n## 使用场景\n\n这一插件主要用于:\n\n- 通过图例快速对元素进行分类\n- 通过图例快速高亮定位到对应元素\n\n## 基本用法\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n // 其他配置...\n plugins: [\n {\n type: 'legend', // 插件类型为 legend\n nodeField: 'cluster', // 用于节点分组的数组字段名称\n edgeField: 'cluster', // 用于边分组的数组字段名称\n },\n ],\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ----------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------ | ---- |\n| type | 插件类型 | string | `legend` | ✓ |\n| key | 插件唯一标识符,用于后续更新 | string | - | |\n| trigger | 图例项触发对应项高亮的方式:
- `hover`:鼠标移入图例项时触发
- `click`:鼠标点击图例项时触发 | `hover` \\| `click` | `hover` | |\n| position | 图例在画布中的相对位置,[可选值](#cardinalplacement) | [CardinalPlacement](#cardinalplacement) | `bottom` | |\n| container | 图例挂载的容器,无则挂载到 Graph 所在容器 | HTMLElement \\| string | - | |\n| className | 图例画布类名,传入外置容器时不生效 | string | - | |\n| containerStyle | 图例的容器样式,传入外置容器时不生效 | [CSSStyleDeclaration](https://developer.mozilla.org/zh-CN/docs/Web/API/CSSStyleDeclaration) | - | |\n| nodeField | 节点分类标识 | string \\| (item: ElementDatum) => string | - | |\n| edgeField | 边分类标识 | string \\| (item: ElementDatum) => string | - | |\n| comboField | 组合分类标识 | string \\| (item: ElementDatum) => string | - | |\n| orientation | 图例项的布局方向:
- `horizontal`:水平方向
- `vertical`:垂直方向 | `horizontal` \\| `vertical` | 'horizontal' | |\n| layout | 布局方式:
- `flex`:弹性布局
- `grid`:网格布局 | `flex` \\| `grid` | `flex` | |\n| showTitle | 是否显示标题 | boolean | false |\n| titleText | 标题内容 | string | \"\" |\n| x | 图例在画布中的相对的横向位置,优先级高于position | number | - | |\n| y | 图例在画布中的相对的纵向位置,优先级高于position | number | - | |\n| width | 图例的宽度 | number | 240 | |\n| height | 图例的高度 | number | 160 | |\n| itemSpacing | 图例项的文本和对应标记之间的间距 | number | 4 | |\n| rowPadding | 图例中每行之间的间距 | number | 10 | |\n| colPadding | 图例中每列之间的间距 | number | 10 | |\n| itemMarkerSize | 图例项标记的大小 | number | 16 | |\n| itemLabelFontSize | 图例项文本的字体大小 | number | 16 | |\n| gridCol | 图例项在宽度允许情况下的最大列数 | number | - | |\n| gridRow | 图例项在高度允许情况下的最大行数 | number | - | |\n\n### CardinalPlacement\n\n`position` 属性支持以下值:\n\n- `'top-left'`:左上角\n- `'top-right'`:右上角\n- `'bottom-left'`:左下角\n- `'bottom-right'`:右下角\n- `'left-top'`:左侧靠上\n- `'left-bottom'`:左侧靠下\n- `'right-top'`:右侧靠上\n- `'right-bottom'`:右侧靠下\n\n## 代码示例\n\n### 基础图例\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'legend', // 插件类型为 legend\n nodeField: 'cluster', // 用于节点分组的数组字段名称\n edgeField: 'cluster', // 用于边分组的数组字段名称\n },\n ],\n});\n```\n\n### 自定义图例位置\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n // 其他配置...\n plugins: [\n {\n type: 'legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n // 可以通过 position 快捷的来指定位置\n // position: \"top-left\",\n // 也可以通过x,y来更加灵活的控制图例的位置\n x: 20,\n y: 20,\n },\n ],\n});\n```\n\n### 自定义图例项布局\n\n```js\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n ],\n edges: [{ source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } }],\n};\n\nconst graph = new Graph({\n data,\n // 其他配置...\n plugins: [\n {\n type: 'legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n layout: 'flex',\n // 控制只显示一行\n gridRow: 1,\n // 控制一行显示10列,当列宽不足时会显示翻页按钮\n gridCol: 10,\n },\n ],\n});\n```\n\n## 常见问题\n\n### 1. 设置了 orientation 无效?\n\n`orientation`主要控制布局的方向,具体展示**一行多列**还是**一列多行**,主要通过 `gridRow` 以及`gridCol`来控制,例如想要看起来像是竖向的图例项,则可以通过这样配置:\n\n```js\n plugins: [\n {\n type: 'legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n layout: \"flex\",\n // 控制一行显示1列\n gridCol:1,\n // 控制显示最多20行\n gridRow: 20,\n },\n ],\n```\n\n这样就变成了只有一列的图例,符合视觉上的竖向排列。\n\n### 2. 如何动态更新工具栏?\n\n可以使用 `updatePlugin` 方法动态更新工具栏:\n\n```js\nconst graph = new Graph({\n data,\n // 其他配置...\n plugins: [\n {\n type: 'legend',\n key: 'my-legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n },\n ],\n});\n\n// 更新图例位置\ngraph.updatePlugin({\n key: 'my-legend',\n position: 'bottom-right',\n});\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node-1', type: 'circle', data: { cluster: 'node-type1' } },\n { id: 'node-2', type: 'rect', data: { cluster: 'node-type2' } },\n { id: 'node-3', type: 'triangle', data: { cluster: 'node-type3' } },\n { id: 'node-4', type: 'diamond', data: { cluster: 'node-type4' } },\n ],\n edges: [\n { source: 'node-1', target: 'node-2', data: { cluster: 'edge-type1' } },\n { source: 'node-1', target: 'node-4', data: { cluster: 'edge-type2' } },\n { source: 'node-3', target: 'node-4' },\n { source: 'node-2', target: 'node-4', data: { cluster: 'edge-type3' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n node: {\n style: { size: 32 },\n palette: {\n field: 'cluster',\n },\n },\n layout: {\n type: 'force',\n },\n plugins: [\n {\n type: 'legend',\n nodeField: 'cluster',\n edgeField: 'cluster',\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Minimap\norder: 12\n---\n\n## Overview\n\nThe main function of the Minimap is to provide users with an overall layout of the current graph content in the form of a thumbnail, allowing quick positioning of graph operation locations.\n\n**⚠️ Note**, The Minimap plugin is currently incompatible with React Node rendering mechanism. When using Minimap functionality, it is recommended to implement node rendering through [built-in nodes](/en/manual/element/node/overview) or [custom nodes](/en/manual/element/node/custom-node).\n\n## Usage Scenarios\n\nThe Minimap plugin is mainly applicable to the following scenarios:\n\n- Providing a global view for quick area positioning\n- Navigation and interaction assistance, allowing quick positioning to the target location through the minimap\n\n## Basic Usage\n\nBelow is a simple example of initializing the Minimap plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n key: 'minimap',\n type: 'minimap',\n size: [240, 160],\n },\n ],\n});\n```\n\n## Online Experience\n\n\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| -------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | -------- |\n| type | Plugin type | string | `minimap` | ✓ |\n| key | Unique identifier for the plugin, used for subsequent updates | string | - | |\n| className | Class name of the thumbnail canvas, not effective when an external container is passed | string | | |\n| container | Container to which the thumbnail is mounted, if not provided, it is mounted to the container where the Graph is located | HTMLElement \\| string | | |\n| containerStyle | Style of the thumbnail container, not effective when an external container is passed | Partial\\ | | |\n| delay | Delay update time (milliseconds) for performance optimization | number | 128 | |\n| filter | Filter for filtering out elements that do not need to be displayed | (id: string, elementType: `node` \\| `edge` \\| `combo`) => boolean | | |\n| maskStyle | Style of the mask | Partial\\ | | |\n| padding | Padding | number \\| number[] | 10 | |\n| position | Position of the thumbnail relative to the canvas | [number, number] \\| `left` \\| `right` \\| `top` \\| `bottom` \\| `left-top` \\| `left-bottom` \\| `right-top` \\| `right-bottom` \\| `top-left` \\| `top-right` \\| `bottom-left` \\| `bottom-right` \\| `center` | `right-bottom` | |\n| renderer | Renderer, default is Canvas renderer | IRenderer | | |\n| shape | Method for generating element thumbnails | `key` \\| ((id: string, elementType: `node` \\| `edge` \\| `combo`, element: DisplayObject) => DisplayObject) | `key` | |\n| size | Width and height | [number, number] | [240, 160] | |\n\n### containerStyle\n\nSet the style of the thumbnail container, not effective when an external container is passed. Inherits all CSS style properties (CSSStyleDeclaration), and you can use any valid CSS property to configure the style of the thumbnail container.\n\nBelow are some common configurations:\n\n| Property | Description | Type | Default Value | Required |\n| ------------ | -------------------------- | ------ | ---------------- | -------- |\n| border | Container border style | string | `1px solid #ddd` | ✓ |\n| background | Container background color | string | `#fff` | ✓ |\n| borderRadius | Container border radius | string | - | |\n| boxShadow | Container shadow effect | string | - | |\n| padding | Container padding | string | - | |\n| margin | Container margin | string | - | |\n| opacity | Opacity | string | - | |\n\n### maskStyle\n\nSpecify the style of the mask. Inherits all CSS style properties (CSSStyleDeclaration), and you can use any valid CSS property to configure the style of the thumbnail container.\n\nBelow are some common configurations:\n\n| Property | Description | Type | Default Value | Required |\n| ------------ | -------------------------- | ------ | -------------------- | -------- |\n| border | Container border style | string | `1px solid #ddd` | ✓ |\n| background | Container background color | string | `rgba(0, 0, 0, 0.1)` | ✓ |\n| borderRadius | Container border radius | string | - | - |\n| boxShadow | Container shadow effect | string | - | - |\n| padding | Container padding | string | - | - |\n| margin | Container margin | string | - | - |\n| opacity | Opacity | string | - | - |\n\n### position\n\nPosition of the thumbnail relative to the canvas, the thumbnail position configuration supports array form and preset value form.\n\n- Array form [number, number] represents relative position, with a value range of 0~1. For example: [0, 0] represents the top left corner of the canvas, [1, 1] represents the bottom right corner of the canvas.\n- Preset value form is used to set the fixed position of the thumbnail on the canvas, optional values are: `left` \\| `right` \\| `top` \\| `bottom` \\| `left-top` \\| `left-bottom` \\| `right-top` \\| `right-bottom` \\| `top-left` \\| `top-right` \\| `bottom-left` \\| `bottom-right` \\| `center`\n\n```js\nconst graph = new Graph({\n plugins:[\n {\n ... // Other configurations\n key: 'minimap',\n type: 'minimap',\n position: 'right-bottom' // Modify the position of the minimap here\n }\n ]\n})\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 300,\n data: {\n nodes: Array.from({ length: 50 }).map((_, i) => ({\n id: `node-${i}`,\n x: Math.random() * 500,\n y: Math.random() * 300,\n })),\n edges: Array.from({ length: 100 }).map((_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 50)}`,\n target: `node-${Math.floor(Math.random() * 50)}`,\n })),\n },\n node: { style: { fill: '#7e3feb' } },\n edge: { style: { stroke: '#8b9baf' } },\n layout: { type: 'force' },\n behaviors: ['drag-canvas'],\n plugins: [{ type: 'minimap', key: 'minimap', size: [240, 160], position: 'right-bottom' }],\n});\n\ngraph.render();\n```\n\n### size\n\nSet the width and height of the minimap, default value is [240, 160]\n\n```js\nconst graph = new Graph({\n plugins:[\n {\n ... // Other configurations\n key: 'minimap',\n type: 'minimap',\n size: [200, 120] // Set the width and height of the minimap\n }\n ]\n})\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 300,\n data: {\n nodes: Array.from({ length: 50 }).map((_, i) => ({\n id: `node-${i}`,\n x: Math.random() * 500,\n y: Math.random() * 300,\n })),\n edges: Array.from({ length: 100 }).map((_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 50)}`,\n target: `node-${Math.floor(Math.random() * 50)}`,\n })),\n },\n node: { style: { fill: '#7e3feb' } },\n edge: { style: { stroke: '#8b9baf' } },\n layout: { type: 'force' },\n behaviors: ['drag-canvas'],\n plugins: [{ type: 'minimap', key: 'minimap', size: [200, 120], position: 'right-bottom' }],\n});\n\ngraph.render();\n```\n\n## Practical Cases\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: { nodes: Array.from({ length: 20 }).map((_, i) => ({ id: `node${i}` })) },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n plugins: [\n {\n type: 'minimap',\n size: [240, 160],\n },\n ],\n node: {\n palette: 'spectral',\n },\n layout: {\n type: 'circular',\n },\n autoFit: 'view',\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 小地图 Minimap\norder: 12\n---\n\n## 概述\n\nMinimap(小地图)的主要作用是为用户提供以缩略图形式展示当前图内容的整体布局,可以快速定位图操作位置。\n\n**⚠️ 需要注意**,Minimap 插件当前不兼容 React Node 渲染机制,在需要使用 Minimap 功能的场景中,建议通过 [内置节点](/manual/element/node/overview) 或者[自定义节点](/manual/element/node/custom-node) 实现节点渲染。\n\n## 使用场景\n\nMinimap(小地图)插件主要适用于以下场景:\n\n- 提供全局视野,快速定位区域\n- 导航与交互辅助,通过操作小地图可以快速定位到目标位置\n\n## 基本用法\n\n以下是一个简单的 Minimap 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n key: 'minimap',\n type: 'minimap',\n size: [240, 160],\n },\n ],\n});\n```\n\n## 在线体验\n\n\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | ---- |\n| type | 插件类型 | string | `minimap` | ✓ |\n| key | 插件唯一标识符,用于后续更新 | string | - | |\n| className | 缩略图画布类名,传入外置容器时不生效 | string | | |\n| container | 缩略图挂载的容器,无则挂载到 Graph 所在容器 | HTMLElement \\| string | | |\n| containerStyle | 缩略图的容器样式,传入外置容器时不生效 | Partial\\ | | |\n| delay | 延迟更新时间(毫秒),用于性能优化 | number | 128 | |\n| filter | 过滤器,用于过滤不必显示的元素 | (id: string, elementType: `node` \\| `edge` \\| `combo`) => boolean | | |\n| maskStyle | 遮罩的样式 | Partial\\ | | |\n| padding | 内边距 | number \\| number[] | 10 | |\n| position | 缩略图相对于画布的位置 | [number, number] \\| `left` \\| `right` \\| `top` \\| `bottom` \\| `left-top` \\| `left-bottom` \\| `right-top` \\| `right-bottom` \\| `top-left` \\| `top-right` \\| `bottom-left` \\| `bottom-right` \\| `center` | `right-bottom` | |\n| renderer | 渲染器,默认使用 Canvas 渲染器 | IRenderer | | |\n| shape | 元素缩略图形的生成方法 | `key` \\| ((id: string, elementType: `node` \\| `edge` \\| `combo`, element: DisplayObject) => DisplayObject) | `key` | |\n| size | 宽度和高度 | [number, number] | [240, 160] | |\n\n### containerStyle\n\n设置缩略图的容器样式,传入外置容器时不生效。继承了所有 CSS 样式属性(CSSStyleDeclaration),你可以使用任何合法的 CSS 属性来配置缩略图容器的样式。\n\n以下是一些常用配置:\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------ | ------------ | ------ | ---------------- | ---- |\n| border | 容器边框样式 | string | `1px solid #ddd` | ✓ |\n| background | 容器背景颜色 | string | `#fff` | ✓ |\n| borderRadius | 容器圆角大小 | string | - | |\n| boxShadow | 容器阴影效果 | string | - | |\n| padding | 容器内边距 | string | - | |\n| margin | 容器外边距 | string | - | |\n| opacity | 透明度 | string | - | |\n\n### maskStyle\n\n指定遮罩的样式。继承了所有 CSS 样式属性(CSSStyleDeclaration),你可以使用任何合法的 CSS 属性来配置缩略图容器的样式。\n\n以下是一些常用配置:\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------ | ------------ | ------ | -------------------- | ---- |\n| border | 容器边框样式 | string | `1px solid #ddd` | ✓ |\n| background | 容器背景颜色 | string | `rgba(0, 0, 0, 0.1)` | ✓ |\n| borderRadius | 容器圆角大小 | string | - | - |\n| boxShadow | 容器阴影效果 | string | - | - |\n| padding | 容器内边距 | string | - | - |\n| margin | 容器外边距 | string | - | - |\n| opacity | 透明度 | string | - | - |\n\n### position\n\n缩略图相对于画布的位置,缩略图位置配置支持数组形式和预设值形式。\n\n- 数组形式 [number, number] 表示相对位置,取值范围为 0~1。举例:[0, 0] 代表画布左上角,[1, 1] 代表画布右下角。\n- 预设值形式用于设定缩略图所在画布固定方位,可选值有:`left` \\| `right` \\| `top` \\| `bottom` \\| `left-top` \\| `left-bottom` \\| `right-top` \\| `right-bottom` \\| `top-left` \\| `top-right` \\| `bottom-left` \\| `bottom-right` \\| `center`\n\n```js\nconst graph = new Graph({\n plugins:[\n {\n ... // 其他配置\n key: 'minimap',\n type: 'minimap',\n position: 'right-bottom' // 这里进行修改minimap所在位置\n }\n ]\n})\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 300,\n data: {\n nodes: Array.from({ length: 50 }).map((_, i) => ({\n id: `node-${i}`,\n x: Math.random() * 500,\n y: Math.random() * 300,\n })),\n edges: Array.from({ length: 100 }).map((_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 50)}`,\n target: `node-${Math.floor(Math.random() * 50)}`,\n })),\n },\n node: { style: { fill: '#7e3feb' } },\n edge: { style: { stroke: '#8b9baf' } },\n layout: { type: 'force' },\n behaviors: ['drag-canvas'],\n plugins: [{ type: 'minimap', key: 'minimap', size: [240, 160], position: 'right-bottom' }],\n});\n\ngraph.render();\n```\n\n### size\n\n设置小地图的宽度和高度,默认值为 [240, 160]\n\n```js\nconst graph = new Graph({\n plugins:[\n {\n ... // 其他配置\n key: 'minimap',\n type: 'minimap',\n size: [200, 120] // minimap的宽度和高度的设置\n }\n ]\n})\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 300,\n data: {\n nodes: Array.from({ length: 50 }).map((_, i) => ({\n id: `node-${i}`,\n x: Math.random() * 500,\n y: Math.random() * 300,\n })),\n edges: Array.from({ length: 100 }).map((_, i) => ({\n id: `edge-${i}`,\n source: `node-${Math.floor(Math.random() * 50)}`,\n target: `node-${Math.floor(Math.random() * 50)}`,\n })),\n },\n node: { style: { fill: '#7e3feb' } },\n edge: { style: { stroke: '#8b9baf' } },\n layout: { type: 'force' },\n behaviors: ['drag-canvas'],\n plugins: [{ type: 'minimap', key: 'minimap', size: [200, 120], position: 'right-bottom' }],\n});\n\ngraph.render();\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: { nodes: Array.from({ length: 20 }).map((_, i) => ({ id: `node${i}` })) },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n plugins: [\n {\n type: 'minimap',\n size: [240, 160],\n },\n ],\n node: {\n palette: 'spectral',\n },\n layout: {\n type: 'circular',\n },\n autoFit: 'view',\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Plugin Overview\norder: 0\n---\n\n## What is a Plugin\n\n\n\nA Plugin is the most flexible extension mechanism in G6, allowing users to extend G6's functionality, such as adding graphical components to the canvas or implementing undo/redo features.\n\nMost customization needs can be achieved through plugins. G6 comes with some built-in plugins, such as: [Tooltip](/en/manual/plugin/tooltip), [Grid](/en/manual/plugin/grid-line), [History](/en/manual/plugin/history).\n\n## Built-in Plugins\n\nG6 provides a rich set of built-in plugins covering various common functional scenarios:\n\n| Category | Plugin Name | Registration Type | Description |\n| ---------------------------- | --------------------------------------------------------------------------------------------------- | ------------------ | ---------------------------------------------------------------------- |\n| **Visual Style Enhancement** | | | |\n| | [Grid Line](/en/manual/plugin/grid-line) | `grid-line` | Displays grid reference lines on the canvas |\n| | [Background](/en/manual/plugin/background) | `background` | Adds background images or colors to the canvas |\n| | [Watermark](/en/manual/plugin/watermark) | `watermark` | Adds a watermark to the canvas to protect copyright |\n| | [Hull](/en/manual/plugin/hull) | `hull` | Creates an outline for a specified set of nodes |\n| | [Bubble Sets](/en/manual/plugin/bubble-sets) | `bubble-sets` | Creates smooth bubble-like element outlines |\n| | [Snapline](/en/manual/plugin/snapline) | `snapline` | Displays alignment reference lines when dragging elements |\n| **Navigation and Overview** | | | |\n| | [Minimap](/en/manual/plugin/minimap) | `minimap` | Displays a thumbnail preview of the graph, supporting navigation |\n| | [Fullscreen](/en/manual/plugin/fullscreen) | `fullscreen` | Supports full-screen display and exit for charts |\n| | [Timebar](/en/manual/plugin/timebar) | `timebar` | Provides filtering and playback control for temporal data |\n| **Interactive Controls** | | | |\n| | [Toolbar](/en/manual/plugin/toolbar) | `toolbar` | Provides a collection of common operation buttons |\n| | [Context Menu](/en/manual/plugin/contextmenu) | `contextmenu` | Displays a menu of selectable operations on right-click |\n| | [Tooltip](/en/manual/plugin/tooltip) | `tooltip` | Displays detailed information about elements on hover |\n| | [Legend](/en/manual/plugin/legend) | `legend` | Displays categories and corresponding style descriptions of chart data |\n| **Data Exploration** | | | |\n| | [Fisheye](/en/manual/plugin/fisheye) | `fisheye` | Provides a focus + context exploration experience |\n| | [Edge Filter Lens](/en/manual/plugin/edge-filter-lens) | `edge-filter-lens` | Filters and displays edges within a specified area |\n| | [Edge Bundling](/en/manual/plugin/edge-bundling) | `edge-bundling` | Bundles edges with similar paths together to reduce visual clutter |\n| **Advanced Features** | | | |\n| | [History](/en/manual/plugin/history) | `history` | Supports undo/redo operations |\n| | [Camera Setting](/enhttps://github.com/antvis/G6/blob/v5/packages/g6/src/plugins/camera-setting.ts) | `camera-setting` | Configures camera parameters in a 3D scene |\n\nFor detailed configuration of each plugin, refer to the [Built-in Plugin Documentation](/en/manual/plugin/grid-line).\n\n## Configuration Methods\n\n### Basic Configuration\n\nSpecify the required plugins through the `plugins` array when initializing the graph instance:\n\n```javascript {}5\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n // Other configurations...\n plugins: ['grid', 'minimap', 'tooltip'],\n});\n```\n\n### Configuring Plugin Parameters\n\nFor plugins that require custom parameters, you can configure properties using the `object` form:\n\n```javascript {5-9}\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n 'grid',\n {\n type: 'tooltip',\n key: 'my-tooltip', // Specify a key for the plugin for future updates\n getContent: (e) => `
Node: ${e.target.id}
`,\n },\n ],\n});\n```\n\n### Dynamically Updating Plugins\n\nG6 supports dynamic management of plugins during the runtime of the graph instance to meet complex interaction needs:\n\nUse the [getPlugins](/en/api/plugin#graphgetplugins) method to get the current list of plugins:\n\n```javascript\n// Get the list of plugins\nconst plugins = graph.getPlugins();\n// console.log(plugins) 👉 ['minimap', 'grid']\n```\n\nYou can adjust plugins using the [setPlugins](/en/api/plugin#graphsetpluginsplugins) method:\n\n```javascript\n// Add a new plugin\ngraph.setPlugins((plugins) => [...plugins, 'minimap']);\n\n// Remove a plugin\ngraph.setPlugins((plugins) => plugins.filter((p) => p !== 'grid'));\n```\n\nYou can update the configuration of a plugin using the [updatePlugin](/en/api/plugin#graphupdatepluginplugin) method:\n\n```javascript {6,14}\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'tooltip',\n key: 'my-tooltip',\n getContent: (e) => `
Node: ${e.target.id}
`,\n },\n ],\n});\n\n// Update a single plugin\ngraph.updatePlugin({\n key: 'my-tooltip',\n getContent: (e) => `
Updated content: ${e.target.id}
`,\n});\n```\n\n:::warning{title=Note}\nWhen using the `updatePlugin` method, you need to specify a unique `key` for the plugin during initialization.\n:::\n\n### Uninstalling Plugins\n\nUse the [setPlugins](/en/api/plugin#graphsetpluginsplugins) method to uninstall plugins by setting the plugin configuration list to empty:\n\n```javascript\n// Uninstall all plugins\ngraph.setPlugins([]);\n```\n\n### Calling Plugin Methods\n\nSome plugins provide API methods for users to call, such as the `history` plugin providing `undo` and `redo` methods, allowing users to implement undo and redo operations by calling these methods.\n\nTo call these methods, you need to first get the plugin instance, which can be obtained through the [getPluginInstance](/en/api/plugin#graphgetplugininstancekey) method:\n\n```javascript\n// Configure the plugin\nconst graph = new Graph({\n plugins: [{ type: 'history', key: 'my-history' }],\n});\n\n// Get the plugin instance\nconst history = graph.getPluginInstance('my-history');\n\n// Call plugin methods\nhistory.undo();\nhistory.redo();\n```\n\n:::warning{title=Note}\nThe `graph.getPluginInstance` method takes the plugin key value as a parameter, so if you need to get the plugin instance, you need to configure the corresponding plugin in the form of an `object` and pass in the `key` value.\n:::\n\nFor more plugin-related APIs, please refer to the [Plugin API Documentation](/en/api/plugin).\n\n## Custom Plugins\n\nWhen built-in plugins cannot meet your needs, you can:\n\n- Inherit and extend existing plugins\n- Create brand new custom plugins\n\nCustom plugins need to be registered before use. For detailed tutorials, please refer to the [Custom Plugin](/en/manual/plugin/custom-plugin) documentation.\n\n```javascript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { MyCustomPlugin } from './my-custom-plugin';\n\n// Register custom plugin\nregister(ExtensionCategory.PLUGIN, 'my-custom-plugin', MyCustomPlugin);\n\n// Use custom plugin\nconst graph = new Graph({\n plugins: ['my-custom-plugin'],\n});\n```\n\nBy reasonably combining and configuring plugins, you can build graph visualization applications with rich features and excellent interactive experiences.\n\n\n\n---\ntitle: 插件总览\norder: 0\n---\n\n## 什么是插件\n\n\n\n插件(Plugin)是 G6 中最灵活的扩展机制,用户可以通过插件来扩展 G6 的功能,例如在画布中额外挂载图形组件、实现撤销重做等功能。\n\n绝大多数的定制需求都可以通过插件来实现,G6 内置了一些常用的插件,例如:[Tooltip](/manual/plugin/tooltip)、[Grid](/manual/plugin/grid-line)、[History](/manual/plugin/history)。\n\n## 内置插件\n\nG6 提供了丰富的内置插件,涵盖多种常见功能场景:\n\n| 分类 | 插件名称 | 注册类型 | 功能描述 |\n| ---------------- | ------------------------------------------------------------------------------------------ | ------------------ | -------------------------------------- |\n| **视觉样式增强** | | | |\n| | [网格线](/manual/plugin/grid-line) | `grid-line` | 在画布上显示网格参考线 |\n| | [背景](/manual/plugin/background) | `background` | 为画布添加背景图片或颜色 |\n| | [水印](/manual/plugin/watermark) | `watermark` | 为画布添加水印,保护版权 |\n| | [轮廓包围](/manual/plugin/hull) | `hull` | 为指定节点集合创建轮廓 |\n| | [气泡集](/manual/plugin/bubble-sets) | `bubble-sets` | 创建平滑气泡状的元素集合轮廓 |\n| | [对齐线](/manual/plugin/snapline) | `snapline` | 拖动元素时显示对齐参考线 |\n| **导航与概览** | | | |\n| | [缩略图](/manual/plugin/minimap) | `minimap` | 显示图的缩略预览,支持导航 |\n| | [全屏](/manual/plugin/fullscreen) | `fullscreen` | 支持图表全屏显示和退出 |\n| | [时间轴](/manual/plugin/timebar) | `timebar` | 提供时序数据的筛选和播放控制 |\n| **交互控件** | | | |\n| | [工具栏](/manual/plugin/toolbar) | `toolbar` | 提供常用操作按钮集合 |\n| | [上下文菜单](/manual/plugin/contextmenu) | `contextmenu` | 右键点击时显示可选操作菜单 |\n| | [提示框](/manual/plugin/tooltip) | `tooltip` | 悬停时显示元素详细信息 |\n| | [图例](/manual/plugin/legend) | `legend` | 显示图表数据的类别和对应样式说明 |\n| **数据探索** | | | |\n| | [鱼眼放大镜](/manual/plugin/fisheye) | `fisheye` | 提供焦点+上下文的探索体验 |\n| | [边过滤镜](/manual/plugin/edge-filter-lens) | `edge-filter-lens` | 在指定区域内筛选显示边 |\n| | [边绑定](/manual/plugin/edge-bundling) | `edge-bundling` | 将相似路径的边捆绑在一起,减少视觉混乱 |\n| **高级功能** | | | |\n| | [历史记录](/manual/plugin/history) | `history` | 支持撤销/重做操作 |\n| | [相机设置](https://github.com/antvis/G6/blob/v5/packages/g6/src/plugins/camera-setting.ts) | `camera-setting` | 配置3D场景下的相机参数 |\n\n各插件的详细配置可参考 [内置插件文档](/manual/plugin/grid-line)。\n\n## 配置方式\n\n### 基本配置\n\n在图实例初始化时,通过 `plugins` 数组指定需要的插件:\n\n```javascript {}5\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n // 其他配置...\n plugins: ['grid', 'minimap', 'tooltip'],\n});\n```\n\n### 配置插件参数\n\n对于需要自定义参数的插件,可以使用 `object` 形式配置属性:\n\n```javascript {5-9}\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n 'grid',\n {\n type: 'tooltip',\n key: 'my-tooltip', // 为插件指定key,便于后续更新\n getContent: (e) => `
节点:${e.target.id}
`,\n },\n ],\n});\n```\n\n### 动态更新插件\n\nG6 支持在图实例运行期间动态管理插件,满足复杂交互需求:\n\n通过 [getPlugins](/api/plugin#graphgetplugins) 方法获取当前插件列表:\n\n```javascript\n// 获取插件列表\nconst plugins = graph.getPlugins();\n// console.log(plugins) 👉 ['minimap', 'grid']\n```\n\n可以通过 [setPlugins](/api/plugin#graphsetpluginsplugins) 方法调整插件:\n\n```javascript\n// 添加新插件\ngraph.setPlugins((plugins) => [...plugins, 'minimap']);\n\n// 移除插件\ngraph.setPlugins((plugins) => plugins.filter((p) => p !== 'grid'));\n```\n\n可以通过 [updatePlugin](/api/plugin#graphupdatepluginplugin) 方法更新插件的配置:\n\n```javascript {6,14}\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'tooltip',\n key: 'my-tooltip',\n getContent: (e) => `
节点:${e.target.id}
`,\n },\n ],\n});\n\n// 更新单个插件\ngraph.updatePlugin({\n key: 'my-tooltip',\n getContent: (e) => `
更新的内容:${e.target.id}
`,\n});\n```\n\n:::warning{title=注意}\n使用 `updatePlugin` 方法时,需要在初始化时为插件指定唯一的 `key`。\n:::\n\n### 卸载插件\n\n使用 [setPlugins](/api/plugin#graphsetpluginsplugins) 方法同样可以卸载插件,将插件配置列表置为空即可:\n\n```javascript\n// 卸载所有插件\ngraph.setPlugins([]);\n```\n\n### 调用插件方法\n\n一些插件提供了可供用户调用的 API 方法,例如 `history` 插件提供了 `undo` 和 `redo` 方法,用户可以通过调用这些方法来实现撤销和重做操作。\n\n要调用这些方法,需要先获取到插件实例,可通过 [getPluginInstance](/api/plugin#graphgetplugininstancekey) 方法获取:\n\n```javascript\n// 配置插件\nconst graph = new Graph({\n plugins: [{ type: 'history', key: 'my-history' }],\n});\n\n// 获取插件实例\nconst history = graph.getPluginInstance('my-history');\n\n// 调用插件方法\nhistory.undo();\nhistory.redo();\n```\n\n:::warning{title=注意}\n`graph.getPluginInstance` 方法接收插件 key 值作为参数,因此如果需要获取插件实例,需要将对应插件配置为 `object` 的形式,并传入 `key` 值。\n:::\n\n更多与插件相关的 API 请参考 [插件 API 文档](/api/plugin)。\n\n## 自定义插件\n\n当内置插件无法满足需求时,你可以:\n\n- 继承和扩展现有插件\n- 创建全新的自定义插件\n\n自定义插件需要先注册后使用。详细教程请参考 [自定义插件](/manual/plugin/custom-plugin) 文档。\n\n```javascript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { MyCustomPlugin } from './my-custom-plugin';\n\n// 注册自定义插件\nregister(ExtensionCategory.PLUGIN, 'my-custom-plugin', MyCustomPlugin);\n\n// 使用自定义插件\nconst graph = new Graph({\n plugins: ['my-custom-plugin'],\n});\n```\n\n通过合理组合和配置插件,你可以构建出功能丰富、交互体验出色的图可视化应用。\n\n\n\n---\ntitle: Snapline\norder: 13\n---\n\n## Overview\n\nThe Snapline plugin provides intelligent alignment guidelines for the canvas, automatically displaying guide lines when moving nodes and supporting automatic snapping. It helps users achieve precise alignment and is an important tool for improving efficiency and accuracy in graphic editing.\n\n## Use Cases\n\nThe Snapline plugin is mainly suitable for the following scenarios:\n\n- When manually adjusting node positions and precise alignment with other nodes is needed\n- When dragging multiple nodes while maintaining their alignment relationships\n- When creating standardized graphic layouts to ensure consistency in node spacing and positioning\n- When improving node layout efficiency through automatic snapping functionality\n\n## Basic Usage\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'snapline',\n key: 'my-snapline', // Specify unique identifier\n tolerance: 5, // Alignment snap threshold\n offset: 20, // Guide line extension distance\n autoSnap: true, // Enable automatic snapping\n },\n ],\n});\n```\n\n## Live Demo\n\n\n\n## Options\n\n| Property | Description | Type | Default | Required |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------- | ----------------------- | -------- |\n| type | Plugin type | string | `'snapline'` | ✓ |\n| key | Plugin unique identifier | string | - | |\n| tolerance | The alignment accuracy, that is, when the distance between the moved node and the target position is less than tolerance, the alignment line is displayed | number | 5 | |\n| offset | The extension distance of the snapline | number | 20 | |\n| autoSnap | Whether to enable automatic snapping | boolean | true | |\n| shape | Specifies which shape on the element to use as the reference shape:
- `'key'`: uses the key shape of the element as the reference shape
- `Function`: receives the element and returns a shape | string \\| ((node: Node) => DisplayObject) | `'key'` | |\n| verticalLineStyle | Vertical snapline style | BaseStyleProps | `{ stroke: '#1783FF' }` | |\n| horizontalLineStyle | Horizontal snapline style | BaseStyleProps | `{ stroke: '#1783FF' }` | |\n| filter | Filter nodes that do not need to participate in alignment | (node: Node) => boolean | `() => true` | |\n\n### shape\n\nThe `shape` property specifies the reference shape for elements and supports the following configurations:\n\n```js\n// Use the key shape as reference\n{\n type: 'snapline',\n shape: 'key'\n}\n\n// Use custom function to return reference shape\n{\n type: 'snapline',\n shape: (node) => {\n return node.getShape('custom-shape');\n }\n}\n```\n\n### Snapline Style Configuration\n\n| Property | Description | Type | Default |\n| -------------- | ----------------------- | ---------------------------------------- | ----------- |\n| stroke | Line color | string \\| Pattern \\| null | `'#1783FF'` |\n| opacity | Overall opacity | number \\| string | 1 |\n| strokeOpacity | Stroke opacity | number \\| string | 1 |\n| lineWidth | Line width | number \\| string | 1 |\n| lineCap | Line end style | `'butt'` \\| `'round'` \\| `'square'` | `'butt'` |\n| lineJoin | Line join style | `'miter'` \\| `'round'` \\| `'bevel'` | `'miter'` |\n| lineDash | Dash line configuration | number \\| string \\| (string \\| number)[] | - |\n| lineDashOffset | Dash line offset | number | 0 |\n| shadowBlur | Shadow blur | number | 0 |\n| shadowColor | Shadow color | string | - |\n| shadowOffsetX | Shadow X offset | number | 0 |\n| shadowOffsetY | Shadow Y offset | number | 0 |\n| cursor | Mouse cursor style | string | `'default'` |\n| zIndex | Rendering level | number | 0 |\n\nExample configuration:\n\n```js\n{\n type: 'snapline',\n horizontalLineStyle: {\n stroke: '#F08F56',\n strokeOpacity: 0.8,\n lineWidth: 2,\n lineDash: [4, 4],\n lineDashOffset: 0,\n opacity: 1,\n cursor: 'move',\n },\n verticalLineStyle: {\n stroke: '#17C76F',\n strokeOpacity: 0.8,\n lineWidth: 2,\n lineDash: [4, 4],\n lineDashOffset: 0,\n opacity: 1,\n cursor: 'move',\n },\n}\n```\n\n## Code Examples\n\n### Basic Snapline\n\nThe simplest usage:\n\n```js\nconst graph = new Graph({\n plugins: ['snapline'],\n});\n```\n\n### Custom Configuration\n\nYou can customize the snapline behavior according to your needs:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'snapline',\n tolerance: 8, // Larger snap range\n offset: 30, // Longer extension lines\n horizontalLineStyle: {\n stroke: '#1890ff',\n lineWidth: 2,\n },\n filter: (node) => node.id !== 'node-0', // Filter nodes by id, exclude from alignment\n },\n ],\n});\n```\n\n## Live Example\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: { type: 'grid' },\n behaviors: ['drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'snapline',\n key: 'snapline',\n verticalLineStyle: { stroke: '#F08F56', lineWidth: 2 },\n horizontalLineStyle: { stroke: '#17C76F', lineWidth: 2 },\n autoSnap: false,\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 对齐线 Snapline\norder: 13\n---\n\n## 概述\n\n对齐线插件为画布提供智能对齐参考线,在移动节点时自动显示辅助线并支持自动吸附,帮助用户实现精确对齐,是图形编辑中提升效率和精确度的重要工具。\n\n## 使用场景\n\n对齐线插件主要适用于以下场景:\n\n- 手动调整节点位置时,需要与其他节点精确对齐\n- 拖拽移动多个节点时,保持它们之间的对齐关系\n- 制作规范的图形布局时,确保节点间距和位置的一致性\n- 通过自动吸附功能提高节点排版效率\n\n## 基本用法\n\n以下是一个简单的 Snapline 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'snapline',\n key: 'my-snapline', // 指定唯一标识符\n tolerance: 5, // 对齐吸附阈值\n offset: 20, // 对齐线延伸距离\n autoSnap: true, // 启用自动吸附\n },\n ],\n});\n```\n\n## 在线体验\n\n\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ---- |\n| type | 插件类型 | string | `'snapline'` | ✓ |\n| key | 插件唯一标识符 | string | - | |\n| tolerance | 对齐精度,即触发对齐的距离阈值 | number | 5 | |\n| offset | 对齐线头尾的延伸距离 | number | 20 | |\n| autoSnap | 是否启用自动吸附 | boolean | true | |\n| shape | 指定元素的参照图形:
- `'key'`: 使用元素的主图形作为参照图形
- `Function`: 传入函数时接收元素对象,返回一个图形 | string \\| ((node: Node) => DisplayObject) | `'key'` | |\n| verticalLineStyle | 垂直对齐线样式 | 支持[BaseStyleProps](/manual/element/shape/properties#baseshapestyle) 下的部分配置,详见[「对齐线样式配置项」](#对齐线样式配置) | `{ stroke: '#1783FF' }` | |\n| horizontalLineStyle | 水平对齐线样式 | 支持[BaseStyleProps](/manual/element/shape/properties#baseshapestyle) 下的部分配置,详见[「对齐线样式配置项」](#对齐线样式配置) | `{ stroke: '#1783FF' }` | |\n| filter | 过滤不需要参与对齐的节点 | (node: Node) => boolean | `() => true` | |\n\n### shape\n\n`shape` 属性用于指定元素的参照图形,支持以下配置方式:\n\n```js\n// 使用主图形作为参照\n{\n type: 'snapline',\n shape: 'key'\n}\n\n// 使用自定义函数返回参照图形\n{\n type: 'snapline',\n shape: (node) => {\n return node.getShape('custom-shape');\n }\n}\n```\n\n### 对齐线样式配置\n\n| 配置项 | 说明 | 类型 | 默认值 |\n| -------------- | --------------- | ---------------------------------------- | ----------- |\n| stroke | 线条颜色 | string \\| Pattern \\| null | `'#1783FF'` |\n| opacity | 整体透明度 | number \\| string | 1 |\n| strokeOpacity | 描边透明度 | number \\| string | 1 |\n| lineWidth | 线宽度 | number \\| string | 1 |\n| lineCap | 线段端点样式 | `'butt'` \\| `'round'` \\| `'square'` | `'butt'` |\n| lineJoin | 线段连接处样式 | `'miter'` \\| `'round'` \\| `'bevel'` | `'miter'` |\n| lineDash | 虚线配置 | number \\| string \\| (string \\| number)[] | - |\n| lineDashOffset | 虚线偏移量 | number | 0 |\n| shadowBlur | 阴影模糊程度 | number | 0 |\n| shadowColor | 阴影颜色 | string | - |\n| shadowOffsetX | 阴影 X 方向偏移 | number | 0 |\n| shadowOffsetY | 阴影 Y 方向偏移 | number | 0 |\n| cursor | 鼠标样式 | string | `'default'` |\n| zIndex | 渲染层级 | number | 0 |\n\n示例配置\n\n```js\n{\n type: 'snapline',\n horizontalLineStyle: {\n stroke: '#F08F56',\n strokeOpacity: 0.8,\n lineWidth: 2,\n lineDash: [4, 4],\n lineDashOffset: 0,\n opacity: 1,\n cursor: 'move',\n },\n verticalLineStyle: {\n stroke: '#17C76F',\n strokeOpacity: 0.8,\n lineWidth: 2,\n lineDash: [4, 4],\n lineDashOffset: 0,\n opacity: 1,\n cursor: 'move',\n },\n}\n```\n\n## 代码示例\n\n### 基础对齐线\n\n最简单的使用方式:\n\n```js\nconst graph = new Graph({\n plugins: ['snapline'],\n});\n```\n\n### 自定义配置\n\n可以根据需要自定义对齐线的行为:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'snapline',\n tolerance: 8, // 更大的吸附范围\n offset: 30, // 更长的延伸线\n horizontalLineStyle: {\n stroke: '#1890ff',\n lineWidth: 2,\n },\n filter: (node) => node.id !== 'node-0', // 根据id过滤节点,不参与对齐\n },\n ],\n});\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: { type: 'grid' },\n behaviors: ['drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'snapline',\n key: 'snapline',\n verticalLineStyle: { stroke: '#F08F56', lineWidth: 2 },\n horizontalLineStyle: { stroke: '#17C76F', lineWidth: 2 },\n autoSnap: false,\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Timebar\norder: 14\n---\n\n## Overview\n\nThe Timebar plugin is an important tool for exploring time-series data. It can display the time distribution of data in the form of a timeline or trend chart, and supports interactions such as time interval filtering and dynamic playback, helping users better understand the changes in data over time.\n\n## Use Cases\n\n- Need to display and analyze the time distribution of time-series data\n- Need to filter and explore graph data through the time dimension\n- Need to dynamically display the process of data changing over time\n\n## Basic Usage\n\nBelow is a simple example of initializing the Timebar plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'timebar',\n data: timeData, // Time data\n width: 450, // Timebar width\n height: 60, // Timebar height\n position: 'bottom', // Position\n loop: false, // Whether to loop playback\n },\n ],\n});\n```\n\n## Online Experience\n\n\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------------- | -------- |\n| type | Plugin type | string | `timebar` | ✓ |\n| key | Unique identifier for the plugin, can be used to get the plugin instance or update plugin options | string | - | |\n| className | Additional class name for the toolbar DOM | string | `g6-timebar` | |\n| x | X position (position will be invalid if set) | number | - | |\n| y | Y position (position will be invalid if set) | number | - | |\n| width | Timebar width | number | 450 | |\n| height | Timebar height | number | 60 | |\n| position | Timebar position | `bottom` \\| `top` | `bottom` | |\n| padding | Padding | number \\| number[] | 10 | |\n| data | Time data | number[] \\| { time: number; value: number }[] | - | ✓ |\n| timebarType | Timebar display type | `time` \\| `chart` | `time` | |\n| elementTypes | Filter element types | (`node` \\| `edge` \\| `combo`)[] | [`node`] | |\n| mode | Control element filtering method, supports the following two configurations:
- `modify`: filter by modifying graph data
- `visibility`: filter by modifying element visibility | `modify` \\| `visibility` | `modify` | |\n| values | Current time value | number \\| [number, number] \\| Date \\| [Date, Date] | - | |\n| loop | Whether to loop playback | boolean | false | |\n| getTime | Method to get element time | (datum: ElementDatum) => number | - | |\n| labelFormatter | Custom time formatting in chart mode | (time: number \\| Date) => string | - | |\n| onChange | Callback when the time interval changes | (values: number \\| [number, number]) => void | - | |\n| onReset | Callback when reset | () => void | - | |\n| onSpeedChange | Callback when playback speed changes | (speed: number) => void | - | |\n| onPlay | Callback when playback starts | () => void | - | |\n| onPause | Callback when paused | () => void | - | |\n| onBackward | Callback when moving backward | () => void | - | |\n| onForward | Callback when moving forward | () => void | - | |\n\n### timebarType\n\nThe `timebarType` property is used to control the display type of the timebar, supporting the following two configurations:\n\n- `time`: Displayed as a timeline, refer to [Time Mode Example](/examples/plugin/timebar/#timer)\n- `chart`: Displayed as a trend chart, at this time the `data` configuration item under `timebar` needs to pass an additional `value` field as chart data, refer to [Chart Mode Example](/examples/plugin/timebar/#chart)\n\n## Code Examples\n\n### Basic Usage\n\nThe simplest configuration method:\n\n```js\nconst graph = new Graph({\n layout: { type: 'grid', cols: 5 },\n plugins: [\n {\n type: 'timebar',\n data: [\n {\n time: new Date('2023-08-01').getTime(),\n value: 5,\n },\n {\n time: new Date('2023-08-02').getTime(),\n value: 10,\n },\n {\n time: new Date('2023-08-03').getTime(),\n value: 15,\n },\n ],\n },\n ],\n data: {\n nodes: [\n {\n id: 'node1',\n label: 'Node 1',\n // By default, elementTypes=['node'], so nodes need to set data.timestamp to display sequentially according to the timeline\n data: {\n timestamp: new Date('2023-08-01').getTime(),\n },\n },\n {\n id: 'node2',\n label: 'Node 2',\n data: {\n timestamp: new Date('2023-08-02').getTime(),\n },\n },\n {\n id: 'node3',\n label: 'Node 3',\n data: {\n timestamp: new Date('2023-08-03').getTime(),\n },\n },\n ],\n edges: [\n {\n id: 'edge1',\n source: 'node1',\n target: 'node2',\n // Scenario 1: By default, elementTypes = ['node']\n // - Edges do not need to set data.timestamp, the display/hide of edges depends entirely on whether the two connected nodes are visible\n\n // Scenario 2: If elementTypes includes 'edge', for example, elementTypes = ['node', 'edge']\n // - At this time, edges must set data.timestamp, and the display of edges is controlled by it\n // data: {\n // timestamp: new Date('2023-08-01').getTime()\n // }\n },\n {\n id: 'edge2',\n source: 'node2',\n target: 'node3',\n },\n {\n id: 'edge3',\n source: 'node3',\n target: 'node1',\n },\n ],\n },\n});\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n width: 600,\n height: 400,\n layout: { type: 'grid', cols: 5 },\n plugins: [\n {\n type: 'timebar',\n data: [\n {\n time: new Date('2023-08-01').getTime(),\n value: 5,\n },\n {\n time: new Date('2023-08-02').getTime(),\n value: 10,\n },\n {\n time: new Date('2023-08-03').getTime(),\n value: 15,\n },\n ],\n },\n ],\n data: {\n nodes: [\n {\n id: 'node1',\n label: 'Node 1',\n data: {\n timestamp: new Date('2023-08-01').getTime(),\n },\n },\n {\n id: 'node2',\n label: 'Node 2',\n data: {\n timestamp: new Date('2023-08-02').getTime(),\n },\n },\n {\n id: 'node3',\n label: 'Node 3',\n data: {\n timestamp: new Date('2023-08-03').getTime(),\n },\n },\n ],\n edges: [\n {\n id: 'edge1',\n source: 'node1',\n target: 'node2',\n },\n {\n id: 'edge2',\n source: 'node2',\n target: 'node3',\n },\n {\n id: 'edge3',\n source: 'node3',\n target: 'node1',\n },\n ],\n },\n});\n\ngraph.render();\n```\n\n### Custom Styles\n\n`width`, `height`, `padding`, `className` can customize the display effect of the timebar, but note that `className` only acts on the outer DOM container and cannot affect the internal Canvas rendering content of the timebar (timeline, chart, play button, etc.).\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'timebar',\n className: 'custom-timebar', // Note: Since the content is Canvas rendered, CSS styles cannot affect the internal content of the timebar\n width: 400, // Set timebar width\n height: 80, // Set timebar height\n padding: [20, 20, 10, 20], // Set padding [top, right, bottom, left]\n position: 'bottom', // Keep position at the bottom\n data: timeData,\n // labelFormatter: (time) => {\n // return new Date(time).toLocaleDateString();\n // }\n },\n ],\n});\n```\n\nCSS can only set the style of the timebar container:\n\n```css\n.custom-timebar {\n background-color: #f0f0f0;\n border: 1px solid #ccc;\n border-radius: 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n```\n\nThe effect is as follows:\n\n```js | ob { pin: false }\ncreateGraph(\n {\n data: () => {\n return {\n nodes: [\n {\n id: 'node1',\n style: { x: 100, y: 100, label: 'Node 1' },\n data: {\n timestamp: new Date('2023-08-01').getTime(),\n },\n },\n {\n id: 'node2',\n style: { x: 200, y: 100, label: 'Node 2' },\n data: {\n timestamp: new Date('2023-08-01').getTime() + 3600 * 24 * 1000,\n },\n },\n {\n id: 'node3',\n style: { x: 150, y: 200, label: 'Node 3' },\n data: {\n timestamp: new Date('2023-08-01').getTime() + 3600 * 24 * 1000 * 2,\n },\n },\n ],\n edges: [\n { id: 'edge1', source: 'node1', target: 'node2' },\n { id: 'edge2', source: 'node2', target: 'node3' },\n { id: 'edge3', source: 'node3', target: 'node1' },\n ],\n };\n },\n node: {\n style: {\n size: 20,\n label: true,\n },\n },\n edge: {\n style: {\n stroke: '#91d5ff',\n lineWidth: 1,\n },\n },\n plugins: [\n {\n type: 'timebar',\n className: 'custom-timebar',\n width: 400,\n height: 80,\n padding: [20, 20, 10, 20],\n position: 'bottom',\n data: [\n {\n time: new Date('2023-08-01').getTime(),\n value: 5,\n },\n {\n time: new Date('2023-08-01').getTime() + 3600 * 24 * 1000,\n value: 10,\n },\n {\n time: new Date('2023-08-01').getTime() + 3600 * 24 * 1000 * 2,\n value: 15,\n },\n ],\n labelFormatter: (time) => {\n return new Date(time).toLocaleDateString();\n },\n },\n ],\n },\n { width: 600, height: 400 },\n (gui, graph) => {\n gui?.hide();\n const style = document.createElement('style');\n style.innerHTML = `\n .custom-timebar {\n background-color: #f0f0f0;\n border: 1px solid #ccc;\n border-radius: 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n }\n `;\n document.head.appendChild(style);\n },\n);\n```\n\n## Real Cases\n\n- [Time Mode](/examples/plugin/timebar/#timer)\n- [Chart Mode](/examples/plugin/timebar/#chart)\n\n\n\n---\ntitle: 时间条 Timebar\norder: 14\n---\n\n## 概述\n\n时间条插件是一个用于时序数据探索的重要工具,它能够通过时间轴或趋势图的形式展示数据的时间分布,并支持时间区间筛选、动态播放等交互方式,帮助用户更好地理解数据随时间的变化。\n\n## 使用场景\n\n- 需要展示和分析时序数据的时间分布\n- 需要通过时间维度筛选和探索图数据\n- 需要动态展示数据随时间变化的过程\n\n## 基本用法\n\n以下是一个简单的 Timebar 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'timebar',\n data: timeData, // 时间数据\n width: 450, // 时间条宽度\n height: 60, // 时间条高度\n position: 'bottom', // 位置\n loop: false, // 是否循环播放\n },\n ],\n});\n```\n\n## 在线体验\n\n\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------- | ------------ | ---- |\n| type | 插件类型 | string | `timebar` | ✓ |\n| key | 插件的唯一标识,可用于获取插件实例或更新插件选项 | string | - | |\n| className | 给工具栏的 DOM 追加的类名 | string | `g6-timebar` | |\n| x | X 位置(设置后 position 会失效) | number | - | |\n| y | Y 位置(设置后 position 会失效) | number | - | |\n| width | 时间条宽度 | number | 450 | |\n| height | 时间条高度 | number | 60 | |\n| position | 时间条位置 | `bottom` \\| `top` | `bottom` | |\n| padding | 边距 | number \\| number[] | 10 | |\n| data | 时间数据 | number[] \\| { time: number; value: number }[] | - | ✓ |\n| timebarType | 时间条展示类型 | `time` \\| `chart` | `time` | |\n| elementTypes | 筛选元素类型 | (`node` \\| `edge` \\| `combo`)[] | [`node`] | |\n| mode | 控制元素的筛选方式,支持以下两种配置:
- `modify`:通过修改图数据进行筛选
- `visibility`:通过修改元素可见性进行筛选 | `modify` \\| `visibility` | `modify` | |\n| values | 当前时间值 | number \\| [number, number] \\| Date \\| [Date, Date] | - | |\n| loop | 是否循环播放 | boolean | false | |\n| getTime | 获取元素时间的方法 | (datum: ElementDatum) => number | - | |\n| labelFormatter | 图表模式下自定义时间格式化 | (time: number \\| Date) => string | - | |\n| onChange | 时间区间变化时的回调 | (values: number \\| [number, number]) => void | - | |\n| onReset | 重置时的回调 | () => void | - | |\n| onSpeedChange | 播放速度变化时的回调 | (speed: number) => void | - | |\n| onPlay | 开始播放时的回调 | () => void | - | |\n| onPause | 暂停时的回调 | () => void | - | |\n| onBackward | 后退时的回调 | () => void | - | |\n| onForward | 前进时的回调 | () => void | - | |\n\n### timebarType\n\n`timebarType` 属性用于控制时间条的展示类型,支持以下两种配置:\n\n- `time`:显示为时间轴形式,参考 [时间模式示例](/examples/plugin/timebar/#timer)\n- `chart`:显示为趋势图形式,此时`timebar`下的`data`配置项,每个数组项需要额外传入 `value` 字段作为图表数据,参考 [图表模式示例](/examples/plugin/timebar/#chart)\n\n## 代码示例\n\n### 基础用法\n\n最简单的配置方式:\n\n```js\nconst graph = new Graph({\n layout: { type: 'grid', cols: 5 },\n plugins: [\n {\n type: 'timebar',\n data: [\n {\n time: new Date('2023-08-01').getTime(),\n value: 5,\n },\n {\n time: new Date('2023-08-02').getTime(),\n value: 10,\n },\n {\n time: new Date('2023-08-03').getTime(),\n value: 15,\n },\n ],\n },\n ],\n data: {\n nodes: [\n {\n id: 'node1',\n label: '节点1',\n // 默认情况下 elementTypes=['node'],所以节点需要设置 data.timestamp,才能按照时间轴内的时间依次展示\n data: {\n timestamp: new Date('2023-08-01').getTime(),\n },\n },\n {\n id: 'node2',\n label: '节点2',\n data: {\n timestamp: new Date('2023-08-02').getTime(),\n },\n },\n {\n id: 'node3',\n label: '节点3',\n data: {\n timestamp: new Date('2023-08-03').getTime(),\n },\n },\n ],\n edges: [\n {\n id: 'edge1',\n source: 'node1',\n target: 'node2',\n // 场景一:默认情况 elementTypes = ['node']\n // - 边不需要设置 data.timestamp,边的显示/隐藏完全取决于其连接的两个节点是否可见\n\n // 场景二:如果elementTypes包含了'edge',比如 elementTypes = ['node', 'edge']\n // - 此时必须为边设置 data.timestamp,边的显示受其控制\n // data: {\n // timestamp: new Date('2023-08-01').getTime()\n // }\n },\n {\n id: 'edge2',\n source: 'node2',\n target: 'node3',\n },\n {\n id: 'edge3',\n source: 'node3',\n target: 'node1',\n },\n ],\n },\n});\n```\n\n效果如下:\n\n```js | ob { pin: false, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n width: 600,\n height: 400,\n width: 600,\n height: 400,\n layout: { type: 'grid', cols: 5 },\n plugins: [\n {\n type: 'timebar',\n data: [\n {\n time: new Date('2023-08-01').getTime(),\n value: 5,\n },\n {\n time: new Date('2023-08-02').getTime(),\n value: 10,\n },\n {\n time: new Date('2023-08-03').getTime(),\n value: 15,\n },\n ],\n },\n ],\n data: {\n nodes: [\n {\n id: 'node1',\n label: '节点1',\n data: {\n timestamp: new Date('2023-08-01').getTime(),\n },\n },\n {\n id: 'node2',\n label: '节点2',\n data: {\n timestamp: new Date('2023-08-02').getTime(),\n },\n },\n {\n id: 'node3',\n label: '节点3',\n data: {\n timestamp: new Date('2023-08-03').getTime(),\n },\n },\n ],\n edges: [\n {\n id: 'edge1',\n source: 'node1',\n target: 'node2',\n },\n {\n id: 'edge2',\n source: 'node2',\n target: 'node3',\n },\n {\n id: 'edge3',\n source: 'node3',\n target: 'node1',\n },\n ],\n },\n});\n\ngraph.render();\n```\n\n### 自定义样式\n\n`width`、`height`、`padding`、`className` 可自定义时间条的展示效果,但需要注意 `className` 仅作用于外层 DOM 容器,无法影响时间条内部的 Canvas 渲染内容(时间轴、图表、播放按钮等)。\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'timebar',\n className: 'custom-timebar', // 注意:由于内容是 Canvas 渲染,CSS 样式无法影响到时间条的内部内容\n width: 400, // 设置时间条宽度\n height: 80, // 设置时间条高度\n padding: [20, 20, 10, 20], // 设置内边距 [上, 右, 下, 左]\n position: 'bottom', // 位置保持在底部\n data: timeData,\n // labelFormatter: (time) => {\n // return new Date(time).toLocaleDateString();\n // }\n },\n ],\n});\n```\n\n通过 CSS 只能设置时间条容器的样式:\n\n```css\n.custom-timebar {\n background-color: #f0f0f0;\n border: 1px solid #ccc;\n border-radius: 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n```\n\n效果如下:\n\n```js | ob { pin: false }\ncreateGraph(\n {\n data: () => {\n return {\n nodes: [\n {\n id: 'node1',\n style: { x: 100, y: 100, label: 'Node 1' },\n data: {\n timestamp: new Date('2023-08-01').getTime(),\n },\n },\n {\n id: 'node2',\n style: { x: 200, y: 100, label: 'Node 2' },\n data: {\n timestamp: new Date('2023-08-01').getTime() + 3600 * 24 * 1000,\n },\n },\n {\n id: 'node3',\n style: { x: 150, y: 200, label: 'Node 3' },\n data: {\n timestamp: new Date('2023-08-01').getTime() + 3600 * 24 * 1000 * 2,\n },\n },\n ],\n edges: [\n { id: 'edge1', source: 'node1', target: 'node2' },\n { id: 'edge2', source: 'node2', target: 'node3' },\n { id: 'edge3', source: 'node3', target: 'node1' },\n ],\n };\n },\n node: {\n style: {\n size: 20,\n label: true,\n },\n },\n edge: {\n style: {\n stroke: '#91d5ff',\n lineWidth: 1,\n },\n },\n plugins: [\n {\n type: 'timebar',\n className: 'custom-timebar',\n width: 400,\n height: 80,\n padding: [20, 20, 10, 20],\n position: 'bottom',\n data: [\n {\n time: new Date('2023-08-01').getTime(),\n value: 5,\n },\n {\n time: new Date('2023-08-01').getTime() + 3600 * 24 * 1000,\n value: 10,\n },\n {\n time: new Date('2023-08-01').getTime() + 3600 * 24 * 1000 * 2,\n value: 15,\n },\n ],\n labelFormatter: (time) => {\n return new Date(time).toLocaleDateString();\n },\n },\n ],\n },\n { width: 600, height: 400 },\n (gui, graph) => {\n gui?.hide();\n const style = document.createElement('style');\n style.innerHTML = `\n .custom-timebar {\n background-color: #f0f0f0;\n border: 1px solid #ccc;\n border-radius: 5px;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n }\n `;\n document.head.appendChild(style);\n },\n);\n```\n\n## 实际案例\n\n- [时间模式](/examples/plugin/timebar/#timer)\n- [图表模式](/examples/plugin/timebar/#chart)\n\n\n\n---\ntitle: Title\norder: 15\n---\n\n## Overview\n\nTitle indicates the name of the image and conveys the brief content of the image.\n\n## Basic Usage\n\nThe following is a simple example of initializing the Title plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n key: 'title',\n type: 'title',\n title: 'This is a title',\n subTitle: 'This is a subtitle',\n },\n ],\n});\n```\n\n## Configuration Options\n\n| Option | Description | Type | Default | Required |\n| --------- | ------------------------------------------------------------- | ----------------------------- | ------------ | -------- |\n| type | Plugin type | string | `title` | ✓ |\n| key | Unique identifier for the plugin, used for subsequent updates | string | - | |\n| title | title content [style config](#title) | `TitleStyle` | - | ✓ |\n| subtitle | subtitle content [style config](#subtitle) | `SubTitleStyle` | - | |\n| spacing | Vertical spacing between main title and subtitle | number | 8 | |\n| className | Class name of the title canvas | string | - | |\n| align | Graph title alignment | `left` \\| `center` \\| `right` | `left` | |\n| size | Height of the title plugin | number | 44 | |\n| padding | Padding | number \\| number[] | [16,24,0,24] | |\n\n### size\n\n _number_ **optional** \n\nUsed to configure the space height of the title plugin. Default is `44`。\n\n### align\n\n _string_ **optional** \n\nUsed to configure the horizontal alignment of the title plugin. Default is `left`. You can choose `left`, `center`, or `right`, representing left-aligned, center-aligned, and right-aligned respectively.\n\n```js | ob { pin: false }\ncreateGraph(\n {\n data: { nodes: Array.from({ length: 12 }).map((_, i) => ({ id: `node${i}` })) },\n node: {\n palette: 'spectral',\n style: { labelText: 'Ciallo' },\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n plugins: [\n {\n key: 'title',\n type: 'title',\n title: 'This is a title This is a title',\n subtitle: 'This is a sub-',\n },\n ],\n layout: { type: 'circular' },\n autoFit: 'view',\n },\n { width: 600, height: 300 },\n (gui, graph) => {\n const options = { align: 'left' };\n const optionFolder = gui.addFolder('Align Options');\n optionFolder.add(options, 'align', ['left', 'center', 'right']);\n optionFolder.onChange(({ property, value }) => {\n graph.updatePlugin({\n key: 'title',\n [property]: value,\n });\n graph.render();\n });\n },\n);\n```\n\n### spacing\n\n _number_ **optional** \n\nUsed to configure the spacing between the chart main title and subtitle. Default is `8`. Appropriate spacing can make the chart look more harmonious overall.\n\n### title\n\nThe title, specifically the main title, can be customized with the following configurations for various title styles.\n\n| Attr | Desc | Type | Default |\n| ------------------ | -------------------------------------------------- | ---------- | --------------------- |\n| title | Title text content | `string` | - |\n| titleFontSize | Title text size | `number` | 16 |\n| titleFontFamily | Title text font | `string` | system-ui, sans-serif |\n| titleFontWeight | Title font weight | `number` | bold |\n| titleLineHeight | Title text line height | `number` | 16 |\n| titleTextAlign | Horizontal alignment of content in title text line | `string` | left |\n| titleTextBaseline | Vertical baseline of title text | `string` | top |\n| titleFill | Fill color of title text | `string` | #1D2129 |\n| titleFillOpacity | Fill transparency of title text | `number` | 0.9 |\n| titleStroke | Stroke color of title text | `string` | transparent |\n| titleStrokeOpacity | Stroke transparency of title text | `number` | 1 |\n| titleLineWidth | Stroke width of title text | `number` | 0 |\n| titleLineDash | Dash style of title text | `number[]` | [] |\n| titleOpacity | Overall transparency of title text | `number` | 1 |\n| titleShadowColor | Shadow color of title text | `string` | transparent |\n| titleShadowBlur | Gaussian blur coefficient of title text shadow | `number` | 0 |\n| titleShadowOffsetX | Horizontal offset of title text shadow | `number` | 0 |\n| titleShadowOffsetY | Vertical offset of title text shadow | `number` | 0 |\n| titleCursor | Mouse style of title text | `string` | default |\n| titleDx | Horizontal offset of title text | `number` | 0 |\n| titleDy | Vertical offset of title text | `number` | 0 |\n\n### subtitle\n\nThe subtitle, which can be customized with the following configurations for various subtitle styles.\n\n| Attr | Desc | Type | Default |\n| --------------------- | ----------------------------------------------- | ---------- | --------------------- |\n| subtitle | Subtitle text content | `string` | - |\n| subtitleFontSize | Subtitle text size | `number` | 12 |\n| subtitleFontFamily | Subtitle text font | `string` | system-ui, sans-serif |\n| subtitleFontWeight | Subtitle font weight | `number` | normal |\n| subtitleLineHeight | Subtitle text line height | `number` | 12 |\n| subtitleTextAlign | Subtitle text line content horizontal alignment | `string` | left |\n| subtitleTextBaseline | Subtitle text vertical baseline | `string` | top |\n| subtitleFill | Subtitle text fill color | `string` | #1D2129 |\n| subtitleFillOpacity | Subtitle text fill transparency | `number` | 0.65 |\n| subtitleStroke | Subtitle text stroke color | `string` | transparent |\n| subtitleStrokeOpacity | Subtitle text stroke transparency | `number` | 1 |\n| subtitleLineWidth | Subtitle text stroke width | `number` | 0 |\n| subtitleLineDash | Subtitle text dashed line style | `number[]` | [] |\n| subtitleOpacity | Subtitle text overall transparency | `number` | 1 |\n| subtitleShadowColor | Subtitle text shadow color | `string` | transparent |\n| subtitleShadowBlur | Subtitle text shadow Gaussian blur coefficient | `number` | 0 |\n| subtitleShadowOffsetX | Subtitle text shadow horizontal offset | `number` | 0 |\n| subtitleShadowOffsetY | Subtitle text shadow vertical offset | `number` | 0 |\n| subtitleCursor | Subtitle text mouse style | `string` | default |\n| subtitleDx | Subtitle text horizontal offset | `number` | 0 |\n| subtitleDy | Subtitle text vertical offset | `number` | 0 |\n\n## Try it\n\nFeel free to modify this example and try different configurations\n\n```js | ob { pin: true, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: { nodes: Array.from({ length: 12 }).map((_, i) => ({ id: `node${i}` })) },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n plugins: [\n {\n key: 'title',\n type: 'title',\n\n align: 'center', // Alignment of title\n spacing: 4, // Spacing between main title and subtitle\n size: 60, // Height of title, default is 44\n\n // title\n title: 'This is a title This is a title', // Title text\n titleFontSize: 28, // Main title font size\n titleFontFamily: 'sans-serif', // Main title font\n titleFontWeight: 600, // Main title font weight\n titleFill: '#fff', // Main title text color\n titleFillOpacity: 1, // Main title text transparency\n titleStroke: '#000', // Main title text stroke color\n titleLineWidth: 2, // Main title text stroke line width\n titleStrokeOpacity: 1, // Main title text stroke transparency\n\n // subtitle\n subtitle: 'This is a sub-', // Subtitle text\n subtitleFontSize: 16, // Subtitle font size\n subtitleFontFamily: 'Arial', // Subtitle font\n subtitleFontWeight: 300, // Subtitle font weight\n subtitleFill: '#2989FF', // Subtitle text color\n subtitleFillOpacity: 1, // Subtitle text transparency\n subtitleStroke: '#000', // Subtitle text stroke color\n subtitleLineWidth: 1, // Subtitle text stroke line width\n subtitleStrokeOpacity: 0.5, // Subtitle text stroke transparency\n },\n ],\n node: {\n palette: 'spectral',\n style: { labelText: 'Ciallo' },\n },\n layout: {\n type: 'circular',\n },\n autoFit: 'view',\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 标题 Title\norder: 15\n---\n\n## 概述\n\nTitle(标题)表明了这张图的名称,传达图的简略内容\n\n## 基本用法\n\n以下是一个简单的 Title 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n key: 'title',\n type: 'title',\n title: '这是一个标题',\n subTitle: '这是一个副标题',\n },\n ],\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------- | -------------------------------- | ----------------------------- | ------------ | ---- |\n| type | 插件类型 | string | `title` | ✓ |\n| key | 插件唯一标识符,用于后续更新 | string | - | |\n| title | 标题内容 [样式配置](#title) | `TitleStyle` | - | ✓ |\n| subtitle | 副标题内容 [样式配置](#subtitle) | `SubTitleStyle` | - | |\n| spacing | 主标题、副标题之间的上下间距 | number | 8 | |\n| className | 标题画布类名 | string | - | |\n| align | 标题相对于画布的位置 | `left` \\| `center` \\| `right` | `left` | |\n| size | 整个标题插件的高度 | number | 44 | |\n| padding | 内边距 | number \\| number[] | [16,24,0,24] | |\n\n### size\n\n _number_ **optional** \n\n用于配置标题的空间高度大小,默认为 `44`。\n\n### align\n\n _string_ **optional** \n\n用于配置标题的的左右对齐方式,默认为 `left`,可以选择使用 `left`,`center`,`right`,分别代表着居左对齐、居中对齐、居右对齐。\n\n```js | ob { pin: false }\ncreateGraph(\n {\n data: { nodes: Array.from({ length: 12 }).map((_, i) => ({ id: `node${i}` })) },\n node: {\n palette: 'spectral',\n style: { labelText: '你好' },\n },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n plugins: [\n {\n key: 'title',\n type: 'title',\n title: '这是一个标题这是一个标题',\n subtitle: '这是一个副标',\n },\n ],\n layout: { type: 'circular' },\n autoFit: 'view',\n },\n { width: 600, height: 300 },\n (gui, graph) => {\n const options = { align: 'left' };\n const optionFolder = gui.addFolder('Align Options');\n optionFolder.add(options, 'align', ['left', 'center', 'right']);\n optionFolder.onChange(({ property, value }) => {\n graph.updatePlugin({\n key: 'title',\n [property]: value,\n });\n graph.render();\n });\n },\n);\n```\n\n### spacing\n\n _number_ **optional** \n\n用于配置主标题和副标题之间的间距,默认为 `8`,合适的间距,可以让看起来整体更协调。\n\n### title\n\n的标题,具体来说是主标题,可以用以下的配置来定制标题的各种样式。\n\n| 属性 | 描述 | 类型 | 默认值 |\n| ------------------ | ------------------------------ | ---------- | --------------------- |\n| title | 标题文字内容 | `string` | - |\n| titleFontSize | 标题文字大小 | `number` | 16 |\n| titleFontFamily | 标题文字字体 | `string` | system-ui, sans-serif |\n| titleFontWeight | 标题字体粗细 | `number` | bold |\n| titleLineHeight | 标题文字的行高 | `number` | 16 |\n| titleTextAlign | 标题文字行内内容的水平对齐方式 | `string` | left |\n| titleTextBaseline | 标题文字垂直方向的基线 | `string` | top |\n| titleFill | 标题文字的填充色 | `string` | #1D2129 |\n| titleFillOpacity | 标题文字的填充透明度 | `number` | 0.9 |\n| titleStroke | 标题文字的描边颜色 | `string` | transparent |\n| titleStrokeOpacity | 标题文字的描边透明度 | `number` | 1 |\n| titleLineWidth | 标题文字描边宽度 | `number` | 0 |\n| titleLineDash | 标题文字虚线样式 | `number[]` | [] |\n| titleOpacity | 标题文字整体透明度 | `number` | 1 |\n| titleShadowColor | 标题文字阴影颜色 | `string` | transparent |\n| titleShadowBlur | 标题文字阴影的高斯模糊系数 | `number` | 0 |\n| titleShadowOffsetX | 标题文字阴影水平偏移量 | `number` | 0 |\n| titleShadowOffsetY | 标题文字阴影垂直偏移量 | `number` | 0 |\n| titleCursor | 标题文字鼠标样式 | `string` | default |\n| titleDx | 标题文字在水平方向的偏移量 | `number` | 0 |\n| titleDy | 标题文字在垂直方向的偏移量 | `number` | 0 |\n\n### subtitle\n\n的副标题,可以用以下的配置来定制副标题的各种样式。\n\n| 属性 | 描述 | 类型 | 默认值 |\n| --------------------- | -------------------------------- | ---------- | --------------------- |\n| subtitle | 副标题文字内容 | `string` | - |\n| subtitleFontSize | 副标题文字大小 | `number` | 12 |\n| subtitleFontFamily | 副标题文字字体 | `string` | system-ui, sans-serif |\n| subtitleFontWeight | 副标题字体粗细 | `number` | normal |\n| subtitleLineHeight | 副标题文字的行高 | `number` | 12 |\n| subtitleTextAlign | 副标题文字行内内容的水平对齐方式 | `string` | left |\n| subtitleTextBaseline | 副标题文字垂直方向的基线 | `string` | top |\n| subtitleFill | 副标题文字的填充色 | `string` | #1D2129 |\n| subtitleFillOpacity | 副标题文字的填充透明度 | `number` | 0.65 |\n| subtitleStroke | 副标题文字的描边颜色 | `string` | transparent |\n| subtitleStrokeOpacity | 副标题文字的描边透明度 | `number` | 1 |\n| subtitleLineWidth | 副标题文字描边宽度 | `number` | 0 |\n| subtitleLineDash | 副标题文字虚线样式 | `number[]` | [] |\n| subtitleOpacity | 副标题文字整体透明度 | `number` | 1 |\n| subtitleShadowColor | 副标题文字阴影颜色 | `string` | transparent |\n| subtitleShadowBlur | 副标题文字阴影的高斯模糊系数 | `number` | 0 |\n| subtitleShadowOffsetX | 副标题文字阴影水平偏移量 | `number` | 0 |\n| subtitleShadowOffsetY | 副标题文字阴影垂直偏移量 | `number` | 0 |\n| subtitleCursor | 副标题文字鼠标样式 | `string` | default |\n| subtitleDx | 副标题文字在水平方向的偏移量 | `number` | 0 |\n| subtitleDy | 副标题文字在垂直方向的偏移量 | `number` | 0 |\n\n## 尝试一下\n\n你可以在这个例子里随意修改并尝试不同的配置\n\n```js | ob { pin: true, inject: true }\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n data: { nodes: Array.from({ length: 12 }).map((_, i) => ({ id: `node${i}` })) },\n behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],\n plugins: [\n {\n key: 'title',\n type: 'title',\n\n align: 'center', // 标题的对齐方式\n spacing: 4, // 主标题和副标题之间的间距\n size: 60, // 标题的高度,默认为 44\n\n // 标题\n title: '这是一个标题这是一个标题', // 标题的文本\n titleFontSize: 28, // 主标题的字体大小\n titleFontFamily: 'sans-serif', // 主标题的字体\n titleFontWeight: 600, // 主标题的字体粗细\n titleFill: '#fff', // 主标题的文字颜色\n titleFillOpacity: 1, // 主标题的文字透明度\n titleStroke: '#000', // 主标题的文字描边颜色\n titleLineWidth: 2, // 主标题的文字描边线宽\n titleStrokeOpacity: 1, // 主标题的文字描边透明度\n\n // 副标题\n subtitle: '这是一个副标', // 副标题的文本\n subtitleFontSize: 16, // 副标题的字体大小\n subtitleFontFamily: 'Arial', // 副标题的字体\n subtitleFontWeight: 300, // 副标题的字体粗细\n subtitleFill: '#2989FF', // 副标题的文字颜色\n subtitleFillOpacity: 1, // 副标题的文字透明度\n subtitleStroke: '#000', // 副标题的文字描边颜色\n subtitleLineWidth: 1, // 副标题的文字描边线宽\n subtitleStrokeOpacity: 0.5, // 副标题的文字描边透明度\n },\n ],\n node: {\n palette: 'spectral',\n style: { labelText: '你好' },\n },\n layout: {\n type: 'circular',\n },\n autoFit: 'view',\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Toolbar\norder: 15\n---\n\n## Overview\n\nThe Toolbar is a plugin in G6 used to provide a collection of operation buttons, supporting common chart operations such as zoom in, zoom out, auto-fit, and reset. Through the toolbar, users can quickly access common functions of the chart, improving operational efficiency and user experience.\n\n## Use Cases\n\nThis plugin is mainly used for:\n\n- Quickly accessing common functions of the chart\n\n## Basic Usage\n\nBelow is a simple example of initializing the Toolbar plugin:\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'toolbar',\n getItems: () => [\n { id: 'zoom-in', value: 'zoom-in' },\n { id: 'zoom-out', value: 'zoom-out' },\n { id: 'auto-fit', value: 'auto-fit' },\n ],\n onClick: (value) => {\n // Handle button click events\n if (value === 'zoom-in') {\n graph.zoomTo(1.1);\n } else if (value === 'zoom-out') {\n graph.zoomTo(0.9);\n } else if (value === 'auto-fit') {\n graph.fitView();\n }\n },\n },\n ],\n});\n```\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| --------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------- | -------- |\n| type | Plugin type | string | `toolbar` | ✓ |\n| key | Unique identifier for the plugin, used for updates | string | - | |\n| className | Additional CSS class name for the toolbar DOM element | string | - | |\n| position | Toolbar position relative to the canvas, [optional values](#position) | string | `'top-left'` | |\n| style | Custom style for the toolbar DOM element, [common values](#style-attributes) | [CSSStyleDeclaration](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration) | - | |\n| getItems | Returns the list of toolbar items | () => [ToolbarItem](#toolbaritem)[] \\| Promise<[ToolbarItem](#toolbaritem)[]> | - | ✓ |\n| onClick | Callback function after a toolbar item is clicked | (value: string, target: Element) => void | - | |\n\n### position\n\nThe `position` parameter supports the following values:\n\n- `'top-left'`: Top left corner\n- `'top-right'`: Top right corner\n- `'bottom-left'`: Bottom left corner\n- `'bottom-right'`: Bottom right corner\n- `'left-top'`: Left side, top\n- `'left-bottom'`: Left side, bottom\n- `'right-top'`: Right side, top\n- `'right-bottom'`: Right side, bottom\n\n### style Attributes\n\n| Attribute | Description | Type | Default Value |\n| --------------- | ---------------- | ------ | ------------------- |\n| backgroundColor | Background color | string | `#fff` |\n| border | Border | string | `1px solid #e8e8e8` |\n| borderRadius | Border radius | string | `4px` |\n| height | Height | string | `auto` |\n| margin | Margin | string | `12px` |\n| opacity | Opacity | number | 0.9 |\n| padding | Padding | string | `8px` |\n| width | Width | string | `auto` |\n\n### ToolbarItem\n\nEach toolbar item (ToolbarItem) includes the following attributes:\n\n| Attribute | Description | Type | Required |\n| --------- | ---------------------------------------------------------- | -------- | -------- |\n| id | Icon ID of the item, see [Built-in Icons](#built-in-icons) | `string` | ✓ |\n| value | Value returned when the item is clicked | `string` | ✓ |\n\n### Built-in Icons\n\nThe Toolbar provides the following built-in icon IDs:\n\n- `'zoom-in'`: Zoom in\n- `'zoom-out'`: Zoom out\n- `'redo'`: Redo\n- `'undo'`: Undo\n- `'edit'`: Edit\n- `'delete'`: Delete\n- `'auto-fit'`: Auto-fit view\n- `'export'`: Export chart\n- `'reset'`: Reset view\n- `'request-fullscreen'`: Request fullscreen\n- `'exit-fullscreen'`: Exit fullscreen\n\n### Custom Icons\n\nIn addition to using built-in icons, you can also use custom icons by introducing third-party icon libraries (such as Alibaba iconfont):\n\n```js\n// Introduce iconfont script\nconst iconFont = document.createElement('script');\niconFont.src = '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js'; // Replace with your iconfont script URL\ndocument.head.appendChild(iconFont);\n\n// Use custom icons in the toolbar\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'toolbar',\n getItems: () => [\n { id: 'icon-xinjian', value: 'new' }, // Use icons from iconfont\n { id: 'icon-fenxiang', value: 'share' },\n { id: 'icon-chexiao', value: 'undo' },\n ],\n onClick: (value) => {\n // Handle click events\n },\n },\n ],\n});\n```\n\n> Note: Custom icon IDs usually start with `icon-` and need to correspond to the icon names in the introduced iconfont.\n\n## Code Examples\n\n### Basic Toolbar\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'toolbar',\n position: 'top-right',\n getItems: () => [\n { id: 'zoom-in', value: 'zoom-in' },\n { id: 'zoom-out', value: 'zoom-out' },\n { id: 'undo', value: 'undo' },\n { id: 'redo', value: 'redo' },\n { id: 'auto-fit', value: 'fit' },\n ],\n onClick: (value) => {\n // redo, undo need to be used with the history plugin\n const history = graph.getPluginInstance('history');\n switch (value) {\n case 'zoom-in':\n graph.zoomTo(1.1);\n break;\n case 'zoom-out':\n graph.zoomTo(0.9);\n break;\n case 'undo':\n history?.undo();\n break;\n case 'redo':\n history?.redo();\n break;\n case 'fit':\n graph.fitView();\n break;\n default:\n break;\n }\n },\n },\n ],\n});\n```\n\n### Custom Styles\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'toolbar',\n className: 'my-custom-toolbar',\n style: {\n backgroundColor: '#f5f5f5',\n padding: '8px',\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',\n borderRadius: '8px',\n border: '1px solid #e8e8e8',\n opacity: '0.9',\n marginTop: '12px',\n marginLeft: '12px',\n },\n getItems: () => [\n { id: 'zoom-in', value: 'zoom-in' },\n { id: 'zoom-out', value: 'zoom-out' },\n ],\n onClick: (value) => {\n // Handle click events\n },\n },\n ],\n});\n```\n\n> Common style attributes include:\n>\n> - `backgroundColor`: Background color\n> - `padding`: Padding\n> - `margin`/`marginTop`/`marginLeft`, etc.: Margin\n> - `border`: Border\n> - `borderRadius`: Border radius\n> - `boxShadow`: Shadow effect\n> - `opacity`: Opacity\n> - `width`/`height`: Width and height (default is content adaptive)\n> - `zIndex`: Layer (default is 100)\n> - `display`: Display mode (default is flex)\n\nThe toolbar container is set to `display: flex` by default, and child items use row layout by default (or change according to the direction configured by position). You can customize its appearance and position through `style`.\n\n### Asynchronous Loading of Toolbar Items\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'toolbar',\n getItems: async () => {\n // Toolbar configuration can be obtained from the server or other asynchronous sources\n const response = await fetch('/api/toolbar-config');\n const items = await response.json();\n return items;\n },\n onClick: (value) => {\n // Handle click events\n },\n },\n ],\n});\n```\n\n## Common Issues\n\n### 1. Toolbar icons not displaying?\n\n- Check if the correct built-in icon ID is used\n- Ensure CSS styles are not overridden or conflicting\n\n### 2. How to use with other plugins?\n\nThe toolbar is often used in conjunction with other plugins (such as history):\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'history',\n key: 'history',\n },\n {\n type: 'toolbar',\n getItems: () => [\n { id: 'undo', value: 'undo' },\n { id: 'redo', value: 'redo' },\n ],\n onClick: (value) => {\n const history = graph.getPluginInstance('history');\n if (value === 'undo') {\n history.undo();\n } else if (value === 'redo') {\n history.redo();\n }\n },\n },\n ],\n});\n```\n\n### 3. How to dynamically update the toolbar?\n\nYou can use the `updatePlugin` method to dynamically update the toolbar:\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'toolbar',\n key: 'my-toolbar',\n },\n ],\n});\n\n// Update toolbar position\ngraph.updatePlugin({\n key: 'my-toolbar',\n position: 'bottom-right',\n});\n```\n\n## Real Cases\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: {\n type: 'grid',\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'toolbar',\n position: 'top-left',\n onClick: (item) => {\n alert('item clicked:' + item);\n },\n getItems: () => {\n // G6 内置了 9 个 icon,分别是 zoom-in、zoom-out、redo、undo、edit、delete、auto-fit、export、reset\n return [\n { id: 'zoom-in', value: 'zoom-in' },\n { id: 'zoom-out', value: 'zoom-out' },\n { id: 'redo', value: 'redo' },\n { id: 'undo', value: 'undo' },\n { id: 'edit', value: 'edit' },\n { id: 'delete', value: 'delete' },\n { id: 'auto-fit', value: 'auto-fit' },\n { id: 'export', value: 'export' },\n { id: 'reset', value: 'reset' },\n ];\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: 工具栏 Toolbar\norder: 15\n---\n\n## 概述\n\nToolbar 是 G6 中用于提供操作按钮集合的插件,支持放大、缩小、自适应、重置等常用图表操作。通过工具栏,用户可以快速访问图表的常用功能,提高操作效率和用户体验。\n\n## 使用场景\n\n这一插件主要用于:\n\n- 快速访问图表的常用功能\n\n## 基本用法\n\n以下是一个简单的 Toolbar 插件初始化示例:\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'toolbar',\n getItems: () => [\n { id: 'zoom-in', value: 'zoom-in' },\n { id: 'zoom-out', value: 'zoom-out' },\n { id: 'auto-fit', value: 'auto-fit' },\n ],\n onClick: (value) => {\n // 处理按钮点击事件\n if (value === 'zoom-in') {\n graph.zoomTo(1.1);\n } else if (value === 'zoom-out') {\n graph.zoomTo(0.9);\n } else if (value === 'auto-fit') {\n graph.fitView();\n }\n },\n },\n ],\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| --------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------- | ------------ | ---- |\n| type | 插件类型 | string | `toolbar` | ✓ |\n| key | 插件唯一标识符,用于后续更新 | string | - | |\n| className | 工具栏DOM元素的额外CSS类名 | string | - | |\n| position | 工具栏位置,相对于画布,[可选值](#position) | string | `'top-left'` | |\n| style | 工具栏DOM元素的自定义样式,[常用值](#style-属性) | [CSSStyleDeclaration](https://developer.mozilla.org/zh-CN/docs/Web/API/CSSStyleDeclaration) | - | |\n| getItems | 返回工具栏项目列表 | () => [ToolbarItem](#toolbaritem)[] \\| Promise<[ToolbarItem](#toolbaritem)[]> | - | ✓ |\n| onClick | 工具栏项目点击后的回调函数 | (value: string, target: Element) => void | - | |\n\n### position\n\n`position` 参数支持以下值:\n\n- `'top-left'`:左上角\n- `'top-right'`:右上角\n- `'bottom-left'`:左下角\n- `'bottom-right'`:右下角\n- `'left-top'`:左侧靠上\n- `'left-bottom'`:左侧靠下\n- `'right-top'`:右侧靠上\n- `'right-bottom'`:右侧靠下\n\n### style 属性\n\n| 属性 | 描述 | 类型 | 默认值 |\n| --------------- | -------- | ------ | ------------------- |\n| backgroundColor | 背景颜色 | string | `#fff` |\n| border | 边框 | string | `1px solid #e8e8e8` |\n| borderRadius | 圆角 | string | `4px` |\n| height | 高度 | string | `auto` |\n| margin | 外边距 | string | `12px` |\n| opacity | 透明度 | number | 0.9 |\n| padding | 内边距 | string | `8px` |\n| width | 宽度 | string | `auto` |\n\n### ToolbarItem\n\n每个工具栏项目 (ToolbarItem) 包含以下属性:\n\n| 属性 | 描述 | 类型 | 必选 |\n| ----- | ----------------------------------------------- | -------- | ---- |\n| id | 项目的图标ID,内置图标ID见[内置图标](#内置图标) | `string` | ✓ |\n| value | 点击项目时返回的值 | `string` | ✓ |\n\n### 内置图标\n\nToolbar 提供以下内置图标 ID:\n\n- `'zoom-in'`:放大\n- `'zoom-out'`:缩小\n- `'redo'`:重做\n- `'undo'`:撤销\n- `'edit'`:编辑\n- `'delete'`:删除\n- `'auto-fit'`:自适应视图\n- `'export'`:导出图表\n- `'reset'`:重置视图\n- `'request-fullscreen'`:请求全屏\n- `'exit-fullscreen'`:退出全屏\n\n### 自定义图标\n\n除了使用内置图标外,还可以通过引入第三方图标库(如阿里巴巴 iconfont)来使用自定义图标:\n\n```js\n// 引入 iconfont 脚本\nconst iconFont = document.createElement('script');\niconFont.src = '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js'; // 替换为你的 iconfont 脚本地址\ndocument.head.appendChild(iconFont);\n\n// 在工具栏中使用自定义图标\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'toolbar',\n getItems: () => [\n { id: 'icon-xinjian', value: 'new' }, // 使用 iconfont 中的图标\n { id: 'icon-fenxiang', value: 'share' },\n { id: 'icon-chexiao', value: 'undo' },\n ],\n onClick: (value) => {\n // 处理点击事件\n },\n },\n ],\n});\n```\n\n> 注意:自定义图标的 ID 通常以 `icon-` 开头,需要与引入的 iconfont 中的图标名称对应。\n\n## 代码示例\n\n### 基础工具栏\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'toolbar',\n position: 'top-right',\n getItems: () => [\n { id: 'zoom-in', value: 'zoom-in' },\n { id: 'zoom-out', value: 'zoom-out' },\n { id: 'undo', value: 'undo' },\n { id: 'redo', value: 'redo' },\n { id: 'auto-fit', value: 'fit' },\n ],\n onClick: (value) => {\n // redo、undo 需要配合 history 插件使用\n const history = graph.getPluginInstance('history');\n switch (value) {\n case 'zoom-in':\n graph.zoomTo(1.1);\n break;\n case 'zoom-out':\n graph.zoomTo(0.9);\n break;\n case 'undo':\n history?.undo();\n break;\n case 'redo':\n history?.redo();\n break;\n case 'fit':\n graph.fitView();\n break;\n default:\n break;\n }\n },\n },\n ],\n});\n```\n\n### 自定义样式\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'toolbar',\n className: 'my-custom-toolbar',\n style: {\n backgroundColor: '#f5f5f5',\n padding: '8px',\n boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',\n borderRadius: '8px',\n border: '1px solid #e8e8e8',\n opacity: '0.9',\n marginTop: '12px',\n marginLeft: '12px',\n },\n getItems: () => [\n { id: 'zoom-in', value: 'zoom-in' },\n { id: 'zoom-out', value: 'zoom-out' },\n ],\n onClick: (value) => {\n // 处理点击事件\n },\n },\n ],\n});\n```\n\n> 常用的样式属性包括:\n>\n> - `backgroundColor`:背景颜色\n> - `padding`:内边距\n> - `margin`/`marginTop`/`marginLeft` 等:外边距\n> - `border`:边框\n> - `borderRadius`:圆角\n> - `boxShadow`:阴影效果\n> - `opacity`:透明度\n> - `width`/`height`:宽高(默认自适应内容)\n> - `zIndex`:层级(默认为 100)\n> - `display`:显示方式(默认为 flex)\n\n工具栏容器默认设置 `display: flex`,子项目默认使用行布局(或根据 position 配置的方向改变)。你可以通过 `style` 自定义其外观和位置。\n\n### 异步加载工具栏项\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'toolbar',\n getItems: async () => {\n // 可以从服务器或其他异步源获取工具栏配置\n const response = await fetch('/api/toolbar-config');\n const items = await response.json();\n return items;\n },\n onClick: (value) => {\n // 处理点击事件\n },\n },\n ],\n});\n```\n\n## 常见问题\n\n### 1. 工具栏图标不显示?\n\n- 检查是否使用了正确的内置图标 ID\n- 确保 CSS 样式未被覆盖或冲突\n\n### 2. 如何结合其他插件使用?\n\n工具栏常常与其他插件(如历史记录)配合使用:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'history',\n key: 'history',\n },\n {\n type: 'toolbar',\n getItems: () => [\n { id: 'undo', value: 'undo' },\n { id: 'redo', value: 'redo' },\n ],\n onClick: (value) => {\n const history = graph.getPluginInstance('history');\n if (value === 'undo') {\n history.undo();\n } else if (value === 'redo') {\n history.redo();\n }\n },\n },\n ],\n});\n```\n\n### 3. 如何动态更新工具栏?\n\n可以使用 `updatePlugin` 方法动态更新工具栏:\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'toolbar',\n key: 'my-toolbar',\n },\n ],\n});\n\n// 更新工具栏位置\ngraph.updatePlugin({\n key: 'my-toolbar',\n position: 'bottom-right',\n});\n```\n\n## 实际案例\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: {\n type: 'grid',\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'toolbar',\n position: 'top-left',\n onClick: (item) => {\n alert('item clicked:' + item);\n },\n getItems: () => {\n // G6 内置了 9 个 icon,分别是 zoom-in、zoom-out、redo、undo、edit、delete、auto-fit、export、reset\n return [\n { id: 'zoom-in', value: 'zoom-in' },\n { id: 'zoom-out', value: 'zoom-out' },\n { id: 'redo', value: 'redo' },\n { id: 'undo', value: 'undo' },\n { id: 'edit', value: 'edit' },\n { id: 'delete', value: 'delete' },\n { id: 'auto-fit', value: 'auto-fit' },\n { id: 'export', value: 'export' },\n { id: 'reset', value: 'reset' },\n ];\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Tooltip\norder: 16\n---\n\n## Overview\n\nThe Tooltip plugin is used to display additional information when users hover over or click on elements in the graph. It helps users better understand the data in the graph and improves the interactive experience.\n\n## Use Cases\n\n- **Detailed Information Display**: When users need to understand detailed information about elements, use Tooltip to display this information\n- **Data Visualization Assistance**: In data visualization, Tooltip can display detailed information about data points in charts, helping users better understand the data\n- **Interactive Feedback**: Provide immediate visual feedback for user mouse operations\n\n## Basic Usage\n\nThe simplest Tooltip plugin configuration:\n\n```js\nconst graph = new Graph({\n // Other configurations...\n plugins: [\n {\n type: 'tooltip',\n },\n ],\n});\n```\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| ------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | -------- |\n| type | Plugin type | string | `tooltip` | ✓ |\n| key | Identifier | string | - | |\n| position | Tooltip position | `top` \\| `bottom` \\| `left` \\| `right` \\| `top-left` \\| `top-right` \\| `bottom-left` \\| `bottom-right` | `top-right` | |\n| enable | Whether plugin is enabled | boolean \\| ((event: [IElementEvent](/en/api/event#event-object-properties), items: NodeData \\| EdgeData \\| ComboData[]) => boolean) | true | |\n| getContent | Custom content | (event: [IElementEvent](/en/api/event#event-object-properties), items: NodeData \\| EdgeData \\| ComboData[]) => Promise | - | |\n| onOpenChange | Show/hide callback | (open: boolean) => void | - | |\n| trigger | Trigger behavior | `hover` \\| `click` | `hover` |\n| container | Custom render container | string \\| HTMLElement | - | |\n| offset | Offset distance | [number,number] | [10,10] | |\n| enterable | Whether pointer can enter | boolean | false | |\n| title | Title | string | - |\n| style | Style object | Record | {'.tooltip': { visibility: 'hidden'}} | |\n\n## Detailed Configuration\n\n### enable - Conditional Enable\n\nControls whether the plugin is enabled, supports passing functions to dynamically adjust enable logic.\n\n**Example: Enable Tooltip only for nodes**\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { name: 'Server Node' } },\n { id: 'node2', style: { x: 200, y: 100 }, data: { name: 'Database Node' } },\n ],\n edges: [{ source: 'node1', target: 'node2', data: { type: 'Connection' } }],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 200,\n data,\n plugins: [\n {\n type: 'tooltip',\n // Enable only for nodes, not for edges\n enable: (e) => e.targetType === 'node',\n getContent: (e, items) => {\n return `
Node: ${items[0].data.name}
`;\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### getContent - Custom Content\n\nCustomize Tooltip content rendering, supports returning HTMLElement or string.\n\n**Example: Dynamically render custom HTML content**\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n {\n id: 'node1',\n style: { x: 100, y: 100 },\n data: { name: 'Server A', type: 'Application Server', status: 'Running', cpu: '45%', memory: '2.1GB' },\n },\n {\n id: 'node2',\n style: { x: 250, y: 100 },\n data: { name: 'Database B', type: 'MySQL Database', status: 'Normal', connections: 23, size: '500MB' },\n },\n ],\n edges: [{ source: 'node1', target: 'node2', data: { bandwidth: '1Gbps', latency: '5ms' } }],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 200,\n data,\n plugins: [\n {\n type: 'tooltip',\n getContent: (e, items) => {\n const item = items[0];\n\n if (e.targetType === 'node') {\n return `\n
\n

\n ${item.data.name}\n

\n
\n Type: ${item.data.type}\n
\n
\n Status:\n \n ${item.data.status}\n \n
\n ${item.data.cpu ? `
CPU: ${item.data.cpu}
` : ''}\n ${item.data.memory ? `
Memory: ${item.data.memory}
` : ''}\n ${item.data.connections ? `
Connections: ${item.data.connections}
` : ''}\n ${item.data.size ? `
Size: ${item.data.size}
` : ''}\n
\n `;\n } else if (e.targetType === 'edge') {\n return `\n
\n

Connection Info

\n
Bandwidth: ${item.data.bandwidth}
\n
Latency: ${item.data.latency}
\n
\n `;\n }\n\n return 'No information available';\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### trigger - Trigger Mode\n\nControls the trigger behavior of Tooltip.\n\n**Available values:**\n\n- `hover`: Trigger when mouse enters element (default)\n- `click`: Trigger when mouse clicks element\n\n**Example: Click-triggered Tooltip**\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { name: 'Click me' } },\n { id: 'node2', style: { x: 200, y: 100 }, data: { name: 'Click me too' } },\n ],\n edges: [{ source: 'node1', target: 'node2' }],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 350,\n height: 200,\n data,\n node: {\n style: {\n labelText: (d) => d.data.name,\n },\n },\n plugins: [\n {\n type: 'tooltip',\n trigger: 'click',\n getContent: (e, items) => {\n return `\n
\n
\n Click Triggered 🖱️\n
\n
\n Element ID: ${items[0].id}
\n Name: ${items[0].data?.name || 'Unnamed'}\n
\n
\n `;\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### position - Display Position\n\nControls the display position of Tooltip relative to mouse position.\n\n**Available values:**\n\n- `top`: Top\n- `bottom`: Bottom\n- `left`: Left\n- `right`: Right\n- `top-left`: Top left\n- `top-right`: Top right (default)\n- `bottom-left`: Bottom left\n- `bottom-right`: Bottom right\n\n**Example: Tooltips at different positions**\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { label: 'TOP' } },\n { id: 'node2', style: { x: 250, y: 100 }, data: { label: 'BOTTOM' } },\n { id: 'node3', style: { x: 100, y: 250 }, data: { label: 'LEFT' } },\n { id: 'node4', style: { x: 250, y: 250 }, data: { label: 'RIGHT' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 800,\n height: 400,\n data,\n node: { style: { labelText: (d) => d.data.label } },\n plugins: [\n {\n key: 'tooltip-top',\n type: 'tooltip',\n position: 'top',\n enable: (e, items) => items[0].id === 'node1',\n getContent: () => `Display at top ⬆️`,\n style: {\n '.tooltip': {\n background: ' #fff2e8',\n border: '1px solid #ffa940',\n borderRadius: 4,\n },\n },\n },\n {\n key: 'tooltip-bottom',\n type: 'tooltip',\n position: 'bottom',\n enable: (e, items) => items[0].id === 'node2',\n getContent: () => `Display at bottom ⬇️`,\n style: {\n '.tooltip': {\n background: '#f6ffed',\n border: '1px solid #73d13d',\n borderRadius: 4,\n },\n },\n },\n {\n key: 'tooltip-left',\n type: 'tooltip',\n position: 'left',\n enable: (e, items) => items[0].id === 'node3',\n getContent: () => `Display at left ⬅️`,\n style: {\n '.tooltip': {\n background: '#fff1f0',\n border: '1px solid #ff7875',\n borderRadius: 4,\n },\n },\n },\n {\n key: 'tooltip-right',\n type: 'tooltip',\n position: 'right',\n enable: (e, items) => items[0].id === 'node4',\n getContent: () => `Display at right ➡️`,\n style: {\n '.tooltip': {\n background: '#f0f5ff',\n border: '1px solid #597ef7',\n borderRadius: 4,\n },\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### offset - Offset\n\nSet the offset for Tooltip display position, with mouse position as the base point.\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { label: 'Default offset' } },\n { id: 'node2', style: { x: 250, y: 100 }, data: { label: 'Custom offset' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 800,\n height: 200,\n data,\n plugins: [\n {\n key: 'tooltip-default',\n type: 'tooltip',\n enable: (e, items) => items[0].id === 'node1',\n getContent: () => `Default offset [10,10]`,\n },\n {\n key: 'tooltip-custom',\n type: 'tooltip',\n offset: [30, -10], // Offset 30px to the right, 10px up\n enable: (e, items) => items[0].id === 'node2',\n getContent: () => `Custom offset [30,-10]`,\n },\n ],\n});\n\ngraph.render();\n```\n\n### enterable - Mouse Enterable\n\nControls whether the mouse pointer can enter the tooltip box, commonly used for scenarios requiring interaction within the Tooltip.\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { name: 'User A', email: '[email protected]' } },\n { id: 'node2', style: { x: 250, y: 100 }, data: { name: 'User B', email: '[email protected]' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 200,\n data,\n plugins: [\n {\n type: 'tooltip',\n enterable: true,\n position: 'right',\n getContent: (e, items) => {\n const item = items[0];\n return `\n
\n

User Actions

\n
\n Name: ${item.data.name}\n
\n
\n Email: ${item.data.email}\n
\n
\n \n \n
\n
\n `;\n },\n style: {\n '.tooltip': {\n background: '#fff',\n borderRadius: '8px',\n boxShadow: '0 4px 20px rgba(0,0,0,0.15)',\n minWidth: '200px',\n },\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### style - Style Customization\n\nCustomize Tooltip styles.\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { theme: 'dark', name: 'Dark Theme' } },\n { id: 'node2', style: { x: 250, y: 100 }, data: { theme: 'light', name: 'Light Theme' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 200,\n data,\n plugins: [\n {\n key: 'tooltip-dark',\n type: 'tooltip',\n enable: (e, items) => items[0].data.theme === 'dark',\n style: {\n '.tooltip': {\n background: '#1f1f1f',\n color: '#fff',\n border: '1px solid #333',\n borderRadius: '8px',\n fontSize: '14px',\n fontFamily: 'Arial, sans-serif',\n boxShadow: '0 4px 20px rgba(0,0,0,0.3)',\n },\n },\n getContent: (e, items) => {\n return `
🌙 ${items[0].data.name}
`;\n },\n },\n {\n key: 'tooltip-light',\n type: 'tooltip',\n enable: (e, items) => items[0].data.theme === 'light',\n style: {\n '.tooltip': {\n background: '#ffffff',\n color: '#333',\n border: '1px solid #d9d9d9',\n borderRadius: '8px',\n fontSize: '14px',\n fontFamily: 'Arial, sans-serif',\n boxShadow: '0 2px 8px rgba(0,0,0,0.15)',\n },\n },\n getContent: (e, items) => {\n return `
☀️ ${items[0].data.name}
`;\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n## Practical Examples\n\n- [Basic Tooltip](/en/examples/plugin/tooltip/#basic)\n- [Click-triggered Tooltip](/en/examples/plugin/tooltip/#click)\n- [Different tooltips for hover and click on the same element](/en/examples/plugin/tooltip/#dual)\n- [Custom styled Tooltip](/en/examples/plugin/tooltip/#custom-style)\n- [Asynchronous content loading Tooltip](/en/examples/plugin/tooltip/#async)\n\n## API\n\n\n\n---\ntitle: 提示框 Tooltip\norder: 16\n---\n\n## 概述\n\nTooltip 插件用于在用户将鼠标悬停或点击图中的元素时,显示额外的信息。它可以帮助用户更好地理解图中的数据,提高交互体验。\n\n## 使用场景\n\n- **详细信息展示**:当用户需要了解元素的详细信息时,使用 Tooltip 提示框来展示这些信息\n- **数据可视化辅助**:在数据可视化中,Tooltip 可以显示图表中数据点的详细信息,帮助用户更好地理解数据\n- **交互反馈**:为用户的鼠标操作提供即时的视觉反馈\n\n## 基本使用\n\n最简单的 Tooltip 插件配置:\n\n```js\nconst graph = new Graph({\n // 其他配置...\n plugins: [\n {\n type: 'tooltip',\n },\n ],\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | ---- |\n| type | 插件类型 | string | `tooltip` | ✓ |\n| key | 标识符 | string | - | |\n| position | 气泡框位置 | `top` \\| `bottom` \\| `left` \\| `right` \\| `top-left` \\| `top-right` \\| `bottom-left` \\| `bottom-right` | `top-right` | |\n| enable | 插件是否启用 | boolean \\| ((event: [IElementEvent](/api/event#事件对象属性), items: NodeData \\| EdgeData \\| ComboData[]) => boolean) | true | |\n| getContent | 自定义内容 | (event: [IElementEvent](/api/event#事件对象属性), items: NodeData \\| EdgeData \\| ComboData[]) => Promise | - | |\n| onOpenChange | 显示隐藏的回调 | (open: boolean) => void | - | |\n| trigger | 触发行为 | `hover` \\| `click` | `hover` |\n| container | tooltip自定义渲染的容器 | string \\| HTMLElement | - | |\n| offset | 偏移距离 | [number,number] | [10,10] | |\n| enterable | 指针是否可以进入 | boolean | false | |\n| title | 标题 | string | - |\n| style | 样式对象 | Record | {'.tooltip': { visibility: 'hidden'}} | |\n\n## 详细配置说明\n\n### enable - 条件启用\n\n控制插件是否启用,支持传入函数动态调整启用逻辑。\n\n**示例:只对节点启用 Tooltip**\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { name: '服务器节点' } },\n { id: 'node2', style: { x: 200, y: 100 }, data: { name: '数据库节点' } },\n ],\n edges: [{ source: 'node1', target: 'node2', data: { type: '连接线' } }],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 200,\n data,\n plugins: [\n {\n type: 'tooltip',\n // 只对节点启用,边不显示tooltip\n enable: (e) => e.targetType === 'node',\n getContent: (e, items) => {\n return `
节点: ${items[0].data.name}
`;\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### getContent - 自定义内容\n\n自定义渲染 Tooltip 内容,支持返回 HTMLElement 或 string。\n\n**示例:动态渲染自定义 HTML 内容**\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n {\n id: 'node1',\n style: { x: 100, y: 100 },\n data: { name: '服务器A', type: '应用服务器', status: '运行中', cpu: '45%', memory: '2.1GB' },\n },\n {\n id: 'node2',\n style: { x: 250, y: 100 },\n data: { name: '数据库B', type: 'MySQL数据库', status: '正常', connections: 23, size: '500MB' },\n },\n ],\n edges: [{ source: 'node1', target: 'node2', data: { bandwidth: '1Gbps', latency: '5ms' } }],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 200,\n data,\n plugins: [\n {\n type: 'tooltip',\n getContent: (e, items) => {\n const item = items[0];\n\n if (e.targetType === 'node') {\n return `\n
\n

\n ${item.data.name}\n

\n
\n 类型: ${item.data.type}\n
\n
\n 状态:\n \n ${item.data.status}\n \n
\n ${item.data.cpu ? `
CPU: ${item.data.cpu}
` : ''}\n ${item.data.memory ? `
内存: ${item.data.memory}
` : ''}\n ${item.data.connections ? `
连接数: ${item.data.connections}
` : ''}\n ${item.data.size ? `
大小: ${item.data.size}
` : ''}\n
\n `;\n } else if (e.targetType === 'edge') {\n return `\n
\n

连接信息

\n
带宽: ${item.data.bandwidth}
\n
延迟: ${item.data.latency}
\n
\n `;\n }\n\n return '暂无信息';\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### trigger - 触发方式\n\n控制 Tooltip 的触发行为。\n\n**可选值:**\n\n- `hover`:鼠标移入元素时触发(默认)\n- `click`:鼠标点击元素时触发\n\n**示例:点击触发 Tooltip**\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { name: '点击我' } },\n { id: 'node2', style: { x: 200, y: 100 }, data: { name: '也点击我' } },\n ],\n edges: [{ source: 'node1', target: 'node2' }],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 350,\n height: 200,\n data,\n node: {\n style: {\n labelText: (d) => d.data.name,\n },\n },\n plugins: [\n {\n type: 'tooltip',\n trigger: 'click',\n getContent: (e, items) => {\n return `\n
\n
\n 点击触发 🖱️\n
\n
\n 元素ID: ${items[0].id}
\n 名称: ${items[0].data?.name || '未命名'}\n
\n
\n `;\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### position - 显示位置\n\n控制 Tooltip 相对于鼠标位置的显示位置。\n\n**可选值:**\n\n- `top`: 顶部\n- `bottom`: 底部\n- `left`: 左侧\n- `right`: 右侧\n- `top-left`: 顶部靠左\n- `top-right`: 顶部靠右(默认)\n- `bottom-left`: 底部靠左\n- `bottom-right`: 底部靠右\n\n**示例:不同位置的 Tooltip**\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { label: 'TOP' } },\n { id: 'node2', style: { x: 250, y: 100 }, data: { label: 'BOTTOM' } },\n { id: 'node3', style: { x: 100, y: 250 }, data: { label: 'LEFT' } },\n { id: 'node4', style: { x: 250, y: 250 }, data: { label: 'RIGHT' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 800,\n height: 400,\n data,\n node: { style: { labelText: (d) => d.data.label } },\n plugins: [\n {\n key: 'tooltip-top',\n type: 'tooltip',\n position: 'top',\n enable: (e, items) => items[0].id === 'node1',\n getContent: () => `顶部显示 ⬆️`,\n style: {\n '.tooltip': {\n background: ' #fff2e8',\n border: '1px solid #ffa940',\n borderRadius: 4,\n },\n },\n },\n {\n key: 'tooltip-bottom',\n type: 'tooltip',\n position: 'bottom',\n enable: (e, items) => items[0].id === 'node2',\n getContent: () => `底部显示 ⬇️`,\n style: {\n '.tooltip': {\n background: '#f6ffed',\n border: '1px solid #73d13d',\n borderRadius: 4,\n },\n },\n },\n {\n key: 'tooltip-left',\n type: 'tooltip',\n position: 'left',\n enable: (e, items) => items[0].id === 'node3',\n getContent: () => `左侧显示 ⬅️`,\n style: {\n '.tooltip': {\n background: '#fff1f0',\n border: '1px solid #ff7875',\n borderRadius: 4,\n },\n },\n },\n {\n key: 'tooltip-right',\n type: 'tooltip',\n position: 'right',\n enable: (e, items) => items[0].id === 'node4',\n getContent: () => `右侧显示 ➡️`,\n style: {\n '.tooltip': {\n background: '#f0f5ff',\n border: '1px solid #597ef7',\n borderRadius: 4,\n },\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### offset - 偏移量\n\n设置 Tooltip 显示位置的偏移量,以鼠标位置为基点。\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { label: '默认偏移' } },\n { id: 'node2', style: { x: 250, y: 100 }, data: { label: '自定义偏移' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 800,\n height: 200,\n data,\n plugins: [\n {\n key: 'tooltip-default',\n type: 'tooltip',\n enable: (e, items) => items[0].id === 'node1',\n getContent: () => `默认偏移 [10,10]`,\n },\n {\n key: 'tooltip-custom',\n type: 'tooltip',\n offset: [30, -10], // 向右偏移30px,向上偏移10px\n enable: (e, items) => items[0].id === 'node2',\n getContent: () => `自定义偏移 [30,-10]`,\n },\n ],\n});\n\ngraph.render();\n```\n\n### enterable - 鼠标可进入\n\n控制鼠标指针是否可以进入气泡框,常用于需要在 Tooltip 内进行交互的场景。\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { name: '用户A', email: '[email protected]' } },\n { id: 'node2', style: { x: 250, y: 100 }, data: { name: '用户B', email: '[email protected]' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 200,\n data,\n plugins: [\n {\n type: 'tooltip',\n enterable: true,\n position: 'right',\n getContent: (e, items) => {\n const item = items[0];\n return `\n
\n

用户操作

\n
\n 姓名: ${item.data.name}\n
\n
\n 邮箱: ${item.data.email}\n
\n
\n \n \n
\n
\n `;\n },\n style: {\n '.tooltip': {\n background: '#fff',\n borderRadius: '8px',\n boxShadow: '0 4px 20px rgba(0,0,0,0.15)',\n minWidth: '200px',\n },\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n### style - 样式自定义\n\n自定义 Tooltip 的样式。\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'node1', style: { x: 100, y: 100 }, data: { theme: 'dark', name: '深色主题' } },\n { id: 'node2', style: { x: 250, y: 100 }, data: { theme: 'light', name: '浅色主题' } },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n width: 400,\n height: 200,\n data,\n plugins: [\n {\n key: 'tooltip-dark',\n type: 'tooltip',\n enable: (e, items) => items[0].data.theme === 'dark',\n style: {\n '.tooltip': {\n background: '#1f1f1f',\n color: '#fff',\n border: '1px solid #333',\n borderRadius: '8px',\n fontSize: '14px',\n fontFamily: 'Arial, sans-serif',\n boxShadow: '0 4px 20px rgba(0,0,0,0.3)',\n },\n },\n getContent: (e, items) => {\n return `
🌙 ${items[0].data.name}
`;\n },\n },\n {\n key: 'tooltip-light',\n type: 'tooltip',\n enable: (e, items) => items[0].data.theme === 'light',\n style: {\n '.tooltip': {\n background: '#ffffff',\n color: '#333',\n border: '1px solid #d9d9d9',\n borderRadius: '8px',\n fontSize: '14px',\n fontFamily: 'Arial, sans-serif',\n boxShadow: '0 2px 8px rgba(0,0,0,0.15)',\n },\n },\n getContent: (e, items) => {\n return `
☀️ ${items[0].data.name}
`;\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n## 实际案例\n\n- [基础提示框](/examples/plugin/tooltip/#basic)\n- [点击触发 Tooltip](/examples/plugin/tooltip/#click)\n- [鼠标移入和点击同一元素时显示不同的提示框](/examples/plugin/tooltip/#dual)\n- [自定义样式的 Tooltip](/examples/plugin/tooltip/#custom-style)\n- [异步加载内容的 Tooltip](/examples/plugin/tooltip/#async)\n\n\n\n---\ntitle: Watermark\norder: 17\n---\n\n## Overview\n\nThe watermark plugin supports using text and images as watermarks. The principle is to add a `background-image` attribute to the div of the Graph container, and then control the position and style of the watermark through CSS. For text watermarks, a hidden canvas is used to convert the text into an image.\n\n## Use Cases\n\n- Add copyright or ownership marks to charts\n- Mark the status of charts during presentations or previews\n- Add anti-leakage marks to sensitive data\n\n## Basic Usage\n\nBelow is a simple example of initializing the Watermark plugin:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'watermark',\n text: 'G6 Graph', // Watermark text\n opacity: 0.2, // Opacity\n rotate: Math.PI / 12, // Rotation angle\n },\n ],\n});\n```\n\n## Online Experience\n\n\n\n## Configuration Options\n\n| Property | Description | Type | Default Value | Required |\n| -------------------- | -------------------------------------------------------- | --------------------------------------------------------------------------- | ------------- | -------- |\n| type | Plugin type | string | `watermark` | ✓ |\n| width | Width of a single watermark | number | 200 | |\n| height | Height of a single watermark | number | 100 | |\n| opacity | Opacity of the watermark | number | 0.2 | |\n| rotate | Rotation angle of the watermark | number | Math.PI / 12 | |\n| imageURL | Image watermark URL, higher priority than text watermark | string | - | |\n| text | Watermark text content | string | - | |\n| textFill | Color of the text watermark | string | `#000` | |\n| textFontSize | Font size of the text watermark | number | 16 | |\n| textFontFamily | Font of the text watermark | string | - | |\n| textFontWeight | Font weight of the text watermark | string | - | |\n| textFontVariant | Font variant of the text watermark | string | - | |\n| textAlign | Text alignment of the watermark | `center` \\| `end` \\| `left` \\| `right` \\| `start` | `center` | |\n| textBaseline | Baseline alignment of the text watermark | `alphabetic` \\| `bottom` \\| `hanging` \\| `ideographic` \\| `middle` \\| `top` | `middle` | |\n| backgroundRepeat | Repeat mode of the watermark | string | `repeat` | |\n| backgroundAttachment | Background attachment behavior of the watermark | string | - | |\n| backgroundBlendMode | Background blend mode of the watermark | string | - | |\n| backgroundClip | Background clip of the watermark | string | - | |\n| backgroundColor | Background color of the watermark | string | - | |\n| backgroundImage | Background image of the watermark | string | - | |\n| backgroundOrigin | Background origin of the watermark | string | - | |\n| backgroundPosition | Background position of the watermark | string | - | |\n| backgroundPositionX | Horizontal position of the watermark background | string | - | |\n| backgroundPositionY | Vertical position of the watermark background | string | - | |\n| backgroundSize | Background size of the watermark | string | - | |\n\n## Code Examples\n\n### Text Watermark\n\nThe simplest text watermark configuration:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'watermark',\n text: 'G6 Graph',\n },\n ],\n});\n```\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: { type: 'grid' },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'watermark',\n text: 'G6: Graph Visualization',\n textFontSize: 14,\n textFontFamily: 'Microsoft YaHei',\n fill: 'rgba(0, 0, 0, 0.1)',\n rotate: Math.PI / 12,\n },\n ],\n});\n\ngraph.render();\n```\n\n### Image Watermark\n\nUse an image as a watermark:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'watermark',\n imageURL: 'https://example.com/logo.png',\n width: 100,\n height: 50,\n opacity: 0.1,\n },\n ],\n});\n```\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: {\n type: 'grid',\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'watermark',\n width: 200,\n height: 100,\n rotate: Math.PI / 12,\n imageURL: 'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg',\n },\n ],\n});\n\ngraph.render();\n```\n\n### Custom Styles\n\nYou can customize the style and position of the watermark:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'watermark',\n text: 'G6 Graph',\n textFontSize: 20, // Set font size\n textFontFamily: 'Arial', // Set font\n textFontWeight: 'bold', // Set font weight\n textFill: '#1890ff', // Set text color\n rotate: Math.PI / 6, // Set rotation angle\n opacity: 0.15, // Set opacity\n width: 180, // Set watermark width\n height: 100, // Set watermark height\n backgroundRepeat: 'space', // Set repeat mode\n backgroundPosition: 'center', // Set position\n textAlign: 'center', // Set text alignment\n textBaseline: 'middle', // Set baseline alignment\n },\n ],\n});\n```\n\n## Real Cases\n\n- [Text Watermark](/examples/plugin/watermark/#text)\n- [Image Watermark](/examples/plugin/watermark/#repeat)\n\n\n\n---\ntitle: 水印 Watermark\norder: 17\n---\n\n## 概述\n\n水印插件支持使用文本和图片作为水印,实现原理是在 Graph 容器的 div 上加上 `background-image` 属性,然后通过 CSS 来控制水印的位置和样式。对于文本水印,会使用隐藏 canvas 将文本转换为图片的方式来实现。\n\n## 使用场景\n\n- 为图表添加版权或所有权标识\n- 在演示或预览时标记图表的状态\n- 为敏感数据添加防泄露标记\n\n## 基本用法\n\n以下是一个简单的 Watermark 插件初始化示例:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'watermark',\n text: 'G6 Graph', // 水印文本\n opacity: 0.2, // 透明度\n rotate: Math.PI / 12, // 旋转角度\n },\n ],\n});\n```\n\n## 在线体验\n\n\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------------- | ---------------------------------- | --------------------------------------------------------------------------- | ------------ | ---- |\n| type | 插件类型 | string | `watermark` | ✓ |\n| width | 单个水印的宽度 | number | 200 | |\n| height | 单个水印的高度 | number | 100 | |\n| opacity | 水印的透明度 | number | 0.2 | |\n| rotate | 水印的旋转角度 | number | Math.PI / 12 | |\n| imageURL | 图片水印的地址,优先级高于文本水印 | string | - | |\n| text | 水印文本内容 | string | - | |\n| textFill | 文本水印的颜色 | string | `#000` | |\n| textFontSize | 文本水印的字体大小 | number | 16 | |\n| textFontFamily | 文本水印的字体 | string | - | |\n| textFontWeight | 文本水印的字体粗细 | string | - | |\n| textFontVariant | 文本水印的字体变体 | string | - | |\n| textAlign | 文本水印的对齐方式 | `center` \\| `end` \\| `left` \\| `right` \\| `start` | `center` | |\n| textBaseline | 文本水印的基线对齐方式 | `alphabetic` \\| `bottom` \\| `hanging` \\| `ideographic` \\| `middle` \\| `top` | `middle` | |\n| backgroundRepeat | 水印的重复方式 | string | `repeat` | |\n| backgroundAttachment | 水印的背景定位行为 | string | - | |\n| backgroundBlendMode | 水印的背景混合模式 | string | - | |\n| backgroundClip | 水印的背景裁剪 | string | - | |\n| backgroundColor | 水印的背景颜色 | string | - | |\n| backgroundImage | 水印的背景图片 | string | - | |\n| backgroundOrigin | 水印的背景原点 | string | - | |\n| backgroundPosition | 水印的背景位置 | string | - | |\n| backgroundPositionX | 水印的背景水平位置 | string | - | |\n| backgroundPositionY | 水印的背景垂直位置 | string | - | |\n| backgroundSize | 水印的背景大小 | string | - | |\n\n## 代码示例\n\n### 文本水印\n\n最简单的文本水印配置:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'watermark',\n text: 'G6 Graph',\n },\n ],\n});\n```\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: { type: 'grid' },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'watermark',\n text: 'G6: Graph Visualization',\n textFontSize: 14,\n textFontFamily: 'Microsoft YaHei',\n fill: 'rgba(0, 0, 0, 0.1)',\n rotate: Math.PI / 12,\n },\n ],\n});\n\ngraph.render();\n```\n\n### 图片水印\n\n使用图片作为水印:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'watermark',\n imageURL: 'https://example.com/logo.png',\n width: 100,\n height: 50,\n opacity: 0.1,\n },\n ],\n});\n```\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [{ id: 'node-0' }, { id: 'node-1' }, { id: 'node-2' }, { id: 'node-3' }, { id: 'node-4' }, { id: 'node-5' }],\n edges: [\n { source: 'node-0', target: 'node-1' },\n { source: 'node-0', target: 'node-2' },\n { source: 'node-0', target: 'node-3' },\n { source: 'node-0', target: 'node-4' },\n { source: 'node-1', target: 'node-0' },\n { source: 'node-2', target: 'node-0' },\n { source: 'node-3', target: 'node-0' },\n { source: 'node-4', target: 'node-0' },\n { source: 'node-5', target: 'node-0' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n data,\n layout: {\n type: 'grid',\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n plugins: [\n {\n type: 'watermark',\n width: 200,\n height: 100,\n rotate: Math.PI / 12,\n imageURL: 'https://gw.alipayobjects.com/os/s/prod/antv/assets/image/logo-with-text-73b8a.svg',\n },\n ],\n});\n\ngraph.render();\n```\n\n### 自定义样式\n\n可以自定义水印的样式和位置:\n\n```js\nconst graph = new Graph({\n plugins: [\n {\n type: 'watermark',\n text: 'G6 Graph',\n textFontSize: 20, // 设置字体大小\n textFontFamily: 'Arial', // 设置字体\n textFontWeight: 'bold', // 设置字体粗细\n textFill: '#1890ff', // 设置文字颜色\n rotate: Math.PI / 6, // 设置旋转角度\n opacity: 0.15, // 设置透明度\n width: 180, // 设置水印宽度\n height: 100, // 设置水印高度\n backgroundRepeat: 'space', // 设置重复方式\n backgroundPosition: 'center', // 设置位置\n textAlign: 'center', // 设置文本对齐\n textBaseline: 'middle', // 设置基线对齐\n },\n ],\n});\n```\n\n## 实际案例\n\n- [文本水印](/examples/plugin/watermark/#text)\n- [图片水印](/examples/plugin/watermark/#repeat)\n\n\n\n---\ntitle: Custom Palette\norder: 4\n---\n\n## Overview\n\n[Core Concepts - Palette](/en/manual/core-concept/palette) mentions that G6 supports discrete palettes and continuous palettes. A discrete palette is essentially an array of colors, while a continuous palette is a color interpolator.\n\nTherefore, customizing a palette also adopts these two methods, and the following sections will introduce how to customize discrete and continuous palettes, respectively.\n\n## Implement Palette\n\n### Discrete Palette\n\nYou can simply define a string array that contains color values. Supported color values include: RGB color values, hexadecimal color values, and color names. Below is an example of a discrete palette:\n\n```typescript\nconst hex = ['#FF0000', '#00FF00', '#0000FF'];\n\nconst color = ['red', 'green', 'blue'];\n\nconst rgb = ['rgb(255, 0, 0)', 'rgb(0, 255, 0)', 'rgb(0, 0, 255)'];\n```\n\n### Continuous Palette\n\nA continuous palette requires the definition of a color interpolator. The interpolator is a function that accepts a numerical value as a parameter and returns a color value. Below is an example of a continuous palette:\n\n```typescript\nconst color = (value: number) => `rgb(${value * 255}, 0, 0)`;\n```\n\n## Register Palette\n\nYou can register a palette using the `register` method provided by G6. For more details, please refer to [Register Palette](/en/manual/core-concept/palette#register-palette)\n\n## Use Without Registration\n\nIn addition to registration, you can also bypass the registration mechanism and directly pass the palette value at the location where the palette is needed, for example:\n\n```typescript\n{\n node: {\n palette: {\n type: 'group',\n field: 'category',\n color: ['#5B8FF9', '#61DDAA', '#F6BD16'], // Pass in a color array.\n }\n },\n edge: {\n palette: {\n type: 'value',\n field: 'value',\n color: (value) => `rgb(${value * 255}, 0, 0)`, // Pass in an interpolator\n }\n }\n}\n```\n\n\n\n---\ntitle: 自定义色板\norder: 4\n---\n\n## 概述\n\n[色板](/manual/theme/palette) 中提到,G6 支持离散色板和连续色板,其中离散色板本质上是一个颜色数组,而连续色板是一个颜色插值器。\n\n因此自定义色板也采用这两种方式,下面分别介绍如何自定义离散色板和连续色板。\n\n## 实现色板\n\n### 离散色板\n\n直接定义一个包含颜色值的字符串数组即可,颜值值支持:RGB 色值、16 进制、颜色名,下面是一组离散色板示例:\n\n```typescript\nconst hex = ['#FF0000', '#00FF00', '#0000FF'];\n\nconst color = ['red', 'green', 'blue'];\n\nconst rgb = ['rgb(255, 0, 0)', 'rgb(0, 255, 0)', 'rgb(0, 0, 255)'];\n```\n\n### 连续色板\n\n连续色板需要定义一个颜色插值器,插值器是一个函数,接受一个数值参数,返回一个颜色值,下面是一个连续色板示例:\n\n```typescript\nconst color = (value: number) => `rgb(${value * 255}, 0, 0)`;\n```\n\n## 注册色板\n\n通过 G6 提供的 register 方法注册即可,详见[注册色板](/manual/theme/palette#注册色板)\n\n## 非注册方式使用\n\n除此之外,你也可以在需要使用色板的位置跳过注册机制直接传入色板值,例如:\n\n```typescript\n{\n node: {\n palette: {\n type: 'group',\n field: 'category',\n color: ['#5B8FF9', '#61DDAA', '#F6BD16'], // 传入颜色数组\n }\n },\n edge: {\n palette: {\n type: 'value',\n field: 'value',\n color: (value) => `rgb(${value * 255}, 0, 0)`, // 传入插值器\n }\n }\n}\n```\n\n\n\n---\ntitle: Custom Theme\norder: 2\n---\n\n## Overview\n\nIn G6, the theme is a subset of Graph Options and includes configurations related to the canvas and element styles. A theme can help you quickly switch between different graph styles.\n\n## Custom Theme\n\nFor element styles, the configurations within a theme are static and do not support the use of callback functions to dynamically calculate styles. Additionally, `type` is also not supported for configuration within a theme. A theme includes the following configurations:\n\n- `background`: Canvas background color\n- `node`: Node style\n- `edge`: Edge style\n- `combo`: Combo style\n\nBelow is a simple example of a theme configuration:\n\n```typescript\nconst theme = {\n background: '#fff',\n node: {\n style: {\n fill: '#e1f3fe',\n lineWidth: 0,\n },\n selected: {\n style: {\n fill: '#3b71d6',\n lineWidth: 1,\n },\n },\n },\n edge: {\n // ...\n },\n combo: {\n // ...\n },\n};\n```\n\n❌ Incorrect Example\n\n```typescript\nconst theme = {\n node: {\n // ❌ The theme does not support configuring element types\n type: 'rect',\n style: {\n // ❌ The theme does not support callback functions\n fill: (d) => d.style.color,\n },\n },\n};\n```\n\n:::warning{title=Note}\nFor element state styles, please ensure that every property in the state style has a corresponding default style in the default style, otherwise it may result in the inability to clear the state style.\n:::\n\n## Register Theme\n\nYou can register a theme using the `register` method provided by G6. Here is an example:\n\n```typescript\nimport { register, ExtensionCategory } from '@antv/g6';\n\nregister(ExtensionCategory.THEME, 'custom-theme', theme);\n```\n\n## Configure Theme\n\nTo enable and configure a theme, you need to pass the `theme` option when instantiating the `Graph`:\n\n```typescript\n{\n theme: 'custom-theme',\n}\n```\n\n### Switch Theme\n\nAfter the `Graph` instance is created, you can switch themes by using the [setTheme](/en/api/theme#graphsetthemetheme) method:\n\n```typescript\ngraph.setTheme('dark');\n```\n\nAdditionally, you can also obtain the current theme by using the `getTheme` method:\n\n```typescript\ngraph.getTheme();\n// => 'dark'\n```\n\n\n\n---\ntitle: 自定义主题\norder: 2\n---\n\n除了使用内置主题外,G6 还支持创建自定义主题来满足特定的视觉需求。本文将介绍如何创建和使用自定义主题。\n\n## 创建自定义主题\n\n一个自定义主题需要遵循主题的基本结构,包含画布背景色和元素样式配置:\n\n```javascript\nconst customTheme = {\n // 1. 画布背景色\n background: '#f0f0f0',\n\n // 2. 节点配置\n node: {\n // 调色板配置\n palette: {\n type: 'group',\n color: ['#1783FF', '#00C9C9' /* 自定义颜色... */],\n },\n // 基础样式\n style: {\n fill: '#fff',\n stroke: '#d9d9d9',\n lineWidth: 1,\n // ... 其他节点样式\n },\n // 状态样式\n state: {\n selected: {\n fill: '#e8f3ff',\n stroke: '#1783FF',\n },\n // ... 其他状态样式\n },\n },\n\n // 3. 边配置\n edge: {\n style: {\n stroke: '#d9d9d9',\n lineWidth: 1,\n // ... 其他边样式\n },\n state: {\n // ... 状态样式\n },\n },\n\n // 4. Combo 配置\n combo: {\n style: {\n fill: '#f7f7f7',\n stroke: '#d9d9d9',\n // ... 其他 Combo 样式\n },\n state: {\n // ... 状态样式\n },\n },\n};\n```\n\n## 使用限制\n\n在创建自定义主题时,需要注意以下限制:\n\n1. **仅支持静态值**\n\n ```javascript\n // ❌ 错误示例:不支持回调函数\n const theme = {\n node: {\n style: {\n fill: (d) => d.style.color,\n },\n },\n };\n ```\n\n2. **不支持配置元素类型**\n\n ```javascript\n // ❌ 错误示例:不支持在主题中配置元素类型\n const theme = {\n node: {\n type: 'rect',\n style: {\n fill: '#fff',\n },\n },\n };\n ```\n\n3. **状态样式需要对应默认样式**\n ```javascript\n // ✅ 正确示例:状态样式的属性在默认样式中都有定义\n const theme = {\n node: {\n style: {\n fill: '#fff',\n stroke: '#000',\n },\n state: {\n selected: {\n fill: '#e8f3ff',\n stroke: '#1783FF',\n },\n },\n },\n };\n ```\n\n## 应用自定义主题\n\n先注册主题,然后通过名称引用:\n\n```javascript\n// 1. 注册主题\nimport { register, ExtensionCategory } from '@antv/g6';\nregister(ExtensionCategory.THEME, 'custom-theme', customTheme);\n\n// 2. 使用主题\nconst graph = new Graph({\n theme: 'custom-theme',\n // ... 其他配置\n});\n```\n\n\n\n---\ntitle: Theme Overview\norder: 1\n---\n\n## Overview\n\n\n\n---\ntitle: 主题总览\norder: 1\n---\n\n## 概述\n\nG6 中的主题是 Graph Options 的子集,它包含了关于画布和元素样式的配置。多主题可以帮助你快速地切换不同的图样式。\n\n\n\n## 主题结构\n\n一个主题由以下四个部分组成:\n\n1. **画布背景色 (background)**\n\n - 控制整个画布的背景颜色\n\n2. **节点配置 (node)**\n\n - 基础样式:填充色、描边、标签等静态视觉属性\n - [调色板](/manual/theme/palette):用于节点分组的颜色配置\n - 状态样式:不同状态下的样式配置(选中、激活、禁用等)\n - 动画配置:节点的动画效果配置\n\n3. **边配置 (edge)**\n\n - 基础样式:线条样式、箭头、标签等静态视觉属性\n - [调色板](/manual/theme/palette):用于边分组的颜色配置\n - 状态样式:不同状态下的样式配置\n - 动画配置:边的动画效果配置\n\n4. **Combo 配置 (combo)**\n - 基础样式:填充、描边、折叠按钮等静态视觉属性\n - 状态样式:不同状态下的样式配置\n - 动画配置:Combo 的动画效果配置\n\n> 注意:主题中的样式配置仅支持静态值,不支持回调函数形式的动态配置。如需动态样式,请使用图的配置项。\n\n## 内置主题\n\nG6 默认提供两种内置主题:\n\n### 亮色主题(默认)\n\n\"亮色主题\"\n\n
查看亮色主题完整配置项\n\n```js\nconst lightTheme = {\n background: '#ffffff',\n node: {\n palette: {\n type: 'group',\n color: [\n '#1783FF',\n '#00C9C9',\n '#F08F56',\n '#D580FF',\n '#7863FF',\n '#DB9D0D',\n '#60C42D',\n '#FF80CA',\n '#2491B3',\n '#17C76F',\n ],\n },\n style: {\n donutOpacity: 1,\n badgeBackgroundOpacity: 1,\n badgeFill: '#fff',\n badgeFontSize: 8,\n badgePadding: [0, 4],\n badgePalette: ['#7E92B5', '#F4664A', '#FFBE3A'],\n fill: '#1783ff',\n fillOpacity: 1,\n halo: false,\n iconFill: '#fff',\n iconOpacity: 1,\n labelBackground: false,\n labelBackgroundFill: '#ffffff',\n labelBackgroundLineWidth: 0,\n labelBackgroundOpacity: 0.75,\n labelFill: '#000000',\n labelFillOpacity: 0.85,\n labelLineHeight: 16,\n labelPadding: [0, 2],\n labelFontSize: 12,\n labelFontWeight: 400,\n labelOpacity: 1,\n labelOffsetY: 2,\n lineWidth: 0,\n portFill: '#1783ff',\n portLineWidth: 1,\n portStroke: '#000000',\n portStrokeOpacity: 0.65,\n size: 32,\n stroke: '#000000',\n strokeOpacity: 1,\n zIndex: 2,\n },\n state: {\n selected: {\n halo: true,\n haloLineWidth: 24,\n haloStrokeOpacity: 0.25,\n labelFontSize: 12,\n labelFontWeight: 'bold',\n lineWidth: 4,\n stroke: '#000000',\n },\n active: {\n halo: true,\n haloLineWidth: 12,\n haloStrokeOpacity: 0.15,\n },\n highlight: {\n labelFontWeight: 'bold',\n lineWidth: 4,\n stroke: '#000000',\n strokeOpacity: 0.85,\n },\n inactive: {\n badgeBackgroundOpacity: 0.25,\n donutOpacity: 0.25,\n fillOpacity: 0.25,\n iconOpacity: 0.85,\n labelFill: '#000000',\n labelFillOpacity: 0.25,\n strokeOpacity: 0.25,\n },\n disabled: {\n badgeBackgroundOpacity: 0.25,\n donutOpacity: 0.06,\n fill: '#1B324F',\n fillOpacity: 0.06,\n iconFill: '#1B324F',\n iconOpacity: 0.25,\n labelFill: '#000000',\n labelFillOpacity: 0.25,\n strokeOpacity: 0.06,\n },\n },\n animation: {\n enter: 'fade',\n exit: 'fade',\n show: 'fade',\n hide: 'fade',\n expand: 'node-expand',\n collapse: 'node-collapse',\n update: [{ fields: ['x', 'y', 'fill', 'stroke'] }],\n translate: [{ fields: ['x', 'y'] }],\n },\n },\n edge: {\n palette: {\n type: 'group',\n color: [\n '#99ADD1',\n '#1783FF',\n '#00C9C9',\n '#F08F56',\n '#D580FF',\n '#7863FF',\n '#DB9D0D',\n '#60C42D',\n '#FF80CA',\n '#2491B3',\n '#17C76F',\n ],\n },\n style: {\n badgeBackgroundFill: '#99ADD1',\n badgeFill: '#fff',\n badgeFontSize: 8,\n badgeOffsetX: 10,\n fillOpacity: 1,\n halo: false,\n haloLineWidth: 12,\n haloStrokeOpacity: 1,\n increasedLineWidthForHitTesting: 2,\n labelBackground: false,\n labelBackgroundFill: '#ffffff',\n labelBackgroundLineWidth: 0,\n labelBackgroundOpacity: 0.75,\n labelBackgroundPadding: [4, 4, 4, 4],\n labelFill: '#000000',\n labelFontSize: 12,\n labelFontWeight: 400,\n labelOpacity: 1,\n labelPlacement: 'center',\n labelTextBaseline: 'middle',\n lineWidth: 1,\n stroke: '#99ADD1',\n strokeOpacity: 1,\n zIndex: 1,\n },\n state: {\n selected: {\n halo: true,\n haloStrokeOpacity: 0.25,\n labelFontSize: 14,\n labelFontWeight: 'bold',\n lineWidth: 3,\n },\n active: {\n halo: true,\n haloStrokeOpacity: 0.15,\n },\n highlight: {\n labelFontWeight: 'bold',\n lineWidth: 3,\n },\n inactive: {\n stroke: '#1B324F',\n fillOpacity: 0.08,\n labelOpacity: 0.25,\n strokeOpacity: 0.08,\n badgeBackgroundOpacity: 0.25,\n },\n disabled: {\n stroke: '#d9d9d9',\n fillOpacity: 0.45,\n strokeOpacity: 0.45,\n labelOpacity: 0.25,\n badgeBackgroundOpacity: 0.45,\n },\n },\n animation: {\n enter: 'fade',\n exit: 'fade',\n expand: 'path-in',\n collapse: 'path-out',\n show: 'fade',\n hide: 'fade',\n update: [{ fields: ['sourceNode', 'targetNode'] }, { fields: ['stroke'], shape: 'key' }],\n translate: [{ fields: ['sourceNode', 'targetNode'] }],\n },\n },\n combo: {\n style: {\n collapsedMarkerFill: '#ffffff',\n collapsedMarkerFontSize: 12,\n collapsedMarkerFillOpacity: 1,\n collapsedSize: 32,\n collapsedFillOpacity: 1,\n fill: '#99ADD1',\n halo: false,\n haloLineWidth: 12,\n haloStroke: '#99ADD1',\n haloStrokeOpacity: 0.25,\n labelBackground: false,\n labelBackgroundFill: '#ffffff',\n labelBackgroundLineWidth: 0,\n labelBackgroundOpacity: 0.75,\n labelBackgroundPadding: [2, 4, 2, 4],\n labelFill: '#000000',\n labelFontSize: 12,\n labelFontWeight: 400,\n labelOpacity: 1,\n lineDash: 0,\n lineWidth: 1,\n fillOpacity: 0.04,\n strokeOpacity: 1,\n padding: 10,\n stroke: '#99ADD1',\n },\n state: {\n selected: {\n halo: true,\n labelFontSize: 14,\n labelFontWeight: 700,\n lineWidth: 4,\n },\n active: {\n halo: true,\n },\n highlight: {\n labelFontWeight: 700,\n lineWidth: 4,\n },\n inactive: {\n fillOpacity: 0.65,\n labelOpacity: 0.25,\n strokeOpacity: 0.65,\n },\n disabled: {\n fill: '#d9d9d9',\n fillOpacity: 0.25,\n labelOpacity: 0.25,\n stroke: '#d9d9d9',\n strokeOpacity: 0.25,\n },\n },\n animation: {\n enter: 'fade',\n exit: 'fade',\n show: 'fade',\n hide: 'fade',\n expand: 'combo-expand',\n collapse: 'combo-collapse',\n update: [{ fields: ['x', 'y'] }, { fields: ['fill', 'stroke', 'lineWidth'], shape: 'key' }],\n translate: [{ fields: ['x', 'y'] }],\n },\n },\n};\n```\n\n
\n\n### 暗色主题\n\n\"暗色主题\"\n\n
查看暗色主题完整配置项\n\n```js\nconst darkTheme = {\n background: '#000000',\n node: {\n palette: {\n type: 'group',\n color: [\n '#1783FF',\n '#00C9C9',\n '#F08F56',\n '#D580FF',\n '#7863FF',\n '#DB9D0D',\n '#60C42D',\n '#FF80CA',\n '#2491B3',\n '#17C76F',\n ],\n },\n style: {\n donutOpacity: 1,\n badgeBackgroundOpacity: 1,\n badgeFill: '#fff',\n badgeFontSize: 8,\n badgePadding: [0, 4],\n badgePalette: ['#7E92B5', '#F4664A', '#FFBE3A'],\n fill: '#1783ff',\n fillOpacity: 1,\n halo: false,\n iconFill: '#fff',\n iconOpacity: 1,\n labelBackground: false,\n labelBackgroundFill: '#000000',\n labelBackgroundLineWidth: 0,\n labelBackgroundOpacity: 0.75,\n labelFill: '#ffffff',\n labelFillOpacity: 0.85,\n labelLineHeight: 16,\n labelPadding: [0, 2],\n labelFontSize: 12,\n labelFontWeight: 400,\n labelOpacity: 1,\n labelOffsetY: 2,\n lineWidth: 0,\n portFill: '#1783ff',\n portLineWidth: 1,\n portStroke: '#d0e4ff',\n portStrokeOpacity: 0.65,\n size: 32,\n stroke: '#d0e4ff',\n strokeOpacity: 1,\n zIndex: 2,\n },\n state: {\n selected: {\n halo: true,\n haloLineWidth: 24,\n haloStrokeOpacity: 0.45,\n labelFontSize: 12,\n labelFontWeight: 'bold',\n lineWidth: 4,\n stroke: '#d0e4ff',\n },\n active: {\n halo: true,\n haloLineWidth: 12,\n haloStrokeOpacity: 0.25,\n },\n highlight: {\n labelFontWeight: 'bold',\n lineWidth: 4,\n stroke: '#d0e4ff',\n strokeOpacity: 0.85,\n },\n inactive: {\n badgeBackgroundOpacity: 0.45,\n donutOpacity: 0.45,\n fillOpacity: 0.45,\n iconOpacity: 0.45,\n labelFill: '#ffffff',\n labelFillOpacity: 0.45,\n strokeOpacity: 0.45,\n },\n disabled: {\n badgeBackgroundOpacity: 0.25,\n donutOpacity: 0.25,\n fill: '#D0E4FF',\n fillOpacity: 0.25,\n iconFill: '#D0E4FF',\n iconOpacity: 0.25,\n labelFill: '#ffffff',\n labelFillOpacity: 0.25,\n strokeOpacity: 0.25,\n },\n },\n animation: {\n enter: 'fade',\n exit: 'fade',\n show: 'fade',\n hide: 'fade',\n expand: 'node-expand',\n collapse: 'node-collapse',\n update: [{ fields: ['x', 'y', 'fill', 'stroke'] }],\n translate: [{ fields: ['x', 'y'] }],\n },\n },\n edge: {\n palette: {\n type: 'group',\n color: [\n '#637088',\n '#0F55A6',\n '#008383',\n '#9C5D38',\n '#8B53A6',\n '#4E40A6',\n '#8F6608',\n '#3E801D',\n '#A65383',\n '#175E75',\n '#0F8248',\n ],\n },\n style: {\n badgeBackgroundFill: '#637088',\n badgeFill: '#fff',\n badgeFontSize: 8,\n badgeOffsetX: 10,\n fillOpacity: 1,\n halo: false,\n haloLineWidth: 12,\n haloStrokeOpacity: 1,\n increasedLineWidthForHitTesting: 2,\n labelBackground: false,\n labelBackgroundFill: '#000000',\n labelBackgroundLineWidth: 0,\n labelBackgroundOpacity: 0.75,\n labelBackgroundPadding: [4, 4, 4, 4],\n labelFill: '#ffffff',\n labelFontSize: 12,\n labelFontWeight: 400,\n labelOpacity: 1,\n labelPlacement: 'center',\n labelTextBaseline: 'middle',\n lineWidth: 1,\n stroke: '#637088',\n strokeOpacity: 1,\n zIndex: 1,\n },\n state: {\n selected: {\n halo: true,\n haloStrokeOpacity: 0.25,\n labelFontSize: 14,\n labelFontWeight: 'bold',\n lineWidth: 3,\n },\n active: {\n halo: true,\n haloStrokeOpacity: 0.15,\n },\n highlight: {\n labelFontWeight: 'bold',\n lineWidth: 3,\n },\n inactive: {\n stroke: '#D0E4FF',\n fillOpacity: 0.08,\n labelOpacity: 0.25,\n strokeOpacity: 0.08,\n badgeBackgroundOpacity: 0.25,\n },\n disabled: {\n stroke: '#637088',\n fillOpacity: 0.45,\n strokeOpacity: 0.45,\n labelOpacity: 0.25,\n badgeBackgroundOpacity: 0.45,\n },\n },\n animation: {\n enter: 'fade',\n exit: 'fade',\n expand: 'path-in',\n collapse: 'path-out',\n show: 'fade',\n hide: 'fade',\n update: [{ fields: ['sourceNode', 'targetNode'] }, { fields: ['stroke'], shape: 'key' }],\n translate: [{ fields: ['sourceNode', 'targetNode'] }],\n },\n },\n combo: {\n style: {\n collapsedMarkerFill: '#000000',\n collapsedMarkerFontSize: 12,\n collapsedMarkerFillOpacity: 1,\n collapsedSize: 32,\n collapsedFillOpacity: 1,\n fill: '#fdfdfd',\n halo: false,\n haloLineWidth: 12,\n haloStroke: '#99add1',\n haloStrokeOpacity: 0.25,\n labelBackground: false,\n labelBackgroundFill: '#000000',\n labelBackgroundLineWidth: 0,\n labelBackgroundOpacity: 0.75,\n labelBackgroundPadding: [2, 4, 2, 4],\n labelFill: '#ffffff',\n labelFontSize: 12,\n labelFontWeight: 400,\n labelOpacity: 1,\n lineDash: 0,\n lineWidth: 1,\n fillOpacity: 0.04,\n strokeOpacity: 1,\n padding: 10,\n stroke: '#99add1',\n },\n state: {\n selected: {\n halo: true,\n labelFontSize: 14,\n labelFontWeight: 700,\n lineWidth: 4,\n },\n active: {\n halo: true,\n },\n highlight: {\n labelFontWeight: 700,\n lineWidth: 4,\n },\n inactive: {\n fillOpacity: 0.65,\n labelOpacity: 0.25,\n strokeOpacity: 0.65,\n },\n disabled: {\n fill: '#d0e4ff',\n fillOpacity: 0.25,\n labelOpacity: 0.25,\n stroke: '#969696',\n strokeOpacity: 0.25,\n },\n },\n animation: {\n enter: 'fade',\n exit: 'fade',\n show: 'fade',\n hide: 'fade',\n expand: 'combo-expand',\n collapse: 'combo-collapse',\n update: [{ fields: ['x', 'y'] }, { fields: ['fill', 'stroke', 'lineWidth'], shape: 'key' }],\n translate: [{ fields: ['x', 'y'] }],\n },\n },\n};\n```\n\n
\n\n## 使用主题\n\n### 配置主题\n\n在创建图时通过 `theme` 选项指定要使用的主题:\n\n```javascript\nconst graph = new Graph({\n theme: 'light', // 或 'dark'\n // ... 其他配置\n});\n```\n\n### 切换主题\n\n创建图后,可以通过 `setTheme` 方法动态切换主题:\n\n```javascript\n// 切换到暗色主题\ngraph.setTheme('dark');\n\n// 获取当前主题\nconst currentTheme = graph.getTheme(); // 'dark'\n```\n\n## 样式优先级\n\n在 G6 中,元素的最终样式由多个层级的样式合并而成,按优先级从低到高排序:\n\n**⭐️ 主题默认样式** < 调色板样式 < 数据样式 < 图的默认样式 < **⭐️ 主题状态样式** < 图的状态样式\n\n详细说明:\n\n1. **主题默认样式**:主题系统提供的基础样式\n2. **调色板样式**:基于主题调色板配置的自动着色样式\n3. **数据样式**:在数据中定义的样式\n4. **图的默认样式**:通过图的配置项设置的样式\n5. **主题状态样式**:主题中定义的状态样式\n6. **图的状态样式**:通过图的配置项设置的状态样式\n\n更多关于自定义主题的内容,请参考[自定义主题](/manual/theme/custom-theme)。\n\n\n\n---\ntitle: Palette\norder: 3\n---\n\n## Overview\n\nA palette refers to a set of predefined color collections that help users more conveniently select colors. In G6, a palette is a common option that allows users to configure the colors of elements such as nodes, edges, and links through the palette.\n\nPalettes are divided into two types: `discrete palette` and `continuous palette`.\n\nA discrete palette is an array of colors used to map discrete values within elements to different colors, such as the type of nodes, the relationship of edges, etc. Below is a simple example of a discrete palette:\n\n```typescript\n['#5B8FF9', '#61DDAA', '#F6BD16', '#F6903D', '#F08BB4'];\n```\n\nA continuous palette is an interpolator that takes a value between 0 and 1 and returns the corresponding color. It is used to map continuous values within elements to different colors, such as the degree of nodes, the weight of edges, etc. Below is a simple example of a continuous palette:\n\n```typescript\n(value: number) => `rgb(${value * 255}, 0, 0)`;\n```\n\n## Register Palette\n\nYou can directly use the built-in palettes, but if you want to use other palettes, you need to register them first:\n\n```typescript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { CustomPalette } from 'package-name/or/path-to-your-custom-palette';\n\nregister(ExtensionCategory.PALETTE, 'custom-palette', CustomPalette);\n```\n\n:::warning{title=note}\n\nDuring the process of registering a palette, there is no distinction made between discrete and continuous palettes. It is necessary to ensure the consistency between the palette type and the data type when using the palette.\n:::\n\n### Built-in Palettes\n\nCurrently, G6 has 5 sets of commonly used discrete palettes that users can directly utilize:\n\n- spectral\n\n
\n\n- tableau\n\n
\n\n- oranges\n\n
\n\n- greens\n\n
\n\n- blues\n\n
\n\n## Configure Palette\n\nCurrently, the configuration of palettes is mainly focused on elements, taking nodes as an example:\n\n### Discrete Palette\n\n1. Default Configuration: By directly setting the value of `palette` to the name of the palette, each node will be assigned a different color by default\n\n```typescript\n{\n node: {\n palette: 'spectral', // spectral is the Palette Name\n }\n}\n```\n\n\n\n> When the number of elements exceeds the number of colors in the palette, the colors in the palette will be reused in a cyclic manner.\n\n2. Standard Configuration: The attributes for configuring a discrete palette include: `type: 'group'`, `field`, `color`, `invert`.\n\nAmong them, `type: 'group'` explicitly specifies that the current palette type is a discrete palette; `field` designates the field for grouping in the element data; `color` is the name of the palette; `invert` indicates whether to invert the palette.\n\nGiven a set of example data:\n\n```json\n{\n \"nodes\": [\n { \"id\": \"node-1\", \"data\": { \"category\": \"A\" } },\n { \"id\": \"node-2\", \"data\": { \"category\": \"B\" } },\n { \"id\": \"node-3\", \"data\": { \"category\": \"C\" } },\n { \"id\": \"node-4\", \"data\": { \"category\": \"A\" } },\n { \"id\": \"node-5\", \"data\": { \"category\": \"B\" } },\n { \"id\": \"node-6\", \"data\": { \"category\": \"C\" } }\n ]\n}\n```\n\nIn the data, `node-1` and `node-4` belong to category A, `node-2` and `node-5` belong to category B, `node-3` and `node-6` belong to category C.\n\nConfigure the color of the nodes in such a way that nodes of the same category have the same color:\n\n```typescript\n{\n node: {\n palette: {\n type: 'group', // Specify the palette type as a categorical palette.\n field: 'category', // Specify the grouping field in the data.\n color: 'tableau', // Use a Tableau-like palette.\n }\n }\n}\n```\n\n\n\n### Continuous Palette\n\nA continuous palette only supports standard configuration methods, with configuration properties including: `type: 'value'`, `field`, `color`, `invert`.\n\nGiven a set of example data:\n\n```json\n{\n \"nodes\": [\n { \"id\": \"node-1\", \"data\": { \"value\": 0 } },\n { \"id\": \"node-2\", \"data\": { \"value\": 20 } },\n { \"id\": \"node-3\", \"data\": { \"value\": 40 } },\n { \"id\": \"node-4\", \"data\": { \"value\": 60 } },\n { \"id\": \"node-5\", \"data\": { \"value\": 80 } },\n { \"id\": \"node-6\", \"data\": { \"value\": 100 } }\n ]\n}\n```\n\nNow, create an interpolator that maps the maximum value to red (`rgb(255, 0, 0)`) and the minimum value to black (`rgb(0, 0, 0)`):\n\n```typescript\n(value) => `rgb(${value * 255}, 0, 0)`;\n```\n\nConfigure the following so that the color of the nodes is mapped to different colors based on the value of the `value` field in the data:\n\n```typescript\n{\n node: {\n palette: {\n type: 'value', // Specify the palette type as a continuous palette\n field: 'value', // Specify the numerical field in the data\n color: (value) => `rgb(${value * 255}, 0, 0)`, // Use an interpolator\n }\n }\n}\n```\n\n\n\n:::warning{title=note}\n\nThe built-in continuous palette does not support specifying a value range. If there is a need for more complex color mapping, it can be customized within the style mapping.\n:::\n\n## Custom Palette\n\nIf the built-in palette does not meet your requirements, you can customize the palette. For details, please refer to [Custom Palette](/en/manual/custom-extension/palette).\n\n## Priority\n\nThe palette generates styles based on the type of element. For nodes and combos, the color is mapped to the `fill` attribute; for edges, the color is mapped to the `stroke` attribute.\n\nIf both a palette and a style mapping are configured, the style mapping will override the palette colors. In the following example, the color of the nodes is always red:\n\n```typescript\n{\n node: {\n style: {\n fill: 'red',\n },\n palette: 'spectral',\n }\n}\n```\n\n\n\n---\ntitle: 色板\norder: 3\n---\n\n## 概述\n\n色板(Palette)是指一组预定义的颜色集合,用于帮助用户更方便的选择颜色。在 G6 中,色板是一种常见的配置项,用户可以通过色板来配置节点、边、连线等元素的颜色。\n\n色板分为`离散色板`和`连续色板`两种类型。\n\n离散色板是一组颜色数组,用于将元素中的离散值映射到不同的颜色上,例如节点的类型、边的关系等。下面是一个简单的离散色板示例:\n\n```typescript\n['#5B8FF9', '#61DDAA', '#F6BD16', '#F6903D', '#F08BB4'];\n```\n\n连续色板是一个插值器,输入 0~1 的值,返回对应的颜色,用于将元素中的连续值映射到不同的颜色上,例如节点的度数、边的权重等。下面是一个简单的连续色板示例:\n\n```typescript\n(value: number) => `rgb(${value * 255}, 0, 0)`;\n```\n\n## 注册色板\n\n你可以直接使用内置色板,如果想使用其他色板,需要先进行注册:\n\n```typescript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { CustomPalette } from 'package-name/or/path-to-your-custom-palette';\n\nregister(ExtensionCategory.PALETTE, 'custom-palette', CustomPalette);\n```\n\n:::warning{title=注意}\n在注册色板过程中并不会区分离散色板和连续色板,使用色板过程中需要自行保证色板类型和数据类型的一致性。\n:::\n\n### 内置色板\n\n目前 G6 内置了 5 套常用的离散色板,用户可以直接使用:\n\n- spectral\n\n
\n\n- tableau\n\n
\n\n- oranges\n\n
\n\n- greens\n\n
\n\n- blues\n\n
\n\n## 配置色板\n\n目前开放色板配置的地方主要以元素为主,以节点为例:\n\n### 离散色板\n\n1. 默认配置,直接配置 `palette` 的值为色板名,会默认为每个节点分配不同的颜色\n\n```typescript\n{\n node: {\n palette: 'spectral', // spectral 为色板名\n }\n}\n```\n\n\n\n> 当元素数量超过色板颜色数量时,会循环使用色板中的颜色\n\n2. 标准配置,离散色板配置属性包括:`type: 'group'`,`field`,`color`,`invert`\n\n其中 `type: 'group'` 显式指定了当前色板类型为离散色板;`field` 指定元素数据中的分组字段;`color` 为色板名;`invert` 为是否反转色板。\n\n给定一组示例数据:\n\n```json\n{\n \"nodes\": [\n { \"id\": \"node-1\", \"data\": { \"category\": \"A\" } },\n { \"id\": \"node-2\", \"data\": { \"category\": \"B\" } },\n { \"id\": \"node-3\", \"data\": { \"category\": \"C\" } },\n { \"id\": \"node-4\", \"data\": { \"category\": \"A\" } },\n { \"id\": \"node-5\", \"data\": { \"category\": \"B\" } },\n { \"id\": \"node-6\", \"data\": { \"category\": \"C\" } }\n ]\n}\n```\n\n数据中 `node-1`,`node-4` 属于 A 类别,`node-2`,`node-5` 属于 B 类别,`node-3`,`node-6` 属于 C 类别。\n\n通过以下方式配置节点的颜色,使得同类别的节点颜色相同:\n\n```typescript\n{\n node: {\n palette: {\n type: 'group', // 指定色板类型为分类色板\n field: 'category', // 指定数据中的分组字段\n color: 'tableau', // 使用 tableau 色板\n }\n }\n}\n```\n\n\n\n### 连续色板\n\n连续色板只支持标准方式配置,配置属性包括:`type: 'value'`,`field`,`color`,`invert`。\n\n给定一组示例数据:\n\n```json\n{\n \"nodes\": [\n { \"id\": \"node-1\", \"data\": { \"value\": 0 } },\n { \"id\": \"node-2\", \"data\": { \"value\": 20 } },\n { \"id\": \"node-3\", \"data\": { \"value\": 40 } },\n { \"id\": \"node-4\", \"data\": { \"value\": 60 } },\n { \"id\": \"node-5\", \"data\": { \"value\": 80 } },\n { \"id\": \"node-6\", \"data\": { \"value\": 100 } }\n ]\n}\n```\n\n现在创建一个插值器,将最大值映射为红色(`rgb(255, 0, 0)`),最小值映射为黑色(`rgb(0, 0, 0)`):\n\n```typescript\n(value) => `rgb(${value * 255}, 0, 0)`;\n```\n\n通过以下配置使得节点的颜色根据数据中的 `value` 字段的值映射到不同的颜色:\n\n```typescript\n{\n node: {\n palette: {\n type: 'value', // 指定色板类型为连续色板\n field: 'value', // 指定数据中的数值字段\n color: (value) => `rgb(${value * 255}, 0, 0)`, // 使用插值器\n }\n }\n}\n```\n\n\n\n:::warning{title=注意}\n内置连续色板不支持指定值域范围,如果有更复杂的颜色映射需求,可以在样式映射中自定义\n:::\n\n## 自定义色板\n\n如果内置色板无法满足需求,可以自定义色板,具体请参考[自定义色板](/manual/theme/custom-palette)。\n\n## 优先级\n\n色板会基于元素类型生成样式,对于节点和组合,会将颜色映射到 `fill` 属性;对于边,会将颜色映射到 `stroke` 属性。\n\n如果同时配置了色板和样式映射,样式映射会覆盖色板颜色。下面的例子中,节点的颜色始终为红色:\n\n```typescript\n{\n node: {\n style: {\n fill: 'red',\n },\n palette: 'spectral',\n }\n}\n```\n\n\n\n---\ntitle: Custom Transform\norder: 4\n---\n\n\n\n---\ntitle: 自定义数据处理\norder: 4\n---\n\n## 概述\n\n自定义数据处理允许用户在业务实现过程中,把额外的数据处理封装起来,在渲染前或者布局后对数据进行进一步处理。用户通过自定义数据处理,实现部分数据处理解耦,更好地进行管理、编排代码以及提高代码可维护性。\n\n## 开始前\n\n用户在进行自定义数据处理前,需要仔细阅读并掌握数据处理器的 [实现原理和执行时机](/manual/transform/overview#实现原理) 。\n\n## 代码示例\n\n接下来将讲述两个可能的业务场景,并通过自定义数据处理来实现:\n\n### 不展示游离节点\n\n- **需求**\n\n 游离节点,即没有连线的节点,画布渲染时不展示游离节点\n\n- **实现**\n\n ```typescript\n import type { DrawData, DrawContext } from '@antv/g6';\n import { Graph, BaseTransform, register, ExtensionCategory } from '@antv/g6';\n\n class HideFreeNode extends BaseTransform {\n public beforeDraw(input: DrawData, context: DrawContext): DrawData {\n const { model } = this.context;\n const { add, update, remove } = input;\n\n add.nodes.forEach((nodeData, nodeId) => {\n // 获取节点的相关连线\n const edges = model.getRelatedEdgesData(nodeId);\n // 没有任何连线的的节点则从add里面移除,添加到remove里面\n if (!edges.length) {\n add.nodes.delete(nodeId);\n remove.nodes.set(nodeId, nodeData);\n }\n });\n\n return input;\n }\n }\n ```\n\n \n\n- **说明**\n\n 示例中总共有6个节点,id为1-6,id为4的节点没有连线,因此被移除了。\n\n 通过 `getRelatedEdgesData` 获取节点的相关连线,没有则把该节点放到 `remove.nodes` 里面去,并从 `add.nodes` 里面移除。\n\n### 环形布局径向label\n\n- **需求**\n\n 使用 [环形布局](/manual/layout/circular-layout) 时,节点 label 的也需要像内置数据处理器 [PlaceRadialLabels](/manual/transform/place-radial-labels) 一样实现径向展示(但 PlaceRadialLabels 只支持径向布局,环形布局不是径向布局)\n\n- **实现**\n\n ```typescript\n import type { RuntimeContext, DrawContext, Point, TransformArray, Vector2, Vector3 } from '@antv/g6';\n import { Graph, BaseTransform, register, ExtensionCategory, BaseTransformOptions } from '@antv/g6';\n\n // 目前circular布局没有暴露方法可以获取布局中心,这里简单处理先固定一个,配置circular布局时center与这里保持一致即可\n const circularCenter = [300, 300];\n\n // 下面的函数 G6 没有暴露出来,先自行声明\n function subtract(a: Vector2 | Vector3, b: Vector2 | Vector3): Vector2 | Vector3 {\n return a.map((v, i) => v - b[i]) as Vector2 | Vector3;\n }\n function rad(a: Vector2 | Vector3): number {\n const [x, y] = a;\n if (!x && !y) return 0;\n return Math.atan2(y, x);\n }\n function rad2deg(rad: number): number {\n return rad * (180 / Math.PI);\n }\n\n interface CircularRadialLabelsOptions extends BaseTransformOptions {\n offset?: number; // 偏移量\n }\n\n class CircularRadialLabels extends BaseTransform {\n static defaultOptions = {\n offset: 5,\n };\n constructor(context: RuntimeContext, options: CircularRadialLabelsOptions) {\n super(context, Object.assign({}, CircularRadialLabels.defaultOptions, options));\n }\n get center(): Point {\n return circularCenter;\n }\n public afterLayout() {\n const { graph, model } = this.context;\n const data = model.getData();\n data.nodes?.forEach((datum) => {\n const radian = rad(subtract([datum.style.x, datum.style.y], this.center));\n const isLeft = Math.abs(radian) > Math.PI / 2;\n const isLeaf = !datum.children || datum.children.length === 0;\n const nodeId = datum.id;\n const node = this.context.element?.getElement(nodeId);\n if (!node || !node.isVisible()) return;\n\n const nodeHalfWidth = graph.getElementRenderStyle(nodeId).size / 2;\n const offset = (isLeaf ? 1 : -1) * (nodeHalfWidth + this.options.offset);\n\n const labelTransform: TransformArray = [\n ['translate', offset * Math.cos(radian), offset * Math.sin(radian)],\n ['rotate', isLeft ? rad2deg(radian) + 180 : rad2deg(radian)],\n ];\n\n model.updateNodeData([\n {\n id: datum.id,\n style: {\n labelTextAlign: isLeft === isLeaf ? 'right' : 'left',\n labelTextBaseline: 'middle',\n labelTransform,\n },\n },\n ]);\n });\n\n graph.draw();\n }\n }\n ```\n\n \n\n- **说明**\n 上面的实现基本是参考内置数据处理器 [PlaceRadialLabels](/manual/transform/place-radial-labels) 来实现的,区别是这里的实现是通过拿到布局中心来计算偏移和旋转,具体可参考 PlaceRadialLabels 的 [源码](https://github.com/antvis/G6/blob/v5/packages/g6/src/transforms/place-radial-labels.ts)\n\n\n\n---\ntitle: MapNodeSize\norder: 1\n---\n\nIn graph visualization, the size of a node is usually used to convey the importance or influence of the node. By adjusting the size of the node based on the centrality of the node, we can more intuitively show the importance of each node in the network, helping users better understand and analyze complex network structures.\n\n## Options\n\n### centrality\n\n> [NodeCentralityOptions](#nodecentralityoptions) _\\| ((graphData:_ [GraphData](/manual/core-concept/data#图数据graphdata)_) =>_ _Map**<**string, number>)_ **Default:** `type: 'eigenvector'`\n\nThe method of measuring the node centrality\n\n- `'degree'`: Degree centrality, measures centrality by the degree (number of connected edges) of a node. Nodes with high degree centrality usually have more direct connections and may play important roles in the network\n- `'betweenness'`: Betweenness centrality, measures centrality by the number of times a node appears in all shortest paths. Nodes with high betweenness centrality usually act as bridges in the network, controlling the flow of information\n- `'closeness'`: Closeness centrality, measures centrality by the reciprocal of the average shortest path length from a node to all other nodes. Nodes with high closeness centrality usually can reach other nodes in the network more quickly\n- `'eigenvector'`: Eigenvector centrality, measures centrality by the degree of connection between a node and other central nodes. Nodes with high eigenvector centrality usually connect to other important nodes\n- `'pagerank'`: PageRank centrality, measures centrality by the number of times a node is referenced by other nodes, commonly used in directed graphs. Nodes with high PageRank centrality usually have high influence in the network, similar to the page ranking algorithm\n- Custom centrality calculation method: `(graphData: GraphData) => Map`, where `graphData` is the graph data, and `Map` is the mapping from node ID to centrality value\n\n#### NodeCentralityOptions\n\n```typescript\ntype NodeCentralityOptions =\n | { type: 'degree'; direction?: 'in' | 'out' | 'both' }\n | { type: 'betweenness'; directed?: boolean; weightPropertyName?: string }\n | { type: 'closeness'; directed?: boolean; weightPropertyName?: string }\n | { type: 'eigenvector'; directed?: boolean }\n | { type: 'pagerank'; epsilon?: number; linkProb?: number };\n```\n\n### mapLabelSize\n\n> _boolean \\| [number, number]_ **Default:** `false`\n\nWhether to map label size synchronously\n\n### maxSize\n\n> _number \\| [number, number] \\| Float32Array \\| [number, number, number]_ **Default:** `80`\n\nThe maximum size of the node\n\n### minSize\n\n> _number \\| [number, number] \\| Float32Array \\| [number, number, number]_ **Default:** `20`\n\nThe minimum size of the node\n\n### scale\n\n> _'linear' \\| 'log' \\| 'pow' \\| 'sqrt' \\| ((value: number, domain: [number, number], range: [number, number]) => number)_ **Default:** `'log'`\n\nScale type\n\n- `'linear'`: Linear scale, maps a value from one range to another range linearly, commonly used for cases where the difference in centrality values is small\n\n- `'log'`: Logarithmic scale, maps a value from one range to another range logarithmically, commonly used for cases where the difference in centrality values is large\n\n- `'pow'`: Power-law scale, maps a value from one range to another range using power law, commonly used for cases where the difference in centrality values is large\n\n- `'sqrt'`: Square root scale, maps a value from one range to another range using square root, commonly used for cases where the difference in centrality values is large\n\n- Custom scale: `(value: number, domain: [number, number], range: [number, number]) => number`,where `value` is the value to be mapped, `domain` is the input range, and `range` is the output range\n\n\n\n---\ntitle: 动态调整节点大小 MapNodeSize\norder: 1\n---\n\n## 概述\n\n在图可视化中,节点的大小通常用于传达节点的重要性或影响力。通过根据节点中心性调整节点的大小,我们可以更直观地展示网络中各个节点的重要性,从而帮助用户更好地理解和分析复杂的网络结构。\n\n## 使用场景\n\n需要通过节点大小来突出节点的重要性和影响力时,可使用此数据处理。\n\n以下为常见的场景:\n\n- **社交网络分析**:比如分析社交媒体平台中用户的活跃度与影响力,通过节点大小突出高互动用户。\n\n- **金融风险传导网络**:比如识别金融系统中承担关键资金流转职能的机构,预防系统性风险。\n\n- **交通枢纽规划**:比如优化城市地铁网络设计,识别换乘压力点。\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| ------------ | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ---- |\n| type | 数据处理类型 | map-node-size | - | ✓ |\n| centrality | 节点中心性的度量方法,[配置项](#centrality) | [NodeCentralityOptions](#nodecentralityoptions) \\| ((graphData: [GraphData](/manual/data#图数据graphdata)) => Map) | `{ type: 'degree' }` | |\n| mapLabelSize | 是否同步调整标签大小 | boolean \\| [number, number] | false | |\n| maxSize | 节点最大尺寸 | number \\| [number, number] \\| [number, number, number] | 80 | |\n| minSize | 节点最小尺寸 | number \\| [number, number] \\| [number, number, number] | 20 | |\n| scale | 插值函数,用于将节点中心性映射到节点大小,[配置项](#scale) | `linear` \\| `log` \\| `pow` \\| `sqrt` \\| ((value: number, domain: [number, number], range: [number, number]) => number) | `log` | |\n\n### centrality\n\n节点中心性的度量方法\n\n- `'degree'`:度中心性,通过节点的度数(连接的边的数量)来衡量其重要性。度中心性高的节点通常具有较多的直接连接,在网络中可能扮演着重要的角色\n- `'betweenness'`:介数中心性,通过节点在所有最短路径中出现的次数来衡量其重要性。介数中心性高的节点通常在网络中起到桥梁作用,控制着信息的流动\n- `'closeness'`:接近中心性,通过节点到其他所有节点的最短路径长度总和的倒数来衡量其重要性。接近中心性高的节点通常能够更快地到达网络中的其他节点\n- `'eigenvector'`:特征向量中心性,通过节点与其他中心节点的连接程度来衡量其重要性。特征向量中心性高的节点通常连接着其他重要节点\n- `'pagerank'`:PageRank 中心性,通过节点被其他节点引用的次数来衡量其重要性,常用于有向图。PageRank 中心性高的节点通常在网络中具有较高的影响力,类似于网页排名算法\n- 自定义中心性计算方法:`(graphData: GraphData) => Map`,其中 `graphData` 为图数据,`Map` 为节点 ID 到中心性值的映射\n\n**示例:**\n\n```typescript {6-9}\nconst graph = new Graph({\n // 其他配置...\n transforms: [\n {\n type: 'map-node-size',\n centrality: {\n type: 'degree',\n direction: 'both',\n },\n },\n ],\n});\n```\n\n效果如下(可切换度量方法查看不同效果,示例中节点 label 为`${节点 id } - ${节点大小}`):\n\n\n\n#### NodeCentralityOptions\n\n```typescript\ntype NodeCentralityOptions =\n | { type: 'degree'; direction?: 'in' | 'out' | 'both' }\n | { type: 'betweenness'; directed?: boolean; weightPropertyName?: string }\n | { type: 'closeness'; directed?: boolean; weightPropertyName?: string }\n | { type: 'eigenvector'; directed?: boolean }\n | { type: 'pagerank'; epsilon?: number; linkProb?: number };\n```\n\n`direction`:表示统计哪些方向的边,`in` -入边、 `out` -出边、 `both` -入边和出边都考虑进去\n\n`directed`:是否为有向图\n\n`weightPropertyName`:边的权重属性名\n\n`epsilon`:PageRank 算法的收敛容差\n\n`linkProb`:PageRank 算法的阻尼系数,指任意时刻,用户访问到某节点后继续访问该节点链接的下一个节点的概率,经验值 0.85\n\n### scale\n\n- `'linear'`:线性插值函数,将一个值从一个范围线性映射到另一个范围,常用于处理中心性值的差异较小的情况\n- `'log'`:对数插值函数,将一个值从一个范围对数映射到另一个范围,常用于处理中心性值的差异较大的情况\n- `'pow'`:幂律插值函数,将一个值从一个范围幂律映射到另一个范围,常用于处理中心性值的差异较大的情况\n- `'sqrt'`:平方根插值函数,将一个值从一个范围平方根映射到另一个范围,常用于处理中心性值的差异较大的情况\n- 自定义插值函数:`(value: number, domain: [number, number], range: [number, number]) => number`,其中 `value` 为需要映射的值,`domain` 为输入值的范围,`range` 为输出值的范围\n\n**示例:**\n\n```typescript {9}\nconst graph = new Graph({\n // 其他配置...\n transforms: [\n {\n type: 'map-node-size',\n centrality: {\n type: 'degree',\n },\n scale: 'linear',\n },\n ],\n});\n```\n\n效果如下(该示例为基于度中心性 `degree` ,可切换插值函数查看不同效果,示例中节点 label 为`${节点 id } - ${节点大小}`):\n\n\n\n## 实际案例\n\n- [场景案例:独角兽和他们的投资者](/examples/feature/default/#unicorns-investors)\n\n\n\n---\ntitle: Data Transformation Overview\norder: 0\n---\n\n## Overview\n\n\n\n---\ntitle: 数据处理总览\norder: 0\n---\n\n## 什么是数据处理\n\n数据处理( `transform` ),也叫数据转换器,是 G6 提供的支持在 **渲染前( `beforeDraw` )** 或者 **布局后( `afterLayout` )** 对绘制数据进行转化处理的机制,用户可以通过数据处理很方便地对数据处理逻辑进行封装解耦。\n\n## 实现原理\n\n### 基类\n\n所有的数据处理器都是基于 [BaseTransform](https://github.com/antvis/G6/blob/v5/packages/g6/src/transforms/base-transform.ts) 这个基类进行实现,里面定义了两个基类方法 `beforeDraw` 和 `afterLayout` :\n\n```typescript\nexport abstract class BaseTransform extends BaseExtension {\n public beforeDraw(data: DrawData, context: DrawContext): DrawData {\n return data;\n }\n\n public afterLayout(type: 'pre', data: DrawData): void;\n public afterLayout(type: 'post', data?: undefined): void;\n public afterLayout(type: 'pre' | 'post', data?: DrawData) {}\n}\n```\n\n以下是这两个方法里核心的参数类型说明:\n\n- **DrawData**\n\n ```typescript\n type ProcedureData = {\n nodes: Map;\n edges: Map;\n combos: Map;\n };\n\n type DrawData = {\n add: ProcedureData; // 本次渲染需要新增的元素\n update: ProcedureData; // 本次渲染需要更新的元素\n remove: ProcedureData; // 本次渲染需要移除的元素\n };\n ```\n\n- **pre | post**\n\n pre:绘制前进行的布局(只会在首次布局触发)\n\n post:完成绘制后进行的布局\n\n### 执行时机\n\n- **beforeDraw**\n\n 下面是每次渲染时数据处理的执行流程/时机:\n\n \n\n **详细说明:**\n\n 1. G6 在每次渲染前计算出 `add`、`update`、`remove`,分别对应为需要新增、更新、移除的元素,以下简称为 `DrawData`\n 2. 此时数据处理介入,按配置顺序执行每个数据处理的 `beforeDraw` 方法,参数则为 `DrawData`\n 3. 数据处理器中,对 `DrawData` 里面的元素数据进行改动,即可以按需对 `add`、`update`、`remove` 里面的元素数据进行修改、移除或者插入元素数据等,最终把改动后的 `DrawData` 返回给渲染主体逻辑\n 4. 在执行数据处理后,执行对应的新增、更新、移除元素的操作,完成渲染\n\n:::info{title=提示}\n触发渲染的场景分为主动和被动,列举如下:\n\n- **主动:** 用户主动调用 `graph.render()` 、 `graph.draw()` 或者在自定义插件、交互等实例里面通过上下文拿到元素控制器( [ElementController](https://github.com/antvis/G6/blob/v5/packages/g6/src/runtime/element.ts) )实例调用 `this.context.element.draw()`,等( `graph.render()` 和 `graph.draw()` 也是调用元素控制器的 `draw` 方法)\n- **被动:** 部分内置交互和插件有触发渲染,布局执行后也有触发渲染更新元素位置,等\n\n:::\n\n- **afterLayout**:在执行完布局计算并开始更新节点位置后,执行数据处理\n\n## 内置数据处理\n\n- **G6 提供给用户的内置数据处理如下:**\n\n各数据处理详细配置可参考 [内置数据处理文档](/manual/transform/map-node-size)。\n\n| 数据处理名称 | 注册类型 | 功能描述 | 执行时机 |\n| --------------------------------------------------- | ------------------------ | ---------------------------------------------------- | ---------- |\n| [动态调整节点大小](/manual/transform/map-node-size) | `map-node-size` | 根据节点中心性调整节点的大小 | beforeDraw |\n| [径向标签](/manual/transform/place-radial-labels) | `place-radial-labels` | 根据径向布局自动调整节点标签样式,包括位置和旋转角度 | afterDraw |\n| [平行边](/manual/transform/process-parallel-edges) | `process-parallel-edges` | 处理平行边,即多条边共享同一源节点和目标节点 | beforeDraw |\n\n- **G6 内嵌的数据处理如下:**\n\n除了提供给用户选用的数据处理外, G6 也封装并使用了以下数据处理机制来实现基础功能。以下数据处理不开放给用户配置使用,默认必带(列举出来供用户有需要时点击查看并参考源码):\n\n| 数据处理名称 | 注册类型 | 功能描述 | 执行时机 |\n| ----------------------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |\n| [调整combo绘制顺序](https://github.com/antvis/G6/blob/v5/packages/g6/src/transforms/arrange-draw-order.ts) | `arrange-draw-order` | combo 嵌套时,优先绘制子 combo | beforeDraw |\n| [处理组合的展开收起](https://github.com/antvis/G6/blob/v5/packages/g6/src/transforms/collapse-expand-combo.ts) | `collapse-expand-combo` | 收起时,移除 combo 内部元素、销毁内部边,外部边则连到收起的 combo 上;
展开时,反之; | beforeDraw |\n| [处理(树图)节点的收起和展开](https://github.com/antvis/G6/blob/v5/packages/g6/src/transforms/collapse-expand-node.ts) | `collapse-expand-node` | 绘制前,处理(树图)节点的收起和展开 | beforeDraw |\n| [获取边的实际端点](https://github.com/antvis/G6/blob/v5/packages/g6/src/transforms/get-edge-actual-ends.ts) | `get-edge-actual-ends` | 配合`collapse-expand-combo`实现收起时,combo 外部连到内部节点的边改为连到收起的 combo 上(`collapse-expand-combo`只是在收起时判断并标记了这些边需要更新端点) | beforeDraw |\n| [更新节点、combo相关边](https://github.com/antvis/G6/blob/v5/packages/g6/src/transforms/update-related-edge.ts) | `update-related-edges` | 如果更新了节点/combo,则把连接的边也一起更新了 | beforeDraw |\n\n:::warning{title=注意}\n\n上面 G6 为实现自身基础功能使用的数据处理仅供参考,不可改动。如有需要在这些数据处理基础上做特殊处理,可通过 [自定义数据处理](#自定义数据处理) 实现。\n\n:::\n\n## 配置方式\n\n### 基本配置\n\n在图实例初始化时,通过 `transforms` 数组指定需要的数据处理:\n\n```javascript\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n // 其他配置...\n transforms: ['process-parallel-edges'],\n});\n```\n\n### 配置数据处理参数\n\n对于需要自定义参数的数据处理,可以使用 `object` 形式配置属性:\n\n```javascript\nconst graph = new Graph({\n // 其他配置...\n transforms: [\n 'place-radial-labels',\n {\n type: 'process-parallel-edges',\n key: 'process-parallel-edges-1',\n mode: 'bundle',\n distance: 30,\n },\n ],\n});\n```\n\n### 动态更新数据处理\n\nG6 支持在图实例运行期间动态管理数据处理:\n\n- 可以通过 [setTransforms](/api/transform#graphsettransformstransforms) 方法调整数据处理器:\n\n```javascript\n// 添加新的数据处理器\ngraph.setTransforms((transforms) => [...transforms, 'place-radial-labels']);\n\n// 移除数据处理器\ngraph.setTransforms((transforms) => transforms.filter((t) => t !== 'place-radial-labels'));\n```\n\n- 可以通过 [updateTransform](/api/transform#graphupdatetransformtransform) 方法更新数据处理的配置:\n\n```javascript\n// 更新单个数据处理器\ngraph.updateTransform({\n key: 'process-parallel-edges-1',\n distance: 100,\n});\n```\n\n:::warning{title=注意}\n使用`updateTransform`方法时,需要在初始化时为数据处理指定唯一的`key`。\n:::\n\n### 卸载数据处理\n\n使用 [setTransforms](/api/transform#graphsettransformstransforms) 方法同样可以卸载数据处理,将数据处理配置列表置为空即可:\n\n```javascript\n// 卸载所有数据处理器\ngraph.setTransforms([]);\n```\n\n## 自定义数据处理\n\n当内置数据处理器无法满足需求时,你可以:\n\n- 继承和扩展现有数据处理\n- 创建全新的自定义数据处理\n\n自定义数据处理需要先注册后使用。详细教程请参考 [自定义数据处理](/manual/transform/custom-transform) 文档。\n\n```javascript\nimport { register, ExtensionCategory } from '@antv/g6';\nimport { MyCustomTransform } from './my-custom-transform';\n\n// 注册自定义数据处理器\nregister(ExtensionCategory.TRANSFORM, 'my-custom-transform', MyCustomTransform);\n\n// 使用自定义数据处理\nconst graph = new Graph({\n transforms: ['my-custom-transform'],\n});\n```\n\n\n\n---\ntitle: PlaceRadialLabels\norder: 3\n---\n\n## Options\n\n### offset\n\n> _number_\n\nOffset\n\n\n\n---\ntitle: 径向标签 PlaceRadialLabels\norder: 2\n---\n\n**参考示例**:\n\n- [径向生态树](/examples/scene-case/tree-graph/#radial-dendrogram)\n- [径向紧凑树](/examples/scene-case/tree-graph/#radial-compact-tree)\n\n## 配置项\n\n### type\n\n> _`place-radial-labels` \\| string_\n\n此数据处理已内置,你可以通过 `type: 'place-radial-labels'` 来使用它。\n\n### offset\n\n> _number_\n\n偏移量\n\n\n\n---\ntitle: ProcessParallelEdges\norder: 3\n---\n\nParallel Edges refer to multiple edges existing between two nodes in a graph structure. These edges share the same source and target nodes but may represent different relationships or attributes. To avoid edge overlap and confusion, two methods are provided for handling parallel edges: (1) Bundle Mode: Bundles parallel edges together and separates them from other edges by altering their curvature; (2) Merge Mode: Merges parallel edges into a single aggregated edge.\n\n## Options\n\n### distance\n\n> _number_\n\nThe distance between edges, only valid for bundling mode\n\n### edges\n\n> _string[]_\n\nThe edges to be handled, all edges by default\n\n### Required mode\n\n> _'bundle' \\| 'merge'_ **Default:** `'bundle'`\n\nProcessing mode\n\n- '`merge`': Merge parallel edges into one edge which is suitable for cases where parallel edges do not need to be distinguished\n\n- '`bundle`': Each edge will be bundled with all other parallel edges and separated from them by varying the curvature. If the number of parallel edges in a group is odd, the central edge will be drawn as a straight line, and the others will be drawn as curves\n\n### style\n\n> _PathStyleProps_ _\\| ((prev:_ [EdgeData](/api/graph/option#edgedata)_[]) =>_ _PathStyleProps)_\n\nThe style of the merged edge, only valid for merging mode\n\n\n\n---\ntitle: 平行边 ProcessParallelEdges\norder: 3\n---\n\n## 概述\n\n平行边(Parallel Edges)是指在图结构中,两个节点之间存在多条边。这些边共享相同的源节点和目标节点,但可能代表不同的关系或属性。为了避免边的重叠和混淆,提供了两种处理平行边的方式:\n\n- 捆绑模式(bundle):将平行边捆绑在一起,通过改变曲率与其他边分开\n- 合并模式(merge):将平行边合并为一条聚合\n\n## 使用场景\n\n下面为常见使用场景举例:\n\n- 双向数据流,比如客户端发送请求,服务端返回响应\n\n- 多依赖关系,即一个节点通过多种方式依赖另一个节点,比如微服务架构中,服务 A 调用服务 B的两个不同 API\n- 多链路,比如高可用架构中,主链路(实线,状态正常)与备份链路(灰色虚线,状态待机)同时展示\n\n## 基本用法\n\n**1. 快速配置(静态)**\n\n使用字符串形式直接声明,这种方式简洁但仅支持默认配置,且配置后不可动态修改:\n\n```javascript\nconst graph = new Graph({\n // 其他配置...\n transforms: ['process-parallel-edges'],\n});\n```\n\n**2. 对象配置(推荐)**\n\n使用对象形式进行配置,支持自定义参数,且可以在运行时动态更新配置:\n\n```javascript\nconst graph = new Graph({\n // 其他配置...\n transforms: [\n {\n type: 'process-parallel-edges',\n key: 'process-parallel-edges',\n mode: 'bundle', // 默认就是捆绑模式了\n distance: 30, // 配置捆绑模式下边之间的距离为30\n },\n ],\n});\n```\n\n## 配置项\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------- | -------------------------------------------------- | --------------------------------------------------------------------------------------- | ----------------------- | ---- |\n| type | 数据处理类型 | process-parallel-edges | - | ✓ |\n| distance | 边之间的距离,仅在捆绑模式下有效 | number | 15 | |\n| edges | 考虑要处理的边,默认为全部边 | string[] | - | |\n| mode | 处理模式,[配置项](#mode) | `'bundle'` | `'merge'` \\| `'bundle'` | |\n| style | 合并边的样式,仅在合并模式下有效,[配置项](#style) | PathStyleProps \\| ((prev: [EdgeData](/manual/data#边数据edgedata)[]) => PathStyleProps) | - | |\n\n### mode\n\n提供了两种处理模式:\n\n- `'merge'`: 将平行边合并为一条边,适用于不需要区分平行边的情况,[示例](#合并模式)\n\n`merge` 使用的是内置的直线( [`line`](/manual/element/edge/Line) )来展示合并边。\n\n- `'bundle'`: 每条边都会与其他所有平行边捆绑在一起,并通过改变曲率与其他边分开。如果一组平行边的数量是奇数,那么中心的边将被绘制为直线,其他的边将被绘制为曲线,[示例](#捆绑模式)\n\n`bundle` 使用的是内置的二次贝塞尔曲线( [`quadratic`](/manual/element/edge/Quadratic) )来实现,数据处理过程会强制把每个 `edgeDatum.type` 改为 `quadratic`,并计算每条线的曲率。\n\n:::warning{title=注意}\n\n禁止在创建 Graph 实例时配置 `edge.type` 即默认的边类型,因为它的优先级比 `edgeDatum.type` 要高,会导致 `bundle` 模式的处理无法生效。\n\n:::\n\n### style\n\n合并边的样式,仅在合并模式下有效\n\n#### PathStyleProps\n\n下表列出了常用的属性,更多属性请参考 [PathStyleProps](https://g.antv.antgroup.com/api/basic/path#pathstyleprops)。\n\n| 属性 | 描述 | 类型 | 默认值 | 必选 |\n| -------------- | ------------------------- | ----------------------------- | --------- | ---- |\n| fill | 填充色 | string | `#1783FF` |\n| fillOpacity | 填充色透明度 | number \\| string | 1 |\n| lineCap | 描边端点样式 | `round` \\| `square` \\| `butt` | `butt` |\n| lineDash | 描边虚线样式 | number[] | - |\n| lineDashOffset | 描边虚线偏移量 | number | - |\n| lineJoin | 描边连接处样式 | `round` \\| `bevel` \\| `miter` | `miter` |\n| lineWidth | 描边宽度 | number | 1 |\n| opacity | 透明度 | number \\| string | 1 |\n| shadowBlur | 阴影模糊度 | number | - |\n| shadowColor | 阴影颜色 | string | - |\n| shadowOffsetX | 阴影在 x 轴方向上的偏移量 | number \\| string | - |\n| shadowOffsetY | 阴影在 y 轴方向上的偏移量 | number \\| string | - |\n| shadowType | 阴影类型 | `inner` \\| `outer` | `outer` |\n| stroke | 描边色 | string | `#000` |\n| strokeOpacity | 描边色透明度 | number \\| string | 1 |\n| visibility | 图形是否可见 | `visible` \\| `hidden` | `visible` |\n\n## 代码示例\n\n### 捆绑模式\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'A', style: { x: 50, y: 350 } },\n { id: 'B', style: { x: 250, y: 150 } },\n { id: 'C', style: { x: 450, y: 350 } },\n ],\n edges: [\n { source: 'A', target: 'C' },\n { source: 'C', target: 'A' },\n ...Array.from({ length: 10 }).map((_, i) => ({\n id: `edge:A-B${i}`,\n source: 'A',\n target: 'B',\n data: {\n label: `A->B:${i}`,\n },\n })),\n ...Array.from({ length: 5 }).map((_, i) => ({\n id: `edge:B-C${i}`,\n source: 'B',\n target: 'C',\n data: {\n label: `B->C:${i}`,\n },\n })),\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n autoFit: 'center',\n data,\n node: {\n style: {\n ports: [{ placement: 'center' }],\n labelText: (d) => d.id,\n },\n },\n edge: {\n style: {\n labelText: (d) => d?.data?.label || `${d.source}->${d.target}`,\n },\n },\n behaviors: ['drag-element'],\n transforms: ['process-parallel-edges'],\n});\n\ngraph.render();\n```\n\n### 合并模式\n\n下面是一个简单的合并模式的例子,需要注意:\n\n- 不需要合并(即两个节点间只有一条边)的边,合并样式不会在这条边上生效,比如例子中的 **A->C**\n- 合并样式实际上是赋值给 `datum.style` ,也就是优先级会比实例化 Graph 时配置的默认样式低( `edge.style` ),所以例子中合并样式的 `startArrow` 没有生效\n\n```js | ob { inject: true }\nimport { Graph } from '@antv/g6';\n\nconst data = {\n nodes: [\n { id: 'A', style: { x: 50, y: 350 } },\n { id: 'B', style: { x: 250, y: 150 } },\n { id: 'C', style: { x: 450, y: 350 } },\n ],\n edges: [\n { source: 'A', target: 'B' },\n { source: 'B', target: 'A' },\n { id: 'B-C:1', source: 'B', target: 'C' },\n { id: 'B-C:2', source: 'B', target: 'C' },\n { source: 'A', target: 'C' },\n ],\n};\n\nconst graph = new Graph({\n container: 'container',\n autoFit: 'center',\n data,\n node: {\n style: {\n labelText: (d) => d.id,\n },\n },\n edge: {\n style: {\n labelText: (d) => d?.data?.label || `${d.source}->${d.target}`,\n startArrow: false,\n },\n },\n transforms: [\n {\n type: 'process-parallel-edges',\n mode: 'merge',\n style: {\n halo: true,\n haloOpacity: 0.2,\n haloStroke: 'red',\n startArrow: true,\n },\n },\n ],\n});\n\ngraph.render();\n```\n\n\n\n---\ntitle: Feature\norder: 1\n---\n\n## 🏖️ Brand New Design Specification for Graphs\n\nG6 version 5.0 has redesigned the Options specification. While ensuring comprehensive capabilities, it optimizes the options structure to be more intuitive and easier to understand.\n\nYou only need to quickly grasp the basic [core concepts](/en/manual/graph/graph) to get started with G6 quickly and achieve graph visualization without delay.\n\n**😰 The 4.0 Options** had a complex nested structure and was less semantically capable\n\n```typescript\n{\n defaultNode: {\n size: 30,\n style: {\n fill: 'steelblue',\n stroke: '#666',\n lineWidth: 1\n },\n labelCfg: {\n style: {\n fill: '#fff',\n }\n }\n },\n nodeStateStyles: {\n hover: {\n fill: 'lightsteelblue'\n }\n },\n modes: {\n default: ['zoom-canvas', 'drag-canvas', 'drag-node'],\n },\n}\n```\n\n**😄 The 5.0 Options** has a clear structure and is easy to understand\n\n```typescript\n{\n node: {\n style: {\n size: 30,\n fill: 'steelblue',\n stroke: '#666',\n lineWidth: 1\n labelFill: '#fff',\n },\n state: {\n hover: {\n fill: 'lightsteelblue'\n }\n }\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n}\n```\n\n## 🔨 Brand New API Design\n\nG6 5.0 features a cleaner, easy-to-use API design that is more in line with modern front-end frameworks.\n\n## 🌲 Merging Graphs with Tree Graphs\n\nTree graphs are essentially a type of directed acyclic graph. G6 5.0 has integrated the design of graphs and tree graphs, reducing the cost of understanding and usage.\n\nNow, you can directly use `Graph` to instantiate and draw tree graphs in G6, without the need to use `TreeGraph`. You simply need to specify the layout as a tree graph layout.\n\nAdditionally, G6 provides the `treeToGraphData` utility method to help you quickly convert tree graph data into graph data.\n\n\n\n## 🌆 Multi-Renderer Support\n\nG6 5.0 employs the next-generation @antv/g rendering engine, which has been newly designed. It offers support for multiple renderers such as `Canvas`, `SVG`, and `WebGL`. Additionally, it supports the mixed use of different renderers on layered canvases.\n\n```typescript\nimport { Renderer } from '@antv/g-webgl';\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n // ... other configurations\n // Use the WebGL renderer\n renderer: () => new Renderer(),\n});\n```\n\n## 🚀 High-Performance Layouts\n\nG6 5.0 has adopted a brand-new layout engine, with some layouts implemented in Rust, providing higher performance for layout calculations. Additionally, there is support for WebGPU acceleration in certain layouts.\n\n> 🚀 To utilize high-performance layouts, you will need to install the `@antv/layout-wasm` package\n\n```typescript\nimport { FruchtermanLayout } from '@antv/layout-gpu';\nimport { Graph, register, ExtensionCategory } from '@antv/g6';\n\nregister(ExtensionCategory.LAYOUT, 'fruchterman-gpu', FruchtermanLayout);\n\nconst graph = new Graph({\n // ... other configurations\n layout: {\n type: 'fruchterman-gpu',\n // ... Other Layout Configurations\n },\n});\n```\n\n## 🎨 Multiple Themes Mechanism\n\nG6 5.0 comes with two built-in themes: light and dark, and allows for flexible customization based on the use case. For details, please refer to [Custom Theme](/en/manual/custom-extension/theme).\n\n\n\n## 🌍 3D Large Graphs\n\nG6 5.0 provides 3D rendering, layout, interaction capabilities, and can be used by import 3d elements, renderer, and behaviors from `@antv/g6-extension-3D` registration, see: [Using 3D](/manual/further-reading/3d).\n\n\n\n## 💪 Plugin Optimization and Enhancement\n\nG6 5.0 has optimized and enhanced existing plugins, decoupling Graph from plugins, and providing richer capabilities while optimizing configurations.\n\nPlease visit [Plugin](/en/api/plugins/bubble-sets) to experience the capabilities of more plugins.\n\n## 💼 Optimized Package Size\n\nThanks to the well-modularized design and extension registration mechanism of G6 5.0, modules that are not used will not be packaged into the final build file, reducing the package size.\n\nCompared to 4.0, the UMD package size has been reduced from 1.8 MB to 0.96 MB, a reduction of nearly 50%.\n\n\n\n---\ntitle: 新版本特性\norder: 1\n---\n\n## 🏖️ 全新设计图配置范式\n\nG6 5.0 重新设计了图配置范式,在保证能力完善的基础上,优化配置项结构,更加直观、易于理解。\n\n仅需快速了解基本[核心概念](/manual/graph/graph),即可快速上手 G6,快速实现图可视化。\n\n**😰 4.0 配置项** 嵌套结构复杂,语义化能力较弱\n\n```typescript\n{\n defaultNode: {\n size: 30,\n style: {\n fill: 'steelblue',\n stroke: '#666',\n lineWidth: 1\n },\n labelCfg: {\n style: {\n fill: '#fff',\n }\n }\n },\n nodeStateStyles: {\n hover: {\n fill: 'lightsteelblue'\n }\n },\n modes: {\n default: ['zoom-canvas', 'drag-canvas', 'drag-node'],\n },\n}\n```\n\n**😄 5.0 配置项** 结构清晰,易于理解\n\n```typescript\n{\n node: {\n style: {\n size: 30,\n fill: 'steelblue',\n stroke: '#666',\n lineWidth: 1\n labelFill: '#fff',\n },\n state: {\n hover: {\n fill: 'lightsteelblue'\n }\n }\n },\n behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element'],\n}\n```\n\n## 🔨 全新 API 设计\n\nG6 5.0 采用了更加简洁、易用的 API 设计,更加符合现代前端框架的设计风格。\n\n## 🌲 合并图与树图\n\n树图本质上是一种单向无环图,G6 5.0 融合了图与树图的设计,降低了理解和使用成本。\n\n现在,你可以在 G6 中直接使用 `Graph` 来实例化绘制树图,而不需要再使用 `TreeGraph`,仅需指定布局为树图布局即可。\n\n此外,G6 提供了 `treeToGraphData` 工具方法,帮助你快速将树图数据转换为图数据。\n\n\n\n## 🌆 多渲染器支持\n\nG6 5.0 采用了新一代设计的 @antv/g 渲染引擎,提供了 `Canvas` `SVG` `WebGL` 多种渲染器支持,并且分层画布支持不同渲染器的混合使用。\n\n```typescript\nimport { Renderer } from '@antv/g-webgl';\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n // ... 其他配置\n // 使用 WebGL 渲染器\n renderer: () => new Renderer(),\n});\n```\n\n## 🚀 高性能布局\n\nG6 5.0 采用了全新布局引擎,部分布局提供 Rust 实现,提供了更高性能的布局计算。另有布局支持 WebGPU 加速。\n\n> 🚀 高性能布局需要安装 `@antv/layout-wasm` 包\n\n```typescript\nimport { ForceAtlas2Layout, initThreads, supportsThreads } from '@antv/layout-wasm';\nimport { Graph, register, ExtensionCategory } from '@antv/g6';\n\nregister(ExtensionCategory.LAYOUT, 'forceatlas2-wasm', ForceAtlas2Layout);\n\nconst supported = await supportsThreads();\nconst threads = await initThreads(supported);\n\nconst graph = new Graph({\n // ... 其他配置\n layout: {\n type: 'forceatlas2-wasm',\n threads,\n // ... 其他布局配置\n },\n});\n```\n\n> GPU 加速布局需要安装 `@antv/layout-gpu` 包\n\n```typescript\nimport { FruchtermanLayout } from '@antv/layout-gpu';\nimport { Graph, register, ExtensionCategory } from '@antv/g6';\n\nregister(ExtensionCategory.LAYOUT, 'fruchterman-gpu', FruchtermanLayout);\n\nconst graph = new Graph({\n // ... 其他配置\n layout: {\n type: 'fruchterman-gpu',\n // ... 其他布局配置\n },\n});\n```\n\n## 🎨 多主题机制\n\nG6 5.0 内置了亮色、暗色两套主题,并可基于使用场景进行灵活定制,具体可参考[自定义主题](/manual/theme/custom-theme)。\n\n\n\n## 🌍 3D 大图\n\nG6 5.0 提供了 3D 大图渲染、布局、交互能力,从 `@antv/g6-extension-3d` 中引入 3D 元素、渲染器、交互等注册即可使用,详见:[使用 3D](/manual/further-reading/3d)。\n\n\n\n## 💪 插件优化增强\n\nG6 5.0 对现有插件进行了优化增强,解除了 Graph 与插件之间的耦合,并优化配置的同时提供了更加丰富了能力。\n\n敬请前往[插件](/manual/plugin/overview)体验更多插件的能力。\n\n## 💼 优化包体积\n\n得益于 G6 5.0 良好的模块化的设计以及扩展注册机制,对于未使用的模块,不会被打包到最终的构建文件中,减小了包体积。\n\n与 4.0 相比,UMD 包体积从 1.8 MB 减小到 0.96 MB,减小了近 50%。\n\n\n\n---\ntitle: Upgrade from 5.0 to 5.1 (Layout)\norder: 7\n---\n\nThis page focuses on layout documentation changes introduced in G6 `5.1`. Starting from `5.1`, layout pages prioritize the field naming aligned with `@antv/layout`; common `5.0` layout writeups are collected here for migration reference.\n\n## What Changed\n\n- Since `5.1`, layout docs prioritize shared `@antv/layout` fields such as `width`, `height`, `center`, `enableWorker`, `node`, and `edge`\n- Individual layout pages mainly keep the recommended `5.1` writeup\n- If you are migrating an existing `5.0` project, use this page to map old field names to the new documentation style\n\n## D3Force: center force to shortcut fields\n\nIn `5.0` documentation, the center force was commonly written inside `center`:\n\n```typescript\n{\n layout: {\n type: 'd3-force',\n center: {\n x: 250,\n y: 150,\n strength: 0.8,\n },\n },\n}\n```\n\nIn `5.1` documentation, the recommended form is the shortcut fields:\n\n```typescript\n{\n layout: {\n type: 'd3-force',\n centerX: 250,\n centerY: 150,\n centerStrength: 0.8,\n },\n}\n```\n\n- `center.x` maps to `centerX`\n- `center.y` maps to `centerY`\n- `center.strength` maps to `centerStrength`\n\nFor the full field description, see [D3Force Layout](/en/manual/layout/d3-force-layout).\n\n## ComboCombined: innerLayout / outerLayout to layout\n\nIn `5.0` documentation, the inner combo layout and the outer layout were commonly configured separately:\n\n```typescript\nimport { ConcentricLayout, ForceLayout } from '@antv/layout';\n\n{\n layout: {\n type: 'combo-combined',\n innerLayout: new ConcentricLayout({\n sortBy: 'id',\n }),\n outerLayout: new ForceLayout({\n gravity: 1,\n }),\n },\n}\n```\n\nIn `5.1` documentation, the recommended form is a single `layout` entry that returns different configurations for different levels based on `comboId`:\n\n```typescript\n{\n layout: {\n type: 'combo-combined',\n layout: (comboId) =>\n comboId\n ? { type: 'concentric', sortBy: 'id' }\n : { type: 'force', gravity: 1 },\n },\n}\n```\n\n- When `comboId` has a value, it refers to the layout inside a combo\n- When `comboId` is empty, it refers to the outermost layout\n- Multiple layout choices are unified under the `layout` entry\n\nFor the `5.1` recommended form, see [ComboCombined Layout](/en/manual/layout/combo-combined-layout).\n\n## Migration Suggestion\n\n1. Identify the matching layout type from your existing `5.0` project\n2. Map the old fields to the recommended `5.1` documentation style\n3. Return to the specific layout page to verify shared fields and defaults\n\n\n\n---\ntitle: 从 5.0 升级到 5.1(布局)\norder: 7\n---\n\n本文档聚焦 G6 `5.1` 中布局文档的写法调整。`5.1` 的布局页面会优先展示与 `@antv/layout` 对齐后的字段;`5.0` 中常见的布局写法统一收敛到本文档中,便于迁移时对照。\n\n## 总体变化\n\n- `5.1` 起,布局文档会优先展示 `@antv/layout` 的公共字段,例如 `width` `height` `center` `enableWorker` `node` `edge`\n- 各布局页主要保留 `5.1` 推荐写法,`5.0` 中常见写法请以本文档为准\n- 迁移时,建议先按旧项目中的字段定位,再映射到 `5.1` 文档里的推荐配置\n\n## D3Force:center force 改为快捷字段\n\n`5.0` 文档中,常见把中心力写在 `center` 中:\n\n```typescript\n{\n layout: {\n type: 'd3-force',\n center: {\n x: 250,\n y: 150,\n strength: 0.8,\n },\n },\n}\n```\n\n`5.1` 文档中推荐写成更直接的快捷字段:\n\n```typescript\n{\n layout: {\n type: 'd3-force',\n centerX: 250,\n centerY: 150,\n centerStrength: 0.8,\n },\n}\n```\n\n- `center.x` 对应 `centerX`\n- `center.y` 对应 `centerY`\n- `center.strength` 对应 `centerStrength`\n\n如果你需要继续对照完整字段说明,可查看 [D3Force 布局](/manual/layout/d3-force-layout)。\n\n## ComboCombined:innerLayout / outerLayout 收敛为 layout\n\n`5.0` 文档中,常见分别配置 Combo 内部布局和最外层布局:\n\n```typescript\nimport { ConcentricLayout, ForceLayout } from '@antv/layout';\n\n{\n layout: {\n type: 'combo-combined',\n innerLayout: new ConcentricLayout({\n sortBy: 'id',\n }),\n outerLayout: new ForceLayout({\n gravity: 1,\n }),\n },\n}\n```\n\n`5.1` 文档中推荐使用单个 `layout` 字段,根据 `comboId` 为不同层级返回布局配置:\n\n```typescript\n{\n layout: {\n type: 'combo-combined',\n layout: (comboId) =>\n comboId\n ? { type: 'concentric', sortBy: 'id' }\n : { type: 'force', gravity: 1 },\n },\n}\n```\n\n- `comboId` 有值时,表示当前为 Combo 内部层级\n- `comboId` 为空时,表示当前为最外层布局\n- 多层布局选择统一收敛到 `layout` 入口\n\n如果你需要查看 `5.1` 推荐写法,可查看 [ComboCombined 布局](/manual/layout/combo-combined-layout)。\n\n## 迁移建议\n\n1. 先按旧项目中的布局类型定位到本文档对应条目\n2. 再把 `5.0` 字段映射成 `5.1` 文档里的推荐写法\n3. 最后回到具体布局页,核对公共字段和默认值\n\n\n\n---\ntitle: Upgrade To 5.0\norder: 6\n---\n\nThis document will guide you through the process of upgrading from G6 version `4.x` to `5.x`. If you are using version `3.x`, please upgrade to version `4.x` first.\n\n## Preparation Before Upgrade\n\n1. Please ensure that your current git branch is clean and there is no uncommitted code.\n2. Refer to the [Installation](./getting-started/installation) document to install version `5.x` and remove the dependencies for version `4.x`.\n\n## Start Upgrade\n\n### Data\n\nThe data format in the new version has changed as follows:\n\n1. All style attributes in `nodes`, `edges`, and `combos` need to be placed within `style`, and data attributes should be stored in `data`:\n\n```typescript\n// 4.x\nconst data = {\n nodes: [\n { id: 'node1', label: 'node1', size: 20 },\n { id: 'node2', label: 'node2', size: 20 },\n ],\n edges: [{ source: 'node1', target: 'node2' }],\n};\n\n// 5.x\nconst data = {\n nodes: [\n // The label is a non-stylistic attribute, placed in the data, and can be accessed in the style mapping function\n // The `size` is a stylistic attribute, placed within the `style`\n { id: 'node1', data: { label: 'node1' }, style: { size: 20 } },\n { id: 'node2', data: { label: 'node2' }, style: { size: 20 } },\n ],\n edges: [{ source: 'node1', target: 'node2' }],\n};\n```\n\nSince we have redesigned and implemented the elements, please refer to the corresponding documentation to modify the new element options:\n\n- [Node](/en/api/elements/nodes/base-node)\n- [Edge](/en/api/elements/edges/base-edge)\n- [Combo](/en/api/elements/combos/base-combo)\n\n2. If you need to specify the element type in the data, you can use the `type` attribute:\n\n```typescript\n{\n nodes: [\n // Specify the node type as rect\n { id: 'node1', type: 'rect' },\n ];\n}\n```\n\n### Options\n\nChange **fitView / fitCenter / fitViewPadding**\n\n- The `fitView` and `fitCenter` options have been merged into `autoFit`.\n- To use `fitView`, you can configure it as `autoFit: 'view'`\n- To use `fitCenter`, you can configure it as `autoFit: 'center'`\n- You can also pass an object for full configuration:\n\n```js\nautoFit: {\n type: 'view',\n options: {\n // ...\n }\n}\n```\n\n- The `fitViewPadding` has been changed to `padding`.\n\nRemoved **linkCenter**\n\nIn version 5.x, the edge connection mechanism will attempt to connect to nodes/Combos in the following order:\n\n1. Connect Port\n2. Outline\n3. Center\n\nRemoved **groupByTypes**\n\nRemoved **autoPaint**\n\nPlease manually call the `render` or `draw` method to perform rendering.\n\nChanged **modes**\n\nIn version 5.x, interaction modes have been removed. You can switch the currently enabled behaviors by setting `behaviors`.\n\n```typescript\n// 4.x\n{\n modes: {\n default: ['drag-canvas', 'zoom-canvas'],\n preview: ['drag-canvas'],\n },\n}\n\ngraph.setMode('preview');\n```\n\n```typescript\n\n// 5.x\n{\n behaviors: ['drag-canvas', 'zoom-canvas'],\n}\n\ngraph.setBehaviors(['drag-canvas']);\n\n```\n\nChange **defaultNode / defaultEdge / defaultCombo**\n\nThe element styles have been moved to `[element].style`, for example, `defaultNode` has been changed to `node.style`:\n\n```typescript\n// 4.x\n{\n defaultNode: {\n size: 20,\n fill: 'red',\n }\n}\n\n// 5.x\n{\n node: {\n style: {\n size: 20,\n fill: 'red',\n }\n }\n}\n```\n\nChange **nodeStateStyles / edgeStateStyles / comboStateStyle**\n\nElement state styles have been moved to `[element].state`, for example, `nodeStateStyles` has been changed to `node.stateStyles`:\n\n```typescript\n// 4.x\n{\n nodeStateStyles: {\n selected: {\n fill: 'red',\n }\n }\n}\n\n// 5.x\n{\n node: {\n state: {\n selected: {\n fill: 'red',\n }\n }\n }\n}\n```\n\nChange **animate / animateCfg**\n\n- The `animate` options has been changed to `animation`\n- `animate` and `animateCfg` have been merged into `animation`\n\n```typescript\n// 4.x\n{\n animate: true,\n}\n\n// 5.x\n{\n animation: true,\n}\n{\n animation: {\n duration: 500,\n easing: 'easeLinear',\n }\n}\n```\n\nChange **minZoom / maxZoom**\n\n- The `minZoom` and `maxZoom` options have been merged into `zoomRange`\n\n```typescript\n// 4.x\n{\n minZoom: 0.5,\n maxZoom: 2,\n}\n\n// 5.x\n{\n zoomRange: [0.5, 2],\n}\n```\n\nChange **renderer**\n\nG6 5.x supports multi-layer canvases and defaults to using the `canvas` renderer.\n\nThe `renderer` no longer supports the string type and has been changed to a callback function:\n\n```typescript\n// 4.x\nvar options = {\n renderer: 'svg',\n};\n\n// 5.x\nimport { Renderer } from '@antv/g-svg';\n\n{\n renderer: () => new Renderer(),\n}\n```\n\nRemoved **enabledStack / maxStep**\n\nThe built-in undo and redo functionality has been removed in version 5.x. For related capabilities, please use a plugin to implement.\n\n### API\n\nChange **data / save / read / changeData**\n\nVersion 5.x offers a completely new data API. For details, see [Data API](/en/api/data).\n\n- The `data` and `changeData` methods from 4.x are replaced by `setData` in 5.x.\n- The `save` method from 4.x is replaced by `getData` in 5.x.\n- The `read` method from 4.x is replaced by `setData` + `render` in 5.x.\n\nChange **get / set**\n\nTo access Graph options, please use `getOptions` or the `getXxx` API, such as `getZoomRange`, `getBehaviors`, etc. The `set` method is analogous.\n\nChange **getContainer**\n\nDirect API to obtain the container is not currently supported, but you can obtain it through `graph.getCanvas().getContainer()`.\n\n> In most cases, you do not need to directly manipulate the container.\n\nRemoved **getGroup**\n\nChange **getMinZoom / getMaxZoom**\n\nUse `getZoomRange` to obtain the values.\n\nChange **setMinZoom / setMaxZoom**\n\nUse the `setZoomRange` method to set the values.\n\nChange **getWidth / getHeight**\n\nUse `getSize` to get the dimensions.\n\nChange **changeSize**\n\nUse `setSize` to set the dimensions.\n\nChange **zoom**\n\nChanged to `zoomBy`.\n\nChange **translate**\n\nChanged to `translateBy`.\n\nChange **moveTo**\n\nChanged to `translateTo`.\n\nChange **focusItem**\n\nChanged to `focusElement`.\n\nRemoved **addItem / updateItem / removeItem**\n\nTo add or remove elements, use the methods `addData` / `updateData` / `removeData` to manipulate data.\n\nRemoved **refreshItem**\n\nRemoved **refreshPositions**\n\nRemoved **updateCombo**\n\nRemoved **updateCombos**\n\nRemoved **updateComboTree**\n\nChange **node / edge / combo**\n\nUse the `setNode` / `setEdge` / `setCombo` methods as alternatives.\n\nChange **showItem / hideItem**\n\nUse the `setElementVisibility` method as an alternative.\n\nRemoved **getNodes / getEdges / getCombos / getComboChildren / getNeighbors / find / findById / findAll / findAllByState**\n\nIn version 5.x, direct retrieval of element instances is not supported.\n\n- To obtain element data, use the methods `getData`, `getNodeData`, `getEdgeData`, `getComboData`, which support searching by element ID.\n- To obtain child node data, use the `getChildrenData` method.\n- To obtain neighbor node data, use the `getNeighborNodesData` method.\n- To find element data based on state, use the `getElementDataByState` method.\n\nChange **collapseCombo / expandCombo**\n\nUse the `collapseElement` / `expandElement` methods as alternatives.\n\nRemoved **collapseExpandCombo**\n\nRemoved **createCombo**\n\nCombos can now be added using the `addData` / `addComboData` methods.\n\nRemoved **uncombo**\n\nCombos can now be removed using the `removeData` / `removeComboData` methods.\n\nChange **setItemState**\n\nUse the `setElementState` method as an alternative.\n\nRemoved **clearItemStates**\n\n- To clear all states of a single element: `graph.setElementState(id, [])`\n- To clear all states of multiple elements: `graph.setElementState({ id1: [], id2: [] })`\n\nRemoved **priorityState**\n\nWhen using `setElementState`, the state that appears later in the array has a higher priority.\n\nRemoved **setMode**\n\nUse `setBehaviors` to set the current behaviors.\n\nRemoved **setCurrentMode**\n\nChange **layout**\n\nDoes not support parameters. To configure the layout, please use `setLayout`.\n\nChange **updateLayout**\n\nChanged to `setLayout`.\n\nRemoved **destroyLayout**\n\nChange **addBehaviors / removeBehaviors**\n\nReplaced with `setBehaviors`.\n\nRemoved **createHull / getHulls / removeHull / removeHulls**\n\n- For multiple `Hull` instances, you need to configure multiple `hull` plugins in `plugins`, such as:\n\n```typescript\n{\n plugins: ['hull', 'hull'],\n};\n```\n\n- Operations to retrieve, update, and remove `Hull` are implemented through `setPlugins`, `updatePlugin`.\n\nNot yet available **getNodeDegree**\n\nNot yet available **getShortestPathMatrix**\n\nNot yet available **getAdjMatrix**\n\nRemoved **pushStack / getUndoStack / getRedoStack / getStackData / clearStack**\n\nAll undo and redo related APIs should be called after obtaining the corresponding plugin, for example:\n\n```typescript\n// 'history' is the key configured for use with the plugin\nconst history = graph.getPluginInstance('history');\n\nhistory.redo();\n```\n\nRemoved **positionsAnimate / stopAnimate / isAnimating**\n\nAnimation-related information is now emitted through events:\n\n- Animation start event: `beforeanimate`\n- Animation end event: `afteranimate`\n- To stop an animation:\n\n```typescript\ngraph.on('beforeanimate', (event) => {\n event.animation.stop();\n});\n```\n\nChange **getPointByClient / getClientByPoint / getPointByCanvas / getCanvasByPoint / getGraphCenterPoint / getViewPortCenterPoint**\n\nG6 5.x uses a different coordinate system than 4.x. For details, see [Coordinate](/en/manual/further-reading/coordinate).\n\nRemoved **setTextWaterMarker / setImageWaterMarker**\n\nFor watermark functionality, please refer to the [Watermark](/en/api/plugins/watermark)plugin.\n\nChange **toFullDataURL**\n\nReplaced with `toDataURL`, specify the parameter as: `mode: 'overall'`\n\n```typescript\ngraph.toDataURL({ mode: 'overall' });\n```\n\nRemoved **downloadFullImage / downloadImage**\n\nOnly the capability to export as a `DataURL` is provided. If you need to download an image, please refer to the following example code:\n\n```typescript\nasync function downloadImage() {\n const dataURL = await graph.toDataURL();\n const [head, content] = dataURL.split(',');\n const contentType = head.match(/:(.*?);/)![1];\n\n const bstr = atob(content);\n let length = bstr.length;\n const u8arr = new Uint8Array(length);\n\n while (length--) {\n u8arr[length] = bstr.charCodeAt(length);\n }\n\n const blob = new Blob([u8arr], { type: contentType });\n\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'graph.png';\n a.click();\n}\n```\n\nRemoved **clear**\n\nUse `setData` + `draw` to clear data and the canvas.\n\n### Extension Registration\n\nUnlike G6 4.x, G6 5.x uses a unified extension registration function (register). You can refer to the [Extension Register](/en/manual/core-concept/extension#register-extension) to register G6 extensions.\n\nThe following G6 4.x registration functions have been deprecated:\n\n- registerNode\n- registerEdge\n- registerCombo\n- registerLayout\n- registerBehavior\n\n### Events\n\nCompared to G6 4.x, G6 5.x has the following differences in events:\n\n- The `mouse` and `touch` events have been removed and are unified under the `pointer` event.\n- The naming convention for lifecycle events is usually in the format of: `before/after` + `object/property` + `action`, for example: `beforeelementcreate` is triggered before an element is created.\n- The following events have been removed:\n - afteractivaterelations\n - afteradditem\n - aftercreateedge\n - aftergraphrefresh\n - aftergraphrefreshposition\n - afteritemrefresh\n - aftermodechange\n - afterremoveitem\n - afterupdateitem\n - beforeadditem\n - beforecreateedge\n - beforegraphrefresh\n - beforegraphrefreshposition\n - beforeitemrefresh\n - beforemodechange\n - beforeremoveitem\n - beforeupdateitem\n - dragnodeend\n - nodeselectchange\n - stackchange\n - tooltipchange\n- The following element change events have been removed, but you can still access them through `beforeelementupdate` and `afterelementupdate`:\n - afteritemstatechange\n - afteritemstatesclear\n - afteritemvisibilitychange\n - beforeitemstatechange\n - beforeitemstatesclear\n - beforeitemvisibilitychange\n- The following events have been changed:\n - The `graphstatechange` event has been changed to `beforeelementstatechange` / `afterelementstatechange`.\n - The `viewportchange` event has been changed to `beforetransform` / `aftertransform`.\n\nFor a complete list of events, please refer to [Event](/en/api/reference/g6#event).\n\n\n\n---\ntitle: 升级到 5.0\norder: 6\n---\n\n本文档将引导你从 G6 `4.x` 版本升级到 `5.x` 版本。如果你使用的是 `3.x` 版本,请先升级到 `4.x` 版本。\n\n## 升级前准备\n\n1. 请确保当前 git 分支是干净的,没有未提交的代码。\n2. 参考 [安装](/manual/getting-started/installation) 文档安装 `5.x` 版本,并移除 `4.x` 版本依赖。\n\n## 开始升级\n\n### 数据\n\n新版本的数据格式有所变化,具体如下:\n\n1. `nodes` `edges` `combos` 中所有样式属性都需要放在 `style` 中,`data` 中存放数据属性:\n\n```typescript\n// 4.x\nconst data = {\n nodes: [\n { id: 'node1', label: 'node1', size: 20 },\n { id: 'node2', label: 'node2', size: 20 },\n ],\n edges: [{ source: 'node1', target: 'node2' }],\n};\n\n// 5.x\nconst data = {\n nodes: [\n // label 为非样式属性,放在 data 中,可在样式映射函数中访问\n // size 为样式属性,放在 style 中\n { id: 'node1', data: { label: 'node1' }, style: { size: 20 } },\n { id: 'node2', data: { label: 'node2' }, style: { size: 20 } },\n ],\n edges: [{ source: 'node1', target: 'node2' }],\n};\n```\n\n由于我们重新设计实现了元素,新的元素配置项请参考相应文档进行修改:\n\n- [Node](/manual/element/node/overview)\n- [Edge](/manual/element/edge/overview)\n- [Combo](/manual/element/combo/overview)\n\n2. 如果要在数据中指定元素类型,可以使用 `type` 属性:\n\n```typescript\n{\n nodes: [\n // 指定节点类型为 rect\n { id: 'node1', type: 'rect' },\n ];\n}\n```\n\n### 配置项\n\n变更 **fitView / fitCenter / fitViewPadding**\n\n- `fitView` 和 `fitCenter` 配置项已经合并为 `autoFit`\n- 若要使用 `fitView`,可以配置为 `autoFit: 'view'`\n- 若要使用 `fitCenter`,可以配置为 `autoFit: 'center'`\n- 也可以传入对象进行完整配置:\n\n```js\nautoFit: {\n type: 'view',\n options: {\n // ...\n }\n}\n```\n\n- `fitViewPadding` 已变更为 `padding`\n\n移除 **linkCenter**\n\n5.x 的边连接机制会按照如下顺序依次尝试连接到节点/Combo:\n\n1. 连接桩\n2. 轮廓\n3. 中心\n\n移除 **groupByTypes**\n\n移除 **autoPaint**\n\n请手动调用 `render` 或 `draw` 方法进行绘制。\n\n变更 **modes**\n\n5.x 已经移除交互模式,你可以通过设置 `behaviors` 来切换当前启用的交互行为。\n\n```typescript\n// 4.x\n{\n modes: {\n default: ['drag-canvas', 'zoom-canvas'],\n preview: ['drag-canvas'],\n },\n}\n\ngraph.setMode('preview');\n```\n\n```typescript\n\n// 5.x\n{\n behaviors: ['drag-canvas', 'zoom-canvas'],\n}\n\ngraph.setBehaviors(['drag-canvas']);\n\n```\n\n变更 **defaultNode / defaultEdge / defaultCombo**\n\n元素样式已移至 `[element].style` 中,如 `defaultNode` 变更为 `node.style`:\n\n```typescript\n// 4.x\n{\n defaultNode: {\n size: 20,\n fill: 'red',\n }\n}\n\n// 5.x\n{\n node: {\n style: {\n size: 20,\n fill: 'red',\n }\n }\n}\n```\n\n变更 **nodeStateStyles / edgeStateStyles / comboStateStyle**\n\n元素状态样式已移至 `[element].state` 中,如 `nodeStateStyles` 变更为 `node.stateStyles`:\n\n```typescript\n// 4.x\n{\n nodeStateStyles: {\n selected: {\n fill: 'red',\n }\n }\n}\n\n// 5.x\n{\n node: {\n state: {\n selected: {\n fill: 'red',\n }\n }\n }\n}\n```\n\n变更 **animate / animateCfg**\n\n- `animate` 配置项已变更为 `animation`\n- `animate` 和 `animateCfg` 已合并为 `animation`\n\n```typescript\n// 4.x\n{\n animate: true,\n}\n\n// 5.x\n{\n animation: true,\n}\n{\n animation: {\n duration: 500,\n easing: 'easeLinear',\n }\n}\n```\n\n变更 **minZoom / maxZoom**\n\n`minZoom` 和 `maxZoom` 已合并为 `zoomRange`\n\n```typescript\n// 4.x\n{\n minZoom: 0.5,\n maxZoom: 2,\n}\n\n// 5.x\n{\n zoomRange: [0.5, 2],\n}\n```\n\n变更 **renderer**\n\nG6 5.x 支持多层画布,默认使用 `canvas` 渲染。\n\nrenderer 不再支持字符串类型,变更为回调函数:\n\n```typescript\n// 4.x\nvar options = {\n renderer: 'svg',\n};\n\n// 5.x\nimport { Renderer } from '@antv/g-svg';\n\n{\n renderer: () => new Renderer(),\n}\n```\n\n移除 **enabledStack / maxStep**\n\n5.x 已移除内置撤销重做功能,相关能力请使用插件实现。\n\n### API\n\n变更 **data / save / read / changeData**\n\n5.x 提供了全新的数据 API,详见 [数据 API](/api/data)。\n\n- 4.x `data` `changeData` 方法使用 5.x `setData` 替代\n- 4.x `save` 方法使用 5.x `getData` 替代\n- 4.x `read` 方法使用 5.x `setData` + `render` 替代\n\n变更 **get / set**\n\n若要访问 Graph options,请使用 `getOptions` 或者 `getXxx` API,例如 `getZoomRange` `getBehaviors` 等。 `set` 同理。\n\n变更 **getContainer**\n\n暂不支持直接获取容器的 API,但可以通过 `graph.getCanvas().getContainer()` 获取。\n\n> 绝大部分情况下,你都不需要直接操作容器。\n\n移除 **getGroup**\n\n变更 **getMinZoom / getMaxZoom**\n\n使用 `getZoomRange` 获取。\n\n变更 **setMinZoom / setMaxZoom**\n\n使用 `setZoomRange` 方法设置。\n\n变更 **getWidth / getHeight**\n\n使用 `getSize` 获取。\n\n变更 **changeSize**\n\n使用 `setSize` 设置。\n\n变更 **zoom**\n\n变更为 `zoomBy`。\n\n变更 **translate**\n\n变更为 `translateBy`。\n\n变更 **moveTo**\n\n变更为 `translateTo`。\n\n变更 **focusItem**\n\n变更为 `focusElement`。\n\n移除 **addItem / updateItem / removeItem**\n\n通过 `addData` / `updateData` / `removeData` 方法操作数据来添加或删除元素。\n\n移除 **refreshItem**\n\n移除 **refreshPositions**\n\n移除 **updateCombo**\n\n移除 **updateCombos**\n\n移除 **updateComboTree**\n\n变更 **node / edge / combo**\n\n使用 `setNode` / `setEdge` / `setCombo` 方法替代。\n\n变更 **showItem / hideItem**\n\n使用 `setElementVisibility` 方法替代。\n\n移除 **getNodes / getEdges / getCombos / getComboChildren /getNeighbors /find /findById / findAll /findAllByState**\n\n5.x 不支持直接获取元素实例。\n\n- 若要获取元素数据,使用 `getData` `getNodeData` `getEdgeData` `getComboData` 方法,支持传入元素 id 进行查找。\n- 获取子节点数据,使用 `getChildrenData` 方法。\n- 获取邻居节点数据,使用 `getNeighborNodesData` 方法。\n- 基于状态查找元素数据,使用 `getElementDataByState`。\n\n变更 **collapseCombo / expandCombo**\n\n使用 `collapseElement` / `expandElement` 方法替代。\n\n移除 **collapseExpandCombo**\n\n移除 **createCombo**\n\n通过 `addData` / `addComboData` 方法添加 Combo。\n\n移除 **uncombo**\n\n通过 `removeData` / `removeComboData` 方法移除 Combo。\n\n变更 **setItemState**\n\n使用 `setElementState` 方法替代。\n\n移除 **clearItemStates**\n\n- 清除单个元素所有状态:`graph.setElementState(id, [])`\n- 清除多个元素所有状态:`graph.setElementState({ id1: [], id2: [] })`\n\n移除 **priorityState**\n\n`setElementState` 时状态数组中靠后的状态优先级更高。\n\n移除 **setMode**\n\n使用 `setBehaviors` 来设置当前交互。\n\n移除 **setCurrentMode**\n\n变更 **layout**\n\n不支持参数,如需配置布局,请使用 `setLayout`。\n\n变更 **updateLayout**\n\n变更为 `setLayout`。\n\n移除 **destroyLayout**\n\n变更 **addBehaviors / removeBehaviors**\n\n使用 `setBehaviors` 替代。\n\n移除 **createHull / getHulls / removeHull / removeHulls**\n\n- 多个 `Hull` 需在 `plugins` 中配置多个 `hull` 插件,如:\n\n```typescript\n{\n plugins: ['hull', 'hull'],\n};\n```\n\n- `Hull` 的获取、更新、移除操作通过 `setPlugins`, `updatePlugin` 实现。\n\n暂未提供 **getNodeDegree**\n\n暂未提供 **getShortestPathMatrix**\n\n暂未提供 **getAdjMatrix**\n\n移除 **pushStack / getUndoStack / getRedoStack / getStackData / clearStack**\n\n所有撤销重做相关 API 请获取到对应插件后调用 API,例:\n\n```typescript\n// 'history' 为使用插件时配置的 key\nconst history = graph.getPluginInstance('history');\n\nhistory.redo();\n```\n\n移除 **positionsAnimate / stopAnimate / isAnimating**\n\n动画相关信息通过事件抛出:\n\n- 动画开始事件:`beforeanimate`\n- 动画结束事件:`afteranimate`\n- 停止动画:\n\n```typescript\ngraph.on('beforeanimate', (event) => {\n event.animation.stop();\n});\n```\n\n变更 **getPointByClient / getClientByPoint / getPointByCanvas / getCanvasByPoint / getGraphCenterPoint / getViewPortCenterPoint**\n\nG6 5.x 采用了与 4.x 不同的坐标系,详见 [坐标系](/manual/further-reading/coordinate)。\n\n移除 **setTextWaterMarker / setImageWaterMarker**\n\n要使用水印功能,请参考 [水印](/manual/plugin/watermark)插件。\n\n变更 **toFullDataURL**\n\n使用 `toDataURL` 替代,指定参数为:`mode: 'overall'`\n\n```typescript\ngraph.toDataURL({ mode: 'overall' });\n```\n\n移除 **downloadFullImage / downloadImage**\n\n仅提供导出为 `DataURL` 的能力,如需下载图片,请参考如下实例代码:\n\n```typescript\nasync function downloadImage() {\n const dataURL = await graph.toDataURL();\n const [head, content] = dataURL.split(',');\n const contentType = head.match(/:(.*?);/)![1];\n\n const bstr = atob(content);\n let length = bstr.length;\n const u8arr = new Uint8Array(length);\n\n while (length--) {\n u8arr[length] = bstr.charCodeAt(length);\n }\n\n const blob = new Blob([u8arr], { type: contentType });\n\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'graph.png';\n a.click();\n}\n```\n\n移除 **clear**\n\n使用 `setData` + `draw` 清空数据和画布。\n\n### 扩展注册\n\n与 G6 4.x 不同,G6 5.x 使用的统一的扩展注册函数(register),你可以参考 [注册扩展](/manual/graph/extension#注册扩展) 来注册 G6 扩展。\n\n下列 G6 4.x 的注册函数已经废除:\n\n- registerNode\n- registerEdge\n- registerCombo\n- registerLayout\n- registerBehavior\n\n### 事件\n\n与 G6 4.x 相比,G6 5.x 的事件但存下如下差异:\n\n- 移除了 `mouse` 和 `touch` 事件,统一使用 `pointer` 事件\n- 生命周期事件名命名格式通常为: `before/after` + `对象/属性` + `操作`,例如:`beforeelementcreate` 表示在创建元素前触发\n- 下列事件已被移除:\n - afteractivaterelations\n - afteradditem\n - aftercreateedge\n - aftergraphrefresh\n - aftergraphrefreshposition\n - afteritemrefresh\n - aftermodechange\n - afterremoveitem\n - afterupdateitem\n - beforeadditem\n - beforecreateedge\n - beforegraphrefresh\n - beforegraphrefreshposition\n - beforeitemrefresh\n - beforemodechange\n - beforeremoveitem\n - beforeupdateitem\n - dragnodeend\n - nodeselectchange\n - stackchange\n - tooltipchange\n- 下列元素变更事件被移除,但你仍可通过 `beforeelementupdate` 和 `afterelementupdate` 获取:\n - afteritemstatechange\n - afteritemstatesclear\n - afteritemvisibilitychange\n - beforeitemstatechange\n - beforeitemstatesclear\n - beforeitemvisibilitychange\n- 下列事件有所变更:\n - graphstatechange 事件变更为 beforeelementstatechange / afterelementstatechange\n - viewportchange 事件变更为 beforetransform / aftertransform\n\n完整的事件列表请参考 [事件](/api/event)。\n\n\n\n# @antv/g6-cli\n\n`@antv/g6-cli` is a G6 template generation tool that comes with several templates.\n\nCurrently, it owns a built-in template called `extension`. This template handles the boilerplate setup, which encompasses a seamless local development environment, linting, code formatting, Jest for snapshot testing and bundling with Rollup etc.\n\n`@antv/g6-cli` i\n\n## Getting Started\n\nTo start using `@antv/g6-cli`, you'll first need to install it globally.\n\n```bash\nnpm i @antv/g6-cli -g\n```\n\nOnce installed, you can easily scaffold a new project:\n\n```bash\ncreate-g6\n```\n\nThen follow the prompts!\n\n![prompts](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*09BKQrIcZUMAAAAAAAAAAAAADmJ7AQ/original)\n\nYou can also directly specify the project name and the template you want to use via additional command line options. For example, to scaffold a **G6 Extension** project, run:\n\n```bash\ncreate-g6 g6-extension-test --template extension\n```\n\n\n\n## 3D extension for G6\n\n\n\n\nThis extension package provides 3D elements, behaviors and plugins for G6.\n\n## Usage\n\n1. Install\n\n```bash\nnpm install @antv/g6-extension-3d\n```\n\n2. Import and Register\n\n> Where renderer, elements and lighting are necessary\n\n```js\nimport { ExtensionCategory, register } from '@antv/g6';\nimport { DragCanvas3D, Light, Line3D, Sphere, renderer } from '@antv/g6-extension-3d';\n\n// 3d light plugin\nregister(ExtensionCategory.PLUGIN, '3d-light', Light);\n// sphere node element\nregister(ExtensionCategory.NODE, 'sphere', Sphere);\n// line edge element\nregister(ExtensionCategory.EDGE, 'line3d', Line3D);\n// drag canvas in 3d scene\nregister(ExtensionCategory.BEHAVIOR, 'drag-canvas-3d', DragCanvas3D);\n// camera setting plugin\nregister(ExtensionCategory.PLUGIN, 'camera-setting', CameraSetting);\n```\n\n3. Use\n\n```js\nimport { Graph } from '@antv/g6';\n\nconst graph = new Graph({\n container: 'container',\n renderer, // use 3d renderer\n data: {\n // your data\n },\n node: {\n type: 'sphere', // use sphere node\n },\n edge: {\n type: 'line3d', // use 3d line edge\n },\n behaviors: ['drag-canvas-3d'],\n plugins: [\n // set camera configs, see: https://g.antv.antgroup.com/en/api/camera/intro\n {\n type: 'camera-setting',\n projectionMode: 'perspective',\n near: 0.1,\n far: 1000,\n fov: 45,\n aspect: 1,\n },\n // add directional light\n {\n type: '3d-light',\n directional: {\n direction: [0, 0, 1],\n },\n },\n ],\n});\n```\n\n## Resources\n\n- [Lite Solar System](https://g6.antv.antgroup.com/en/examples/feature/default/#lite-solar-system)\n- [3D Node](https://g6.antv.antgroup.com/en/examples/element/node/#3d-node)\n\n\n\n## React extension for G6\n\n\n\nThis extension allows you to define G6 node by React component and JSX syntax.\n\n## Usage\n\n1. Install\n\n```bash\nnpm install @antv/g6-extension-react\n```\n\n2. Import and Register\n\n```js\nimport { ExtensionCategory, register } from '@antv/g6';\nimport { ReactNode } from '@antv/g6-extension-react';\n\nregister(ExtensionCategory.NODE, 'react', ReactNode);\n```\n\n3. Define Node\n\nReact Node:\n\n```jsx\nconst ReactNode = () => {\n return
node
;\n};\n```\n\nG Node:\n\n```jsx\nimport { Group, Rect, Text } from '@antv/g6-extension-react';\n\nconst GNode = () => {\n return \n \n \n \n};\n```\n\n4. Use\n\nUse ReactNode:\n\n```jsx\nconst graph = new Graph({\n // ... other options\n node: {\n type: 'react',\n style: {\n component: () => ,\n },\n },\n});\n```\n\nUse GNode:\n\n```jsx\nconst graph = new Graph({\n // ... other options\n node: {\n type: 'g',\n style: {\n component: () => ,\n },\n },\n});\n```\n\n## Q&A\n\n1. Difference between ReactNode and GNode\n\nReactNode is a React component, while GNode support jsx syntax but can only use G tag node.\n\n## Resources\n\n- [React node](https://g6.antv.antgroup.com/examples/element/custom-node/#react-node)\n- [G node with JSX syntax](https://g6.antv.antgroup.com/en/examples/element/custom-node/#react-g)\n\n\n\n## SSR extension for G6 5.0\n\nThis extension package provides SSR support for G6 5.0, which supports canvas rendering in server side.\n\n## Usage\n\n### Install\n\n```bash\nnpm install @antv/g6-ssr\n```\n\n### Render in JavaScript API\n\n> For complete options, please refer to [G6 Graph Options](https://g6.antv.antgroup.com/api/graph/option)\n\n```js\nimport { createGraph } from '@antv/g6-ssr';\n\nconst graph = await createGraph({\n width: 500,\n height: 500,\n imageType: 'png', // or 'jpeg'\n data: {\n // data\n },\n // other options\n});\n\ngraph.exportToFile('image');\n// -> image.png\n\ngraph.toBuffer();\n// -> get buffer\n```\n\n### Render in CLI\n\n```bash\nnpx g6-ssr export -i [graph-options].json -o ./image\n```\n\n### Export SVG / PDF\n\nWhen render in JavaScript API, you can pass `outputType` option to export SVG or PDF.\n\n```js\nconst graph = await createGraph({\n width: 500,\n height: 500,\n data: {\n // data\n },\n outputType: 'svg', // or 'pdf'\n // other options\n});\n```\n\nWhen render in CLI, you can pass `-t` or `--type` option to export SVG or PDF.\n\n```bash\nnpx g6-ssr export -i [graph-options].json -o ./file -t pdf\n```\n\n### Register Custom G6 Extensions\n\nIf you need to register custom extensions of G6, please use the `registry` function exported from `@antv/g6-ssr`.\n\n```js\nimport { createGraph, registry } from '@antv/g6-ssr';\nimport { BaseNode, ExtensionCategory } from '@antv/g6';\n\nclass CustomNode extends BaseNode {\n // custom node\n}\n\nregistry(ExtensionCategory.Node, 'custom-node', CustomNode);\n\nconst graph = await createGraph({\n width: 500,\n height: 500,\n node: {\n type: 'custom-node',\n // other options\n },\n // other options\n});\n```\n\n### Use Plugins\n\nWhen using G6-SSR, you can also use G render plugins. Here's how to use plugins in server-side rendering:\n\n```js\nimport { createGraph } from '@antv/g6-ssr';\nimport { Plugin as RoughCanvasPlugin } from '@antv/g-plugin-rough-canvas-renderer';\n\nconst graph = await createGraph({\n width: 500,\n height: 500,\n renderPlugins: [new RoughCanvasPlugin()],\n data: {\n // data\n },\n});\n```\n\n## License\n\nMIT\n\n\n\n> Code copied from [hull-js](https://github.com/hull/hull-js), and fix the issue [[Bug]: hull.js Code Injection Vulnerability #6605](https://github.com/antvis/G6/issues/6605).\n\n\n"; if (navigator.clipboard) { navigator.clipboard.writeText(snippet).then(function() { alert('All Agent Skills & Rules copied to clipboard!'); }); } } function copyFullPrompt() { var prompt = '\n# 1. Technical Documentation:\nGET https://yakaai.com/api/cheat/antvis/G6\n\n# 2. Agent Skills & Rules:\nGET https://yakaai.com/api/skills/antvis/G6\n\n# 3. Model Context Protocol (MCP):\nGET https://yakaai.com/mcp/antvis/G6?format=md\n'; if (navigator.clipboard) { navigator.clipboard.writeText(prompt).then(function() { alert('Full AI Context Prompt copied to clipboard!'); }); } } function copySkillsPrompt() { var prompt = '\n# Agent Skills & Directives (.cursorrules, CLAUDE.md, AGENTS.md):\nGET https://yakaai.com/api/skills/antvis/G6\n'; if (navigator.clipboard) { navigator.clipboard.writeText(prompt).then(function() { alert('Skills Prompt copied to clipboard!'); }); } }