mirror of
https://gitee.com/antv/g6.git
synced 2024-11-30 02:38:20 +08:00
docs: tutorial docs
This commit is contained in:
parent
4587c4d5e1
commit
b201af82aa
@ -165,9 +165,9 @@ export class DataController {
|
||||
* Get the extensions from useLib.
|
||||
*/
|
||||
private getExtensions() {
|
||||
const { transform = [] } = this.graph.getSpecification();
|
||||
const { transforms = [] } = this.graph.getSpecification();
|
||||
const requiredTransformers = ['validate-data'];
|
||||
return [...transform, ...requiredTransformers]
|
||||
return [...transforms, ...requiredTransformers]
|
||||
.map((config) => ({
|
||||
config,
|
||||
func: getExtension(config, registry.useLib, 'transform'),
|
||||
|
@ -144,12 +144,12 @@ export class DragCanvas extends Behavior {
|
||||
.map((node) => node.id)
|
||||
.filter((id) => graph.getItemVisible(id) === true);
|
||||
// draw node's keyShapes on transient, and then hidden the real nodes;
|
||||
this.hiddenNodeIds.forEach((id) => {
|
||||
graph.drawTransient('node', id, {
|
||||
onlyDrawKeyShape: true,
|
||||
});
|
||||
});
|
||||
graph.hideItem(this.hiddenNodeIds, true);
|
||||
// this.hiddenNodeIds.forEach((id) => {
|
||||
// graph.drawTransient('node', id, {
|
||||
// onlyDrawKeyShape: true,
|
||||
// });
|
||||
// });
|
||||
// graph.hideItem(this.hiddenNodeIds, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -247,12 +247,12 @@ export class DragCanvas extends Behavior {
|
||||
if (this.hiddenEdgeIds) {
|
||||
graph.showItem(this.hiddenEdgeIds, true);
|
||||
}
|
||||
if (this.hiddenNodeIds) {
|
||||
this.hiddenNodeIds.forEach((id) => {
|
||||
this.graph.drawTransient('node', id, { action: 'remove' });
|
||||
});
|
||||
graph.showItem(this.hiddenNodeIds, true);
|
||||
}
|
||||
// if (this.hiddenNodeIds) {
|
||||
// this.hiddenNodeIds.forEach((id) => {
|
||||
// this.graph.drawTransient('node', id, { action: 'remove' });
|
||||
// });
|
||||
// graph.showItem(this.hiddenNodeIds, true);
|
||||
// }
|
||||
graph.stopBatch();
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export interface Specification<
|
||||
|
||||
/** data */
|
||||
data?: DataConfig;
|
||||
transform?:
|
||||
transforms?:
|
||||
| string[]
|
||||
| {
|
||||
type: string;
|
||||
|
97034
packages/g6/tests/datasets/eva-3d-data.json
Normal file
97034
packages/g6/tests/datasets/eva-3d-data.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1413,7 +1413,7 @@ export default (context: TestCaseContext, options?: {}) => {
|
||||
const { width, height } = context;
|
||||
const graph = new CustomGraph({
|
||||
...context,
|
||||
transform: [
|
||||
transforms: [
|
||||
'transform-v4-data',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
@ -1479,7 +1479,7 @@ export default (context: TestCaseContext, options?: {}) => {
|
||||
|
||||
// const graph = new CustomGraph({
|
||||
// ...context,
|
||||
// transform: [
|
||||
// transforms: [
|
||||
// 'transform-v4-data',
|
||||
// 'edge-cluster',
|
||||
// {
|
||||
|
@ -99,11 +99,10 @@ const data = {
|
||||
|
||||
- `nodes` 数组中包含节点对象。每个节点对象中唯一的、必要的 `id` 以标识不同的节点,`x`、 `y` 指定该节点的位置;
|
||||
- `edges` 数组中包含边对象。`source` 和 `target` 是每条边的必要属性,分别代表了该边的起始点 `id` 与 目标点 `id`。
|
||||
- 点和边的其他属性参见链接:[内置节点](/zh/docs/manual/middle/elements/nodes/defaultNode) 和 [内置边](/en/docs/manual/middle/elements/edges/defaultEdge)。
|
||||
|
||||
### Step 3 创建关系图
|
||||
|
||||
创建关系图(实例化)时,至少需要为图设置容器、宽和高。
|
||||
创建关系图(实例化)时,至少需要为图设置容器:
|
||||
|
||||
```javascript
|
||||
const graph = new Graph({
|
||||
@ -142,7 +141,7 @@ graph.read(data); // 读取 Step 2 中的数据源到图上
|
||||
<div id="container"></div>
|
||||
|
||||
/* 引入 G6 */
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0/dist/g6.min.js"></script>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
|
||||
<script>
|
||||
// 定义数据源
|
||||
|
@ -31,7 +31,7 @@ const rect = group.addShape('rect', {
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*lkUoTp5xXmoAAAAAAAAAAABkARQnAQ' width='200' alt='img'/>
|
||||
|
||||
Now, we call the transform:
|
||||
Now, we call the transforms:
|
||||
|
||||
```javascript
|
||||
rect.transform([
|
||||
|
@ -326,7 +326,7 @@ The format of business data varies and may not conform to the data format of G6.
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
// ... other graph configurations
|
||||
transform: [
|
||||
transforms: [
|
||||
'transform-v4-data', // built-in data processor, converts v4 data format to v5
|
||||
{
|
||||
// built-in data processor, maps the size of nodes to the 'value' field of node data, normalizes the size range to [4, 28]
|
||||
@ -354,7 +354,7 @@ const ExtGraph = extend(Graph, {
|
||||
});
|
||||
const graph = new ExtGraph({
|
||||
// ... other graph configurations
|
||||
transform: [
|
||||
transforms: [
|
||||
'transform-v4-data', // built-in data processor, converts v4 data format to v5
|
||||
'custom-data-transform', // use custom data processor
|
||||
],
|
||||
|
@ -327,7 +327,7 @@ const graph = new Graph({
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
// ... 其他图配置
|
||||
transform: [
|
||||
transforms: [
|
||||
'transform-v4-data', // 内置的数据处理器,将 v4 的数据格式转换为 v5
|
||||
{
|
||||
// 内置的数据处理器,节点大小映射到节点数据的 value 字段上,大小范围归一化到 [4, 28]
|
||||
@ -358,7 +358,7 @@ const ExtGraph = extend(Graph, {
|
||||
|
||||
const graph = new ExtGraph({
|
||||
// ... 其他图配置
|
||||
transform: [
|
||||
transforms: [
|
||||
'transform-v4-data', // 内置的数据处理器,将 v4 的数据格式转换为 v5
|
||||
'custom-data-transform', // 使用自定义的数据处理器
|
||||
],
|
||||
|
@ -3,34 +3,75 @@ title: Animation*
|
||||
order: 6
|
||||
---
|
||||
|
||||
The animation mechanism is too complicated to understand by beginners and out of the scope of the tutorial. In this chapter, we only introduce the animation in G6 briefly. For more information, please refer to [Basic Animation](/en/docs/manual/middle/animation).
|
||||
[Animation Configuration DEMO](https://g6-next.antv.antgroup.com/examples/scatter/changePosition/#itemAnimates)
|
||||
|
||||
There are two levels of animation in G6:
|
||||
G6 5.0 provides a standardized way to describe animations. You can configure animations for different elements in different scenarios when instantiating the graph. You can specify the `animates` field in the `node` / `edge` / `combo` field of the graph configuration mentioned above:
|
||||
|
||||
- GLobal animation: Transform the graph animatively when the changes are global;
|
||||
- Item animation: The animation on a node or an edge.
|
||||
|
||||
## Global Animation
|
||||
|
||||
The global animation is controlled by Graph instance. It takes effect when some global changes happen, such as:
|
||||
|
||||
- `graph.updateLayout(cfg)`
|
||||
|
||||
Configure `animate: true` when instantiating a graph to achieve it.
|
||||
|
||||
**Example**
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ... // Other configurations
|
||||
animate: true, // Boolean, whether to activate the animation when global changes happen
|
||||
});
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
node: {
|
||||
animates: {
|
||||
buildIn: [...],
|
||||
buildOut: [...],
|
||||
update: [...],
|
||||
show: [...],
|
||||
hide: [...],
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Item Animation
|
||||
Or use the functional mapping method for `node` / `edge` / `combo`:
|
||||
|
||||
G6 allows user to customize animation for item when register a type of custom item. <br /> <img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*hYJSQaneVmgAAAAAAAAAAABkARQnAQ' width=330 alt='img'/>
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
node: model => {
|
||||
const { id, data } = model
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
// ...Other style configurations
|
||||
animates: {
|
||||
buildIn: [...],
|
||||
buildOut: [...],
|
||||
update: [...],
|
||||
show: [...],
|
||||
hide: [...],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*-90pSrm4hkUAAAAAAAAAAABkARQnAQ' width=330 alt='img' />
|
||||
We have standardized the animation into five scenarios that occur at different times for different shapes: buildIn, buildOut, update (data/state update), show (appearance, relative to hide), and hide (hide). For each scenario, you can specify different animations for different shapes and fields, and you can also specify the animation configuration and execution order. For example, the following code specifies various animations for different shapes during updates:
|
||||
|
||||
For more cases, please refer to [Animation Case](/en/examples/scatter/node).
|
||||
```typescript
|
||||
update: [
|
||||
{
|
||||
// Animate the entire node (shapeId: 'group') when x and y change
|
||||
fields: ['x', 'y'],
|
||||
shapeId: 'group',
|
||||
duration: 500,
|
||||
},
|
||||
{
|
||||
// Animate the opacity of haloShape when the selected or active state changes
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
duration: 500,
|
||||
},
|
||||
// Animate the fill and r of the keyShape in the order specified by the 'order' field, achieving sequential animation effects
|
||||
{
|
||||
fields: ['fill'],
|
||||
shapeId: 'keyShape',
|
||||
order: 0,
|
||||
},
|
||||
{
|
||||
fields: ['r'],
|
||||
shapeId: 'keyShape',
|
||||
order: 1,
|
||||
},
|
||||
];
|
||||
```
|
||||
|
@ -1,36 +1,77 @@
|
||||
---
|
||||
title: 动画(选读)
|
||||
title: 动画
|
||||
order: 6
|
||||
---
|
||||
|
||||
由于动画机制较为复杂,我们未在 Tutorial-案例 中增加动画。本文简单描述了 G6 中的动画,希望快速上手的用户可以跳过本文,希望深入了解的用户可参见:[基础动画](/zh/docs/manual/middle/animation)。
|
||||
[动画配置 DEMO](https://g6-next.antv.antgroup.com/examples/scatter/changePosition/#itemAnimates)
|
||||
|
||||
G6 的动画分为两个层次:
|
||||
G6 5.0 提供了规范化的动画描述方式,您可以在实例化图时,为各个元素配置不同场景下的动画。您可以在上面介绍的 graph 配置的 `node` / `edge` / `combo` 字段中指定 `animates` 字段:
|
||||
|
||||
- 图全局动画:图整体变化时的动画过渡;
|
||||
- 元素动画:节点和边的动画效果。
|
||||
|
||||
## 全局动画
|
||||
|
||||
G6 的全局动画指通过图实例进行操作时,产生的动画效果。例如:
|
||||
|
||||
- `graph.updateLayout(cfg)`
|
||||
|
||||
通过实例化图时配置 `animate: true`,可以达到每次进行上述操作时,动画效果变化的目的。
|
||||
|
||||
**例子**
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ... // 其他配置项
|
||||
animate: true, // Boolean,可选,全局变化时否使用动画过渡
|
||||
});
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
node: {
|
||||
animates: {
|
||||
buildIn: [...],
|
||||
buildOut: [...],
|
||||
update: [...],
|
||||
show: [...],
|
||||
hide: [...],
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 元素动画
|
||||
或 `node` / `edge` / `combo` 的函数式映射方式:
|
||||
|
||||
G6 允许用户通过自定义节点/边的方式,给元素增加动画效果,如下:<br /> <img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*hYJSQaneVmgAAAAAAAAAAABkARQnAQ' width=330 alt='img'/>
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
node: model => {
|
||||
const { id, data } = model
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
// ... 其他样式配置
|
||||
animates: {
|
||||
buildIn: [...],
|
||||
buildOut: [...],
|
||||
update: [...],
|
||||
show: [...],
|
||||
hide: [...],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*-90pSrm4hkUAAAAAAAAAAABkARQnAQ' width=330 alt='img' />
|
||||
我们规范了动画的五个场景,发生在各个图形的不同时机:入场(buildId)、出场(buildOut)、update(数据/状态更新)、show(出现,相对于 hide)、hide(隐藏)。每个场景的可以为不同的图形、不同的字段指定动画,还可以指定动画的配置和执行顺序。例如,下面表达了指定各类更新时的各种图形的动画:
|
||||
|
||||
更多关于动画的案例请参考 [G6 中的动画案例](/zh/examples/scatter/node)。
|
||||
```typescript
|
||||
update: [
|
||||
{
|
||||
// 整个节点(shapeId: 'group')在 x、y 发生变化时,动画更新
|
||||
fields: ['x', 'y'],
|
||||
shapeId: 'group',
|
||||
duration: 500,
|
||||
},
|
||||
{
|
||||
// 在 selected 和 active 状态变化导致的 haloShape opacity 变化时,使 opacity 带动画地更新
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
duration: 500,
|
||||
},
|
||||
// 当 keyShape 的 fill、r 同时发生变化时,按照 order 指定的顺序带动画地更新,可以实现依次动画的效果
|
||||
{
|
||||
fields: ['fill'],
|
||||
shapeId: 'keyShape',
|
||||
order: 0,
|
||||
},
|
||||
{
|
||||
fields: ['r'],
|
||||
shapeId: 'keyShape',
|
||||
order: 1,
|
||||
},
|
||||
];
|
||||
```
|
||||
|
@ -3,153 +3,154 @@ title: Interaction Behavior
|
||||
order: 4
|
||||
---
|
||||
|
||||
G6 encapsulates a set of interaction behaviors. Now we add simple some behaviors to **Tutorial Demo**: hover node, click node, click edge, drag cavas, zoom canvas. The expected result:
|
||||
G6 encapsulates a series of interaction methods for users to use directly. This article will add simple interactions to the **Tutorial example**: clicking on nodes, clicking on edges, selecting nodes by dragging a box, zooming the canvas, and dragging the canvas. The target effect for this section is as follows:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*dijtQ6nB5Y4AAAAAAAAAAABkARQnAQ' width=500 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*kgrxQJnxNPoAAAAAAAAAAAAADmJ7AQ/original' width=500 alt='img' />
|
||||
|
||||
<div style="text-align: center;"> Figure 1 **Tutorial Demo** with interaction behaviors</div>
|
||||
> Figure 1: Interaction effects of the Tutorial example.
|
||||
|
||||
## Basic Concept
|
||||
## Basic Concepts
|
||||
|
||||
### Interaction Behavior
|
||||
### Interaction Behaviors
|
||||
|
||||
G6 provides several **Built-in** interaction behaviors. You can enable these behaviors conveniently:
|
||||
The interaction behaviors in G6. G6 provides a series of **built-in** interaction behaviors that users can use directly. Essentially, these behaviors can be activated with a single click:
|
||||
|
||||
- `drag-canvas`: enable the canvas to be dragged;
|
||||
- `zoom-canvas`: enable the canvas to be zoomed;
|
||||
- `drag-canvas`: Drags the canvas.
|
||||
- `zoom-canvas`: Zooms the canvas.
|
||||
|
||||
Refer to [Interaction Behavior](/en/docs/manual/middle/states/defaultBehavior) document for more information.
|
||||
For more information, see [Interaction Behaviors](https://g6-next.antv.antgroup.com/apis/interfaces/behaviors/activate-relations-options).
|
||||
|
||||
### Mode
|
||||
### Interaction Modes
|
||||
|
||||
Mode is a mechanism for state management in G6. One mode is a set of several Behaviors. You can assemble different Behaviors to modes. The concept of mode is too complicated to understand for the beginners of G6. You do not need to know it well in this tutorial. For more information, please refer to [Interaction Mode](/en/docs/manual/middle/states/mode).
|
||||
Modes are the management mechanism for interaction behaviors in G6. A mode is a combination of multiple interaction behaviors and allows users to manage these behaviors by switching different modes. Due to the complexity of this concept, readers do not need to understand it in depth in this tutorial. For more information, see [Interaction Modes](https://g6-next.antv.antgroup.com/apis/interfaces/graph/i-graph).
|
||||
|
||||
### Interaction State
|
||||
### Interaction States
|
||||
|
||||
[State](/en/docs/manual/middle/states/state) is a mechanism of item state in G6. You can set different item styles for different states. When the state of an item is changed, the style will be updated automatically. For example, set the state `'click'` of a node as `true` or `false`, and set the node style of the state `'click'` with thicker stroke. This style will take effect when the state `'click'` is switched to `true`, and restore when `'click'` state is switched to `false`. There will be a specific in the Usage part.
|
||||
States are the state machine mechanism in G6. Users can set different states for elements (nodes/edges) in the graph and define different styles for these states. When the state changes, G6 automatically updates the style of the elements. For example, you can set the state `'click'` of a node to `true` or `false`, and set the style of the node in the `'click'` state to have a bold border. When the `'click'` state is switched to `true`, the border of the node becomes bold. When the `'click'` state is switched to `false`, the style of the node returns to the default state. The following usage examples will provide specific illustrations.
|
||||
|
||||
## Usage
|
||||
## Usage Method
|
||||
|
||||
### Built-in Behaviors: Drag and Zoom
|
||||
### Dragging and Zooming - Built-in Interaction Behaviors
|
||||
|
||||
Only assign `modes` when instantiating the graph, the corresponding built-in Behaviors will be enabled:
|
||||
Using the built-in behaviors in G6 is very simple. You only need to configure `modes` when instantiating the graph. To manage bundle size, some built-in interactions are not registered to the Graph instance in advance, and need to be registered as follows:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ... // Other configurations
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node'], // Allow users to drag canvas, zoom canvas, and drag nodes
|
||||
const { Graph: GraphBase, extend, Extensions } = G6;
|
||||
const Graph = extend(GraphBase, {
|
||||
behaviors: {
|
||||
// 'brush-select' is a built-in interaction, it is not registered in advance and needs to be imported from Extensions and registered as follows:
|
||||
'brush-select': Extensions.BrushSelect,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The code above uses the Behaviors by assigning their types. Besides, you can also configure the parameters for them, e.g. the sensitivity of zooming, max/min zoom ratio. Refer to [Ineteraction Behavior](/en/docs/manual/middle/states/defaultBehavior) document for more detail.
|
||||
|
||||
`modes` object above defines a set of interaction modes of the graph, where `default` is the default mode, which includes `'drag-canvas'`, `'zoom-canvas'`, and `'drag-node'`. You can add more modes with their Behaviors into `modes`, e.g. `edit` mode:
|
||||
You can view all the built-in interactions in [Interaction Behaviors](https://g6-next.antv.antgroup.com/apis/interfaces/behaviors/activate-relations-options). Apart from the interactions that are registered in advance, other interactions need to be registered using the above method.
|
||||
|
||||
```javascript
|
||||
// Different modes with different Behaviors
|
||||
// Pre-registered interactions
|
||||
{
|
||||
'drag-canvas': DragCanvas, // Drag the canvas
|
||||
'zoom-canvas': ZoomCanvas, // Zoom the canvas
|
||||
'drag-node': DragNode, // Drag nodes
|
||||
'drag-combo': DragCombo, // Drag combos
|
||||
'collapse-expand-combo': CollapseExpandCombo, // Expand/collapse combos
|
||||
'collapse-expand-tree': CollapseExpandTree, // Expand/collapse subtree
|
||||
'click-select': ClickSelect, // Click to select
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Note that the Graph returned by extend is used here
|
||||
const graph = new G6.Graph({
|
||||
// ...Other configurations
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'click-select', 'brush-select'],
|
||||
// Allow dragging canvas, zooming canvas, dragging nodes, clicking to select nodes, and brushing to select nodes
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
In addition to using the built-in interaction names directly, you can also configure parameters for the behavior, such as the sensitivity of zooming the canvas and the maximum/minimum zoom level. For specific usage, see [Behavior](https://g6-next.antv.antgroup.com/apis/interfaces/behaviors/zoom-canvas-options).
|
||||
|
||||
The modes in the above code defines the `modes` of G6. `default` is the default mode, and other modes can also be allowed, such as the `edit` mode edit. Different modes allow different user behaviors. For example, the default mode allows dragging the canvas, while the edit mode does not allow dragging the canvas:
|
||||
|
||||
```javascript
|
||||
// Example explaining different modes
|
||||
modes: {
|
||||
default: ['drag-canvas'],
|
||||
edit: []
|
||||
}
|
||||
```
|
||||
|
||||
Refer to [Interaction Mode](/en/docs/manual/middle/states/mode) and [Interaction Behavior](/en/docs/manual/middle/states/defaultBehavior) document for more detail.
|
||||
### Define the Interaction Styles
|
||||
|
||||
### Hover and Click to Change Styles
|
||||
Sometimes we want to change the element style to a specific style through interaction. As shown in Figure 1, the style changes when the mouse hovers over a node, clicks a node, or clicks an edge. This involves the concept of states in G6. In simple terms, whether `hover`, `click`, or any operation (can be a self-defined state name), can be called a state. Users can freely set the element style under different states. To achieve interactive style change, two steps are required:
|
||||
|
||||
Sometimes, the styles of the items interacted by users should be updated to make the response. As shown in figure 1, the styles are changed when user hovers the node, clicks the node, and clicks the edge. It is achieved by [State](/en/docs/manual/middle/states/state) mechanism. In other word, whether the item is clicked or hovered can be described as some states. You are able to set the styles for different states by two steps:
|
||||
- Step 1: Set the element style under each state;
|
||||
- Step 2: Listen to events and switch element states.
|
||||
|
||||
- Step 1: Set the styles for different states;
|
||||
- Step 2: Listen to the relative events and switch the states.
|
||||
#### Set the element style under each state
|
||||
|
||||
#### Set the State Styles
|
||||
|
||||
Set the state styles by `nodeStateStyles` and `edgeStateStyles` for nodes and edges respectively when instantiating a Graph. <br />The relative requirements in **Tutorial Demo** are:
|
||||
|
||||
- The color of the node is changed when mouse hover it;
|
||||
- The stroke of the node gets thicker and darker when user clicks it;
|
||||
- The edge become blue when user clicks it.
|
||||
|
||||
The following code sets the styles for nodes in the state of `hover` and `click`( = `true`), and the styles for edges in the state of `click` ( = `true`):
|
||||
When instantiating the graph, the `nodeState` and `edgeState` configurations can be used to configure the element styles under different states. G6 provides some preset state styles: 'selected', 'highlight', 'active', 'inactive', 'disable'. In the 'click-select' and 'brush-select' interactions, the default triggered state for nodes and edges is 'selected'. Therefore, even if `nodeState` and `edgeState` are not configured, we can see the selected state style response. If you need to customize the state style, you can configure `selectedState` as a custom string for 'click-select' and 'brush-select', and then configure the corresponding styles in the `nodeState` and `edgeState` configurations, for example:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ... // Other configurations
|
||||
// The set of styles of nodes in different states
|
||||
nodeStateStyles: {
|
||||
// The node style when the state 'hover' is true
|
||||
hover: {
|
||||
fill: 'lightsteelblue',
|
||||
const graph = new Graph({
|
||||
// ...Other configurations
|
||||
// Node state styles
|
||||
nodeState: {
|
||||
// Custom state name
|
||||
customstatename1: {
|
||||
// Configure for different shapes
|
||||
keyShape: {
|
||||
lineWidth: 2,
|
||||
stroke: 'red',
|
||||
},
|
||||
labelShape: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
},
|
||||
// The node style when the state 'click' is true
|
||||
click: {
|
||||
stroke: '#000',
|
||||
lineWidth: 3,
|
||||
},
|
||||
},
|
||||
// The edge styles in different states
|
||||
edgeStateStyles: {
|
||||
// The edge style when the state 'click' is true
|
||||
click: {
|
||||
stroke: 'steelblue',
|
||||
// Custom state name
|
||||
customstatename2: {
|
||||
keyShape: {
|
||||
lineDash: [2, 2],
|
||||
lineWidth: 4,
|
||||
stroke: 'blue',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Configure edgeState similarly
|
||||
});
|
||||
```
|
||||
|
||||
#### Listen to the Events and Switch the States
|
||||
#### Listen to events and switch element states
|
||||
|
||||
The listeners in G6 are mounted on the instance of Graph. `graph` is the instance of G6.Graph in the following code. `graph.on()` listens some event (`click` / `mouseenter` / `mouseleave` / ... all the events are collected in: [Event API](/en/docs/api/Event))of some type of item(`node` / `edge`)
|
||||
In addition to the built-in interactions, you can also call `graph.setItemState` to set the state of an element whenever needed. For example, when double-clicking a node:
|
||||
|
||||
```javascript
|
||||
// add listener on graph
|
||||
graph.on('itemType:event', (e) => {
|
||||
graph.on('node:dblclick', (e) => {
|
||||
graph.setItemState(e.itemId, 'customstatename1', true);
|
||||
});
|
||||
graph.on('canvas:click', (e) => {
|
||||
// Find the node IDs in the 'customstatename1' state
|
||||
const stateNodeIds = graph.findIdByState('node', 'customstatename1', true);
|
||||
// Batch cancel the state
|
||||
graph.setItemState(stateNodeIds, 'customstatename1', false);
|
||||
});
|
||||
```
|
||||
|
||||
All element listeners in G6 are mounted on the `graph` instance. In the code below, the graph object is an instance of G6.Graph, and the `graph.on()` function listens for a specific event (`click`/`mouseenter`/`mouseleave`, etc.) on a certain type of element (node/edge).
|
||||
|
||||
```javascript
|
||||
// Listeners on graph
|
||||
graph.on('ItemType:EventName', (e) => {
|
||||
// do something
|
||||
});
|
||||
```
|
||||
|
||||
Now, we add listeners to graph for **Tutorial Demo**, and update the states by `graph.setItemState()`:
|
||||
|
||||
```javascript
|
||||
// Mouse enter a node
|
||||
graph.on('node:mouseenter', (e) => {
|
||||
const nodeItem = e.item; // Get the target item
|
||||
graph.setItemState(nodeItem, 'hover', true); // Set the state 'hover' of the item to be true
|
||||
});
|
||||
|
||||
// Mouse leave a node
|
||||
graph.on('node:mouseleave', (e) => {
|
||||
const nodeItem = e.item; // Get the target item
|
||||
graph.setItemState(nodeItem, 'hover', false); // Set the state 'hover' of the item to be false
|
||||
});
|
||||
|
||||
// Click a node
|
||||
graph.on('node:click', (e) => {
|
||||
// Swich the 'click' state of the node to be false
|
||||
const clickNodes = graph.findAllByState('node', 'click');
|
||||
clickNodes.forEach((cn) => {
|
||||
graph.setItemState(cn, 'click', false);
|
||||
});
|
||||
const nodeItem = e.item; // et the clicked item
|
||||
graph.setItemState(nodeItem, 'click', true); // Set the state 'click' of the item to be true
|
||||
});
|
||||
|
||||
// Click an edge
|
||||
graph.on('edge:click', (e) => {
|
||||
// Swich the 'click' state of the edge to be false
|
||||
const clickEdges = graph.findAllByState('edge', 'click');
|
||||
clickEdges.forEach((ce) => {
|
||||
graph.setItemState(ce, 'click', false);
|
||||
});
|
||||
const edgeItem = e.item; // Get the clicked item
|
||||
graph.setItemState(edgeItem, 'click', true); // Set the state 'click' of the item to be true
|
||||
});
|
||||
```
|
||||
|
||||
## Complete Code
|
||||
|
||||
Here is the complete code:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@ -158,140 +159,173 @@ graph.on('edge:click', (e) => {
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<div id="container"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
<script>
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 800,
|
||||
height: 600,
|
||||
// Default properties for all the nodes
|
||||
defaultNode: {
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#fff',
|
||||
const { Graph: GraphBase, extend, Extensions } = G6;
|
||||
|
||||
// Custom data processor - degree calculator
|
||||
const degreeCalculator = (data, options, userGraphCore) => {
|
||||
const { edges, nodes } = data;
|
||||
const degreeMap = new Map();
|
||||
edges.forEach(({ source, target }) => {
|
||||
degreeMap.set(source, (degreeMap.get(source) || 0) + 1);
|
||||
degreeMap.set(target, (degreeMap.get(target) || 0) + 1);
|
||||
});
|
||||
nodes.forEach((node) => {
|
||||
node.data.degree = degreeMap.get(node.id) || 0;
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
// Custom data processor - node clustering
|
||||
const clusteringNodes = (data, options = {}, userGraphCore) => {
|
||||
if (!Algorithm?.labelPropagation) return;
|
||||
const clusteredData = Algorithm.louvain(data, false);
|
||||
const clusterMap = new Map();
|
||||
clusteredData.clusters.forEach((cluster, i) => {
|
||||
cluster.nodes.forEach((node) => {
|
||||
clusterMap.set(node.id, `c${i}`);
|
||||
});
|
||||
});
|
||||
data.nodes.forEach((node) => {
|
||||
node.data.cluster = clusterMap.get(node.id);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
const Graph = extend(BaseGraph, {
|
||||
transforms: {
|
||||
'degree-calculator': degreeCalculator,
|
||||
'node-clustering': clusteringNodes,
|
||||
},
|
||||
behaviors: {
|
||||
'brush-select': Extensions.BrushSelect,
|
||||
},
|
||||
nodes: {
|
||||
'triangle-node': Extensions.TriangleNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
transforms: [
|
||||
'transform-v4-data',
|
||||
'degree-calculator',
|
||||
'node-clustering',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
field: 'degree',
|
||||
range: [16, 60],
|
||||
},
|
||||
],
|
||||
modes: {
|
||||
default: ['drag-node', 'drag-canvas', 'zoom-canvas', 'click-select', 'brush-select'],
|
||||
},
|
||||
layout: {
|
||||
type: 'force',
|
||||
animated: true,
|
||||
linkDistance: 50,
|
||||
},
|
||||
theme: {
|
||||
type: 'spec',
|
||||
base: 'light',
|
||||
specification: {
|
||||
node: {
|
||||
dataTypeField: 'cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Default properties for all the edges
|
||||
defaultEdge: {
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
},
|
||||
node: (model) => {
|
||||
const { id, data } = model;
|
||||
let type = 'circle-node';
|
||||
if (data.degree === 2) type = 'rect-node';
|
||||
else if (data.degree === 1) type = 'triangle-node';
|
||||
|
||||
const badgeShapes = {
|
||||
fontSize: 12,
|
||||
lod: 0,
|
||||
};
|
||||
|
||||
if (data.degree > 10) {
|
||||
badgeShapes[0] = {
|
||||
color: '#F86254',
|
||||
text: 'Important',
|
||||
position: 'rightTop',
|
||||
};
|
||||
}
|
||||
if (data.degree > 5) {
|
||||
badgeShapes[1] = {
|
||||
text: 'A',
|
||||
textAlign: 'center',
|
||||
color: '#EDB74B',
|
||||
position: 'right',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
type,
|
||||
labelShape: {
|
||||
position: 'bottom',
|
||||
text: id,
|
||||
},
|
||||
labelBackgroundShape: {},
|
||||
iconShape:
|
||||
data.degree <= 2
|
||||
? undefined
|
||||
: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
fill: '#fff',
|
||||
lod: 0,
|
||||
fontSize: data.keyShape.r - 4,
|
||||
},
|
||||
badgeShapes,
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
// The node styles in different states
|
||||
nodeStateStyles: {
|
||||
// The node style when the state 'hover' is true
|
||||
hover: {
|
||||
fill: 'lightsteelblue',
|
||||
edge: {
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
// The node style when the state 'click' is true
|
||||
click: {
|
||||
stroke: '#000',
|
||||
lineWidth: 3,
|
||||
},
|
||||
},
|
||||
// The edge styles in different states
|
||||
edgeStateStyles: {
|
||||
// The edge style when the state 'click' is true
|
||||
click: {
|
||||
stroke: 'steelblue',
|
||||
},
|
||||
},
|
||||
// Layout
|
||||
layout: {
|
||||
type: 'force',
|
||||
linkDistance: 100,
|
||||
preventOverlap: true,
|
||||
nodeStrength: -30,
|
||||
edgeStrength: 0.1,
|
||||
},
|
||||
// Built-in Behaviors
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
|
||||
const nodes = remoteData.nodes;
|
||||
const edges = remoteData.edges;
|
||||
nodes.forEach((node) => {
|
||||
if (!node.style) {
|
||||
node.style = {};
|
||||
}
|
||||
node.style.lineWidth = 1;
|
||||
node.style.stroke = '#666';
|
||||
node.style.fill = 'steelblue';
|
||||
switch (node.class) {
|
||||
case 'c0': {
|
||||
node.type = 'circle';
|
||||
node.size = 30;
|
||||
break;
|
||||
}
|
||||
case 'c1': {
|
||||
node.type = 'rect';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
case 'c2': {
|
||||
node.type = 'ellipse';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight;
|
||||
edge.style.opacity = 0.6;
|
||||
edge.style.stroke = 'grey';
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
|
||||
// Mouse enter a node
|
||||
graph.on('node:mouseenter', (e) => {
|
||||
const nodeItem = e.item; // Get the target item
|
||||
graph.setItemState(nodeItem, 'hover', true); // Set the state 'hover' of the item to be true
|
||||
});
|
||||
|
||||
// Mouse leave a node
|
||||
graph.on('node:mouseleave', (e) => {
|
||||
const nodeItem = e.item; // Get the target item
|
||||
graph.setItemState(nodeItem, 'hover', false); // Set the state 'hover' of the item to be false
|
||||
});
|
||||
|
||||
// Click a node
|
||||
graph.on('node:click', (e) => {
|
||||
// Swich the 'click' state of the node to be false
|
||||
const clickNodes = graph.findAllByState('node', 'click');
|
||||
clickNodes.forEach((cn) => {
|
||||
graph.setItemState(cn, 'click', false);
|
||||
});
|
||||
const nodeItem = e.item; // et the clicked item
|
||||
graph.setItemState(nodeItem, 'click', true); // Set the state 'click' of the item to be true
|
||||
});
|
||||
|
||||
// Click an edge
|
||||
graph.on('edge:click', (e) => {
|
||||
// Swich the 'click' state of the edge to be false
|
||||
const clickEdges = graph.findAllByState('edge', 'click');
|
||||
clickEdges.forEach((ce) => {
|
||||
graph.setItemState(ce, 'click', false);
|
||||
});
|
||||
const edgeItem = e.item; // Get the clicked item
|
||||
graph.setItemState(edgeItem, 'click', true); // Set the state 'click' of the item to be true
|
||||
});
|
||||
graph.read(remoteData);
|
||||
};
|
||||
main();
|
||||
</script>
|
||||
@ -299,4 +333,4 @@ graph.on('edge:click', (e) => {
|
||||
</html>
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️Attention:</strong></span> <br />Replace the url `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` to change the data into yours.
|
||||
**⚠️ Note:** <br /> If you need to replace the data, please replace `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` with the new data file address.
|
||||
|
@ -3,9 +3,9 @@ title: 图的交互 Behavior
|
||||
order: 4
|
||||
---
|
||||
|
||||
G6 封装了一系列交互方法,方便用户直接使用。本文将为 **Tutorial 案例** 增加简单的交互:hover 节点、点击节点、点击边、放缩画布、拖拽画布。本节目标效果如下:
|
||||
G6 封装了一系列交互方法,方便用户直接使用。本文将为 **Tutorial 案例** 增加简单的交互:点击节点、点击边、框选节点、放缩画布、拖拽画布。本节目标效果如下:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*dijtQ6nB5Y4AAAAAAAAAAABkARQnAQ' width=500 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*kgrxQJnxNPoAAAAAAAAAAAAADmJ7AQ/original' width=500 alt='img' />
|
||||
|
||||
> 图 1 Tutorial 案例的交互效果。
|
||||
|
||||
@ -18,32 +18,59 @@ G6 中的交互行为。G6 **内置**了一系列交互行为,用户可以直
|
||||
- `drag-canvas`:拖拽画布;
|
||||
- `zoom-canvas`:缩放画布。
|
||||
|
||||
更多详见:[交互行为 Behavior](/zh/docs/manual/middle/states/defaultBehavior)
|
||||
更多详见:[交互行为 Behavior](https://g6-next.antv.antgroup.com/apis/interfaces/behaviors/activate-relations-options)
|
||||
|
||||
### 交互管理 Mode
|
||||
|
||||
Mode 是 G6 交互行为的管理机制,一个 mode 是多种行为 Behavior 的组合,允许用户通过切换不同的模式进行交互行为的管理。由于该概念较为复杂,在本入门教程中,读者不需要对该机制深入理解。如有需求,参见 [交互模式 Mode](/zh/docs/manual/middle/states/mode)。
|
||||
Mode 是 G6 交互行为的管理机制,一个 mode 是多种行为 Behavior 的组合,允许用户通过切换不同的模式进行交互行为的管理。由于该概念较为复杂,在本入门教程中,读者不需要对该机制深入理解。如有需求,参见 [交互模式 Mode](https://g6-next.antv.antgroup.com/apis/interfaces/graph/i-graph)。
|
||||
|
||||
### 交互状态 State
|
||||
|
||||
[状态 State](/zh/docs/manual/middle/states/state) 是 G6 中的状态机制。用户可以为图中的元素(节点/边)设置不同的状态及不同状态下的样式。在状态发生变化时,G6 自动更新元素的样式。例如,可以为节点设置状态 `'click'` 为 `true` 或 `false`,并为节点设置 `'click'` 的样式为加粗节点边框。当 `'click'` 状态被切换为 `true` 时,节点的边框将会被加粗,`'click'` 状态被切换为 `false` 时,节点的样式恢复到默认。在下面的使用方法中,将会有具体例子。
|
||||
状态是 G6 中的状态机制。用户可以为图中的元素(节点/边)设置不同的状态及不同状态下的样式。在状态发生变化时,G6 自动更新元素的样式。例如,可以为节点设置状态 `'click'` 为 `true` 或 `false`,并为节点设置 `'click'` 的样式为加粗节点边框。当 `'click'` 状态被切换为 `true` 时,节点的边框将会被加粗,`'click'` 状态被切换为 `false` 时,节点的样式恢复到默认。在下面的使用方法中,将会有具体例子。
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 拖拽、缩放——内置的交互行为
|
||||
### 拖拽、缩放 —— 内置的交互行为
|
||||
|
||||
在 G6 中使用内置 Behavior 的方式非常简单,只需要在图实例化时配置 `modes`。拖拽和缩放属于 G6 内置交互行为,修改代码如下:
|
||||
在 G6 中使用内置 Behavior 的方式非常简单,只需要在图实例化时配置 `modes`。为了包体积管理,部分内置交互没有提前注册到 Graph 上,需要如下注册方式:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ... // 其他配置项
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node'], // 允许拖拽画布、放缩画布、拖拽节点
|
||||
const { Graph: GraphBase, extend, Extensions } = G6;
|
||||
|
||||
const Graph = extend(GraphBase, {
|
||||
behaviors: {
|
||||
// 框选节点事内置交互,未提前注册,需要从 Extensions 中引入后如下注册:
|
||||
'brush-select': Extensions.BrushSelect,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
除了直接使用内置交互名称外,也可以为 Behavior 配置参数,例如放缩画布的敏感度、最大/最小放缩程度等,具体用法参见 [交互行为 Behavior](/zh/docs/manual/middle/states/defaultBehavior)。
|
||||
所有的内置交互可以在 [交互行为 Behavior](https://g6-next.antv.antgroup.com/apis/interfaces/behaviors/activate-relations-options) 查看,除了下面这戏鹅已经提前注册的交互,其他需要使用上面方式进行注册。
|
||||
|
||||
```javascript
|
||||
// 已提前注册的交互
|
||||
{
|
||||
'drag-canvas': DragCanvas, // 拖拽画布
|
||||
'zoom-canvas': ZoomCanvas, // 缩放画布
|
||||
'drag-node': DragNode, // 拖拽节点
|
||||
'drag-combo': DragCombo, // 拖拽 Combo
|
||||
'collapse-expand-combo': CollapseExpandCombo, // 展开/收起 Combo
|
||||
'collapse-expand-tree': CollapseExpandTree, // 展开/收起子树
|
||||
'click-select': ClickSelect, // 点击选择
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 注意此处使用的是 exnted 返回的 Graph
|
||||
const graph = new G6.Graph({
|
||||
// ...其他配置项
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'click-select', 'brush-select'], // 允许拖拽画布、放缩画布、拖拽节点、点选节点、框选节点
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
除了直接使用内置交互名称外,也可以为 Behavior 配置参数,例如放缩画布的敏感度、最大/最小放缩程度等,具体用法参见 [交互行为 Behavior](https://g6-next.antv.antgroup.com/apis/interfaces/behaviors/zoom-canvas-options)。
|
||||
|
||||
上面代码中的 `modes` 定义了 G6 的模式,`default` 是默认的模式,还可以允许有其他的模式,比如:编辑模式 `edit` 等。不同的模式,用户能进行的行为可以不同,比如默认模式能拖拽画布,编辑模式不允许拖拽画布:
|
||||
|
||||
@ -55,53 +82,63 @@ modes: {
|
||||
}
|
||||
```
|
||||
|
||||
更多关于模式、行为可以参考: [交互模型 Mode](/zh/docs/manual/middle/states/mode) 和 [交互行为 Behavior](/zh/docs/manual/middle/states/defaultBehavior) 文档。
|
||||
### 定义交互状态样式
|
||||
|
||||
### Hover、Click 改变样式——状态式交互
|
||||
|
||||
有时我们希望通过交互可以将元素样式变成特定样式,如我们看到的图 1 中,鼠标 hover 节点、点击节点、点击边时,样式发生了变化。这里涉及到了 G6 中 [状态 State](/zh/docs/manual/middle/states/state) 的概念。简单地说,是否 `hover` 、`click` 、任何操作(可以是自己起的状态名),都可以称为一种状态(state)。用户可以自由设置不同状态下的元素样式。要达到交互更改元素样式,需要两步:
|
||||
有时我们希望通过交互可以将元素样式变成特定样式,如我们看到的图 1 中,鼠标 hover 节点、点击节点、点击边时,样式发生了变化。这里涉及到了 G6 中状态的概念。简单地说,是否 `hover` 、`click` 、任何操作(可以是自己起的状态名),都可以称为一种状态(state)。用户可以自由设置不同状态下的元素样式。要达到交互更改元素样式,需要两步:
|
||||
|
||||
- Step 1: 设置各状态下的元素样式;
|
||||
- Step 2: 监听事件并切换元素状态。
|
||||
|
||||
#### 设置各状态下的元素样式
|
||||
|
||||
在实例化图时,通过 `nodeStateStyles` 和 `edgeStateStyles` 两个配置项可以配置元素在不同状态下的样式。<br />为达到 **Tutorial 案例** 中的效果:
|
||||
|
||||
- 鼠标 hover 节点时,该节点颜色变浅;
|
||||
- 点击节点时,该节点边框加粗变黑;
|
||||
- 点击边时,该边变成蓝色。
|
||||
|
||||
下面代码设置了节点分别在 `hover` 和 `click` 状态为 `true` 时的样式,边在 `click` 状态为 `true` 时的样式:
|
||||
在实例化图时,通过 `nodeState` 和 `edgeState` 两个配置项可以配置元素在不同状态下的样式。G6 预置了一些状态样式:'selected', 'highlight', 'active', 'inactive', 'disable'。在 'click-select' 和 'brush-select' 交互中默认触发的是节点和边的 'selected' 状态,因此在没有配置 `nodeState` 和 `edgeState` 的情况下,我们也可以看到有选中的状态样式响应。如果需要自定义状态样式,可以为 'click-select' 和 'brush-select' 配置 `selectedState` 为自定义的字符串,然后在图配置的 `nodeState` 和 `edgeState` 中配置对应的样式,例如:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ... // 其他配置项
|
||||
// 节点不同状态下的样式集合
|
||||
nodeStateStyles: {
|
||||
// 鼠标 hover 上节点,即 hover 状态为 true 时的样式
|
||||
hover: {
|
||||
fill: 'lightsteelblue',
|
||||
const graph = new Graph({
|
||||
// ...其他配置项
|
||||
// 节点状态样式
|
||||
nodeState: {
|
||||
// 自定义的状态名称
|
||||
customstatename1: {
|
||||
// 针对不同的图形进行配置
|
||||
keyShape: {
|
||||
lineWidth: 2,
|
||||
stroke: 'red',
|
||||
},
|
||||
labelShape: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
},
|
||||
// 鼠标点击节点,即 click 状态为 true 时的样式
|
||||
click: {
|
||||
stroke: '#000',
|
||||
lineWidth: 3,
|
||||
},
|
||||
},
|
||||
// 边不同状态下的样式集合
|
||||
edgeStateStyles: {
|
||||
// 鼠标点击边,即 click 状态为 true 时的样式
|
||||
click: {
|
||||
stroke: 'steelblue',
|
||||
// 自定义的状态名称
|
||||
customstatename2: {
|
||||
keyShape: {
|
||||
lineDash: [2, 2],
|
||||
lineWidth: 4,
|
||||
stroke: 'blue',
|
||||
},
|
||||
},
|
||||
},
|
||||
// edgeState 同理
|
||||
});
|
||||
```
|
||||
|
||||
#### 监听事件并切换元素状态
|
||||
|
||||
G6 中所有元素监听都挂载在图实例上,如下代码中的 `graph` 对象是 G6.Graph 的实例,`graph.on()` 函数监听了某元素类型(`node` / `edge`)的某种事件(`click` / `mouseenter` / `mouseleave` / ... 所有事件参见:[Event API](/zh/docs/api/Event))。
|
||||
除了内置的交互,您也可以在任意需要的使用调用 `graph.setItemState` 来设置元素的状态,例如在双击节点时:
|
||||
|
||||
```javascript
|
||||
graph.on('node:dblclick', (e) => {
|
||||
graph.setItemState(e.itemId, 'customstatename1', true);
|
||||
});
|
||||
graph.on('canvas:click', (e) => {
|
||||
// 找到所有 customstatename1 状态下的节点 id
|
||||
const stateNodeIds = graph.findIdByState('node', 'customstatename1', true);
|
||||
// 批量取消状态
|
||||
graph.setItemState(stateNodeIds, 'customstatename1', false);
|
||||
});
|
||||
```
|
||||
|
||||
G6 中所有元素监听都挂载在图实例上,如下代码中的 `graph` 对象是 G6.Graph 的实例,`graph.on()` 函数监听了某元素类型(`node` / `edge`)的某种事件(`click` / `mouseenter` / `mouseleave` / ...。
|
||||
|
||||
```javascript
|
||||
// 在图实例 graph 上监听
|
||||
@ -110,44 +147,6 @@ graph.on('元素类型:事件名', (e) => {
|
||||
});
|
||||
```
|
||||
|
||||
现在,我们通过下面代码,为 **Tutorial 案例** 增加点和边上的监听事件,并在监听函数里使用 `graph.setItemState()` 改变元素的状态:
|
||||
|
||||
```javascript
|
||||
// 鼠标进入节点
|
||||
graph.on('node:mouseenter', (e) => {
|
||||
const nodeItem = e.item; // 获取鼠标进入的节点元素对象
|
||||
graph.setItemState(nodeItem, 'hover', true); // 设置当前节点的 hover 状态为 true
|
||||
});
|
||||
|
||||
// 鼠标离开节点
|
||||
graph.on('node:mouseleave', (e) => {
|
||||
const nodeItem = e.item; // 获取鼠标离开的节点元素对象
|
||||
graph.setItemState(nodeItem, 'hover', false); // 设置当前节点的 hover 状态为 false
|
||||
});
|
||||
|
||||
// 点击节点
|
||||
graph.on('node:click', (e) => {
|
||||
// 先将所有当前是 click 状态的节点置为非 click 状态
|
||||
const clickNodes = graph.findAllByState('node', 'click');
|
||||
clickNodes.forEach((cn) => {
|
||||
graph.setItemState(cn, 'click', false);
|
||||
});
|
||||
const nodeItem = e.item; // 获取被点击的节点元素对象
|
||||
graph.setItemState(nodeItem, 'click', true); // 设置当前节点的 click 状态为 true
|
||||
});
|
||||
|
||||
// 点击边
|
||||
graph.on('edge:click', (e) => {
|
||||
// 先将所有当前是 click 状态的边置为非 click 状态
|
||||
const clickEdges = graph.findAllByState('edge', 'click');
|
||||
clickEdges.forEach((ce) => {
|
||||
graph.setItemState(ce, 'click', false);
|
||||
});
|
||||
const edgeItem = e.item; // 获取被点击的边元素对象
|
||||
graph.setItemState(edgeItem, 'click', true); // 设置当前边的 click 状态为 true
|
||||
});
|
||||
```
|
||||
|
||||
## 完整代码
|
||||
|
||||
至此,完整代码如下:
|
||||
@ -160,141 +159,173 @@ graph.on('edge:click', (e) => {
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<div id="container"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
<script>
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 800,
|
||||
height: 600,
|
||||
// 节点默认配置
|
||||
defaultNode: {
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#fff',
|
||||
const { Graph: GraphBase, extend, Extensions } = G6;
|
||||
|
||||
// 自定义数据处理器 - 度数计算
|
||||
const degreeCalculator = (data, options, userGraphCore) => {
|
||||
const { edges, nodes } = data;
|
||||
const degreeMap = new Map();
|
||||
edges.forEach(({ source, target }) => {
|
||||
degreeMap.set(source, (degreeMap.get(source) || 0) + 1);
|
||||
degreeMap.set(target, (degreeMap.get(target) || 0) + 1);
|
||||
});
|
||||
nodes.forEach((node) => {
|
||||
node.data.degree = degreeMap.get(node.id) || 0;
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
// 自定义数据处理器 - 节点聚类
|
||||
const clusteringNodes = (data, options = {}, userGraphCore) => {
|
||||
if (!Algorithm?.labelPropagation) return;
|
||||
const clusteredData = Algorithm.louvain(data, false);
|
||||
const clusterMap = new Map();
|
||||
clusteredData.clusters.forEach((cluster, i) => {
|
||||
cluster.nodes.forEach((node) => {
|
||||
clusterMap.set(node.id, `c${i}`);
|
||||
});
|
||||
});
|
||||
data.nodes.forEach((node) => {
|
||||
node.data.cluster = clusterMap.get(node.id);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
const Graph = extend(BaseGraph, {
|
||||
transforms: {
|
||||
'degree-calculator': degreeCalculator,
|
||||
'node-clustering': clusteringNodes,
|
||||
},
|
||||
behaviors: {
|
||||
'brush-select': Extensions.BrushSelect,
|
||||
},
|
||||
nodes: {
|
||||
'triangle-node': Extensions.TriangleNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
transforms: [
|
||||
'transform-v4-data',
|
||||
'degree-calculator',
|
||||
'node-clustering',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
field: 'degree',
|
||||
range: [16, 60],
|
||||
},
|
||||
],
|
||||
modes: {
|
||||
default: ['drag-node', 'drag-canvas', 'zoom-canvas', 'click-select', 'brush-select'],
|
||||
},
|
||||
layout: {
|
||||
type: 'force',
|
||||
animated: true,
|
||||
linkDistance: 50,
|
||||
},
|
||||
theme: {
|
||||
type: 'spec',
|
||||
base: 'light',
|
||||
specification: {
|
||||
node: {
|
||||
dataTypeField: 'cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
// 边默认配置
|
||||
defaultEdge: {
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
},
|
||||
node: (model) => {
|
||||
const { id, data } = model;
|
||||
let type = 'circle-node';
|
||||
if (data.degree === 2) type = 'rect-node';
|
||||
else if (data.degree === 1) type = 'triangle-node';
|
||||
|
||||
const badgeShapes = {
|
||||
fontSize: 12,
|
||||
lod: 0,
|
||||
};
|
||||
|
||||
if (data.degree > 10) {
|
||||
badgeShapes[0] = {
|
||||
color: '#F86254',
|
||||
text: 'Important',
|
||||
position: 'rightTop',
|
||||
};
|
||||
}
|
||||
if (data.degree > 5) {
|
||||
badgeShapes[1] = {
|
||||
text: 'A',
|
||||
textAlign: 'center',
|
||||
color: '#EDB74B',
|
||||
position: 'right',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
type,
|
||||
labelShape: {
|
||||
position: 'bottom',
|
||||
text: id,
|
||||
},
|
||||
labelBackgroundShape: {},
|
||||
iconShape:
|
||||
data.degree <= 2
|
||||
? undefined
|
||||
: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
fill: '#fff',
|
||||
lod: 0,
|
||||
fontSize: data.keyShape.r - 4,
|
||||
},
|
||||
badgeShapes,
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
// 节点在各状态下的样式
|
||||
nodeStateStyles: {
|
||||
// hover 状态为 true 时的样式
|
||||
hover: {
|
||||
fill: 'lightsteelblue',
|
||||
edge: {
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
// click 状态为 true 时的样式
|
||||
click: {
|
||||
stroke: '#000',
|
||||
lineWidth: 3,
|
||||
},
|
||||
},
|
||||
// 边在各状态下的样式
|
||||
edgeStateStyles: {
|
||||
// click 状态为 true 时的样式
|
||||
click: {
|
||||
stroke: 'steelblue',
|
||||
},
|
||||
},
|
||||
// 布局
|
||||
layout: {
|
||||
type: 'force',
|
||||
linkDistance: 100,
|
||||
preventOverlap: true,
|
||||
nodeStrength: -30,
|
||||
edgeStrength: 0.1,
|
||||
},
|
||||
// 内置交互
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||
},
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
|
||||
const nodes = remoteData.nodes;
|
||||
const edges = remoteData.edges;
|
||||
nodes.forEach((node) => {
|
||||
if (!node.style) {
|
||||
node.style = {};
|
||||
}
|
||||
node.style.lineWidth = 1;
|
||||
node.style.stroke = '#666';
|
||||
node.style.fill = 'steelblue';
|
||||
switch (node.class) {
|
||||
case 'c0': {
|
||||
node.type = 'circle';
|
||||
node.size = 30;
|
||||
break;
|
||||
}
|
||||
case 'c1': {
|
||||
node.type = 'rect';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
case 'c2': {
|
||||
node.type = 'ellipse';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight;
|
||||
edge.style.opacity = 0.6;
|
||||
edge.style.stroke = 'grey';
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
|
||||
// 监听鼠标进入节点
|
||||
graph.on('node:mouseenter', (e) => {
|
||||
const nodeItem = e.item;
|
||||
// 设置目标节点的 hover 状态 为 true
|
||||
graph.setItemState(nodeItem, 'hover', true);
|
||||
});
|
||||
// 监听鼠标离开节点
|
||||
graph.on('node:mouseleave', (e) => {
|
||||
const nodeItem = e.item;
|
||||
// 设置目标节点的 hover 状态 false
|
||||
graph.setItemState(nodeItem, 'hover', false);
|
||||
});
|
||||
// 监听鼠标点击节点
|
||||
graph.on('node:click', (e) => {
|
||||
// 先将所有当前有 click 状态的节点的 click 状态置为 false
|
||||
const clickNodes = graph.findAllByState('node', 'click');
|
||||
clickNodes.forEach((cn) => {
|
||||
graph.setItemState(cn, 'click', false);
|
||||
});
|
||||
const nodeItem = e.item;
|
||||
// 设置目标节点的 click 状态 为 true
|
||||
graph.setItemState(nodeItem, 'click', true);
|
||||
});
|
||||
// 监听鼠标点击节点
|
||||
graph.on('edge:click', (e) => {
|
||||
// 先将所有当前有 click 状态的边的 click 状态置为 false
|
||||
const clickEdges = graph.findAllByState('edge', 'click');
|
||||
clickEdges.forEach((ce) => {
|
||||
graph.setItemState(ce, 'click', false);
|
||||
});
|
||||
const edgeItem = e.item;
|
||||
// 设置目标边的 click 状态 为 true
|
||||
graph.setItemState(edgeItem, 'click', true);
|
||||
});
|
||||
graph.read(remoteData);
|
||||
};
|
||||
main();
|
||||
</script>
|
||||
@ -302,4 +333,4 @@ graph.on('edge:click', (e) => {
|
||||
</html>
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️ 注意:</strong></span><br /> 若需更换数据,请替换 `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` 为新的数据文件地址。
|
||||
**⚠️ 注意:** <br /> 若需更换数据,请替换 `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` 为新的数据文件地址。
|
||||
|
@ -3,224 +3,357 @@ title: Configure the Items
|
||||
order: 2
|
||||
---
|
||||
|
||||
There are `Node` and `Edge` two types of items in a graph. In the last chapter, we rendered the **Tutorial Demo** with items with rough styles. Now, we are going to beautify the items while introducing the properties of the items.
|
||||
In this chapter, we have already drawn the graph of the **Tutorial example**, but the items and their labels look visually basic. In this article, we will beautify the items from the previous chapter to achieve the following effects and introduce the properties and configuration methods of the items.
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*46GdQaNFiVIAAAAAAAAAAABkARQnAQ' width=450 height=450 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*K3ADTJct0o4AAAAAAAAAAAAADmJ7AQ/original' width=450 height=450 alt='img'/>
|
||||
|
||||
> Figure 1 The **Tutorial Demo** with cofigured items.
|
||||
> Figure 1: The **\*Tutorial example** \*after configuring element properties.
|
||||
|
||||
## Basic Concept
|
||||
## Basic Concepts
|
||||
|
||||
### Graph Item
|
||||
### Graph Items
|
||||
|
||||
There are `Node` and `Edge` two types of items in a graph. Several [Built-in Nodes](/en/docs/manual/middle/elements/nodes/defaultNode) and [Built-in Edges](/en/docs/manual/middle/elements/edges/defaultEdge) are provided by G6. The main difference between different types of items is their [Graphics Shape](/en/docs/manual/middle/elements/shape/shape-keyshape). For example, a node's graphics type can be a circle, a rect, an image, or others.
|
||||
Graph items refer to the **nodes** `Node`, **edges** `Edge`, and _node groups_ `Combo` on the graph. G6 provides a series of [built-in nodes](https://g6-next.antv.antgroup.com/en/examples#item-defaultNodes) for users to choose from.
|
||||
|
||||
## Properties of Item
|
||||
## Element Properties
|
||||
|
||||
The properties of an item can be be divided into two categories:
|
||||
Whether it is a node or an edge, their properties can be divided into two types:
|
||||
|
||||
- **Style Property `style`**: Corresponds to the style in Canvas. When the [State](/en/docs/manual/middle/states/state) of an item is changed, the style can be updated. It is an object named `style`;
|
||||
- **Other Property**: Such as graphics `type`, `id`, they are a kind of properties that will not be changed when the [State](/en/docs/manual/middle/states/state) of the item is changed.
|
||||
- **Graphic style properties**: Correspond to various styles in the canvas and can be changed when the element's state changes.
|
||||
- **Other properties**: For example, the type (`type`), id (`id`), position (`x`, `y`) properties, which cannot be changed when the element's state changes.
|
||||
|
||||
For example, When you change the state `'hover'` or `'click'` to `true` for a node A, only the **style properties** of A can be updated, e.g. `fill`, `stroke`, and so on. The **other properties** such as `type` can not be changed. To update the other properties, configure A by [graph.updateItem](/en/docs/api/graphFunc/item#graphupdateitemitem-model-stack) manually.
|
||||
For example, when G6 sets a node to be hovered or clicked, it generally uses the Graph's set state API `graph.setItemState` in the event listener to put the node into a certain state, e.g. the selected state. At this time, the node should make certain style changes to respond to the selected state. This can only automatically change the **graphic style properties** of the node (such as `fill` and `stroke` in `keyShape`), and the other properties (such as `type`) cannot be changed. If you need to change other properties, you need to update the data with `graph.updateData`. The **graphic style properties** are stored in the `xxxShape` object of the node/edge/combo's configuration, corresponding to the styles of different shapes.
|
||||
|
||||
### Data Structure
|
||||
|
||||
The data structure of a node:
|
||||
Taking the node element as an example, the data structure of its properties is as follows:
|
||||
|
||||
```javascript
|
||||
{
|
||||
id: 'node0', // Unique id of the node
|
||||
type: 'circle', // The type of graphics shape of the node
|
||||
size: 40, // The size
|
||||
label: 'node0' // The label
|
||||
visible: true, // Controls the visible of the node when first render. false means hide the item. All the items are visible by default
|
||||
labelCfg: { // The configurations for the label
|
||||
positions: 'center',// The relative position of the label
|
||||
style: { // The style properties of the label
|
||||
fontSize: 12, // The font size of the label
|
||||
// ... // Other style properties of the label
|
||||
id: 'node0', // The id of the element
|
||||
data: {
|
||||
x: 100, // The position of the node. If the graph does not configure a layout and all node data in the data has x and y information, this information will be used to draw the node's position.
|
||||
y: 100,
|
||||
type: 'circle-node', // The shape of the element. Compared to v4, it has an extra -node suffix.
|
||||
label: 'node0' // The label text
|
||||
keyShape: { // The style properties of the main shape
|
||||
r: 20 // The size of the main shape. If it is a rect-node, it is controlled by width and height.
|
||||
fill: '#000', // The fill color of the main shape
|
||||
stroke: '#888', // The stroke color of the main shape
|
||||
// ... // Other style properties of the main shape
|
||||
},
|
||||
labelShape: {
|
||||
positions: 'center', // The properties of the label, the position of the label in the element
|
||||
text: 'node-xxx-label'// The text of the element's label. If not configured, it will be filled with data.label
|
||||
fontSize: 12 // The style properties of the label, the font size of the text
|
||||
// ... // Other style properties of the label
|
||||
}
|
||||
}
|
||||
// ..., // Other properties of the node
|
||||
style: { // The object of style properties of the node
|
||||
fill: '#000', // The filling color
|
||||
stroke: '#888', // The stroke color
|
||||
// ... // Other styleattribtues of the node
|
||||
}
|
||||
// ..., // Other properties
|
||||
},
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The data structure of an edge is similar to node, but two more properties `source` and `target` in addition, representing the `id` of the source node and the `id` of the target node respectively.
|
||||
The data structure of the edge item is similar to that of the node item, except that it has `source` and `target` fields at the same level as `id` and `data`, which represent the ids of the start and end nodes.
|
||||
|
||||
<br />We can refine the visual requirements in figure 1 of **Tutorial Demo** into:
|
||||
Refining the visual requirements of the **Tutorial example** in Figure 1, we need to achieve the following:
|
||||
|
||||
- Visual Effect:
|
||||
- R1: Set the color for stroke and filling for nodes with `fill` and `stroke`;
|
||||
- R2: Set the color for the label with `labelCfg`;
|
||||
- R3: Set the opacity and color for edges with `opacity`,`stroke`;
|
||||
- R4: Set the direction of the label with `labelCfg`;
|
||||
- Map the data to visual channels:
|
||||
- R5: Configure the type of nodes with `type` according to the property `class` in node data;
|
||||
- R6: Configure the line widht of edges with `lineWidth` according to the property `weight` in edge data.
|
||||
- Visual effects:
|
||||
- R1: Set different node types, `'circle-node'`, `'rect-node'`, `'triangle-node'`.
|
||||
- R2: Draw the icons and badges of the nodes, corresponding properties: `iconShape`, `badgeShapes`.
|
||||
- R3: Arrows on the edges, corresponding to the edge property: `keyShape.endArrow`.
|
||||
- Data and visual mapping:
|
||||
- R5: Cluster the nodes and map the colors of the nodes based on categories, corresponding property: `keyShape.fill`.
|
||||
- R6: Map the size of the nodes based on their degree, corresponding property: `keyShape.r`.
|
||||
|
||||
## Configure the Properties
|
||||
## Properties Configuration
|
||||
|
||||
To satisfy different scenario, G6 provides 7 ways to configure the properties for items. Here we will only introduce two of them:
|
||||
In G6, there are multiple ways to configure element properties based on different scenario requirements. Here, we introduce the configuration of element properties when instantiating a graph. Compared to v4, where only static global properties can be configured on the graph, v5 supports JSON Spec attribute mapping and function mapping configuration methods:
|
||||
|
||||
1. Configure the global properties when instantiating a Graph;
|
||||
2. Configure the properties for different items in their data.
|
||||
### 1.JSON Spec Configuration when Instantiating the Graph
|
||||
|
||||
### 1. Configure the Global Properties When Instantiating a Graph
|
||||
**Scenario**: Uniform configuration of properties for all nodes and edges.
|
||||
|
||||
**Applicable Scene:** Unify the configurations for all the nodes or edges. <br />**Usage:** Configure it with two configurations of graph:
|
||||
**Usage**: Use two configuration options of the graph:
|
||||
|
||||
- `defaultNode`: The **Style Property** and **Other Properties** in the default state;
|
||||
- `defaultEdge`: The **Style Property** and **Other Properties** in the default state.
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️Attention:</strong></span> It is a way of unified global configuration, which does not distinguish the nodes with different properties (e.g. `class` and `weight`) in their data. That is to say, only R1, R2, R3, and R4 can be satisfied now:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*bufdS75wcmMAAAAAAAAAAABkARQnAQ' width=450 height=450 alt='img' />
|
||||
|
||||
> Figure 2 **Tutorial Demo** with items configured by global configurations.
|
||||
|
||||
<br />Configure the `defaultNode` and `defaultEdge` for graph to achieve the expected effect:
|
||||
- `node`: The **graphic style properties** and **other properties** of the nodes in the default state.
|
||||
- `edge`: The **graphic style properties** and **other properties** of the edges in the default state.
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ... // Other configurations of the graph
|
||||
// The style properties and other properties for all the nodes in the default state
|
||||
defaultNode: {
|
||||
size: 30, // The size of nodes
|
||||
// ... // The other properties
|
||||
// The style properties of nodes
|
||||
style: {
|
||||
fill: 'steelblue', // The filling color of nodes
|
||||
stroke: '#666', // The stroke color of nodes
|
||||
lineWidth: 1, // The line width of the stroke of nodes
|
||||
// Configuration for the graphic style and other properties of nodes in the default state
|
||||
node: {
|
||||
type: 'circle-node',
|
||||
keyShape: {
|
||||
r: 16, // Node size
|
||||
fill: '#4089FF', // Node fill color
|
||||
},
|
||||
// The properties for label of nodes
|
||||
labelCfg: {
|
||||
// The style properties for the label
|
||||
style: {
|
||||
fill: '#fff', // The color of the text
|
||||
// Configuration for the label text on the nodes
|
||||
labelShape: {
|
||||
// All styles support the following mapping, which means that based on the label field in the data model.data, use the result returned by formatter
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: (model) => model.data.label,
|
||||
},
|
||||
fill: '#000', // Node label text color
|
||||
},
|
||||
// Animation configuration for nodes
|
||||
animates: {
|
||||
// When data/state updates
|
||||
update: [
|
||||
{
|
||||
shapeId: 'haloShape', // Background halo shape
|
||||
states: ['selected', 'active'], // When in the selected and active states change
|
||||
fields: ['opacity'], // Animated change in opacity
|
||||
},
|
||||
{
|
||||
shapeId: 'keyShape', // Main shape
|
||||
states: ['selected', 'active'], // When in the selected and active states change
|
||||
fields: ['lineWidth'], // Animated change in edge thickness
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// The style properties and other properties for all the edges in the default state
|
||||
defaultEdge: {
|
||||
// ... // The other properties
|
||||
// The style properties of edges
|
||||
style: {
|
||||
opacity: 0.6, // The opacity of edges
|
||||
stroke: 'grey', // The color of the edges
|
||||
// Configuration for the style (style) and other properties of edges in the default state
|
||||
edge: {
|
||||
// ... // Other configurations for edges
|
||||
// Edge style configuration
|
||||
type: 'line-edge',
|
||||
keyShape: {
|
||||
opacity: 0.6, // Opacity of the main shape of the edge
|
||||
stroke: 'grey', // Stroke color of the main shape of the edge
|
||||
},
|
||||
// The properties for label of edges
|
||||
labelCfg: {
|
||||
autoRotate: true, // Whether to rotate the label according to the edges
|
||||
// Configuration for the label text on the edges
|
||||
labelShape: {
|
||||
autoRotate: true, // Rotate the label text on the edge based on the direction of the edge
|
||||
},
|
||||
// Animation configuration for edges
|
||||
animates: {
|
||||
// When data/state updates
|
||||
update: [
|
||||
{
|
||||
shapeId: 'haloShape', // Background halo shape
|
||||
states: ['selected', 'active'], // When in the selected and active states change
|
||||
fields: ['opacity'], // Animated change in opacity
|
||||
},
|
||||
{
|
||||
shapeId: 'keyShape', // Main shape
|
||||
states: ['selected', 'active'], // When in the selected and active states change
|
||||
fields: ['lineWidth'], // Animated change in edge thickness
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Configure the Properties in Data
|
||||
### 2. Function Mapping Configuration for Instantiating a Graph
|
||||
|
||||
**Applicable Scene:** By this way, you can configure different items according to their properties in data. <br />Thus, the R5 and R6 can be satisfied now. <br />**Usage:** Write the properties into each item data, or traverse the data to assign the properties. Here we show assigning the attrbiutes into data by traversing:
|
||||
**Scenario**: Different nodes/edges can have different personalized configurations. More flexibility.
|
||||
|
||||
```javascript
|
||||
const nodes = remoteData.nodes;
|
||||
nodes.forEach((node) => {
|
||||
if (!node.style) {
|
||||
node.style = {};
|
||||
}
|
||||
switch (
|
||||
node.class // Configure the graphics type of nodes according to their class
|
||||
) {
|
||||
case 'c0': {
|
||||
node.type = 'circle'; // The graphics type is circle when class = 'c0'
|
||||
break;
|
||||
}
|
||||
case 'c1': {
|
||||
node.type = 'rect'; // The graphics type is rect when class = 'c1'
|
||||
node.size = [35, 20]; // The node size when class = 'c1'
|
||||
break;
|
||||
}
|
||||
case 'c2': {
|
||||
node.type = 'ellipse'; // The graphics type is ellipse when class = 'c2'
|
||||
node.size = [35, 20]; // The node size when class = 'c2'
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
**Usage:** Before looking at the function mapping code, we know that each node in the original data is relatively simple:
|
||||
|
||||
graph.data(remoteData);
|
||||
```
|
||||
{ "id": "0", "data": { "label": "0" } },
|
||||
{ "id": "1", "data": { "label": "1" } },
|
||||
```
|
||||
|
||||
The result:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*JU6xRZLKCjcAAAAAAAAAAABkARQnAQ' width=450 height=450 alt='img' />
|
||||
|
||||
> Figure 3
|
||||
|
||||
From figure 3, we find some nodes are rendered as rects, some are ellipses. We also set the `size` to override the `size` in global configuration. The `size` is an array when the node is a rect or an ellipse. We did not set the `size` for circle node, so `size: 30` in global configuration will still take effect for circle node. That is to say, configuring items by writing into data has higher priority than global configurations.
|
||||
|
||||
We further set the line widths for edges according to their weight:
|
||||
Generally, the larger the degree (number of one-hop neighbors) of a node in a graph, the more important it is. We can use node size to express this information. At the same time, if the degree is large enough, we can use more additional graphics to highlight its status. We can calculate the degree of the nodes in advance through a data processor:
|
||||
|
||||
```javascript
|
||||
// const nodes = ...
|
||||
|
||||
// Traverse the egdes data
|
||||
const edges = remoteData.edges;
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight; // Mapping the weight in data to lineWidth
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
const degreeCalculator = (data, options, userGraphCore) => {
|
||||
const { edges, nodes } = data;
|
||||
const degreeMap = new Map();
|
||||
edges.forEach(({ source, target }) => {
|
||||
degreeMap.set(source, (degreeMap.get(source) || 0) + 1);
|
||||
degreeMap.set(target, (degreeMap.get(target) || 0) + 1);
|
||||
});
|
||||
nodes.forEach((node) => {
|
||||
node.data.degree = degreeMap.get(node.id) || 0;
|
||||
});
|
||||
return data;
|
||||
};
|
||||
```
|
||||
|
||||
The result:
|
||||
In addition, we hope to use colors to represent the categories of nodes. If the data contains a field indicating the node category, we can use it directly. In this example, we use the clustering algorithm provided by @antv/algorithm to calculate the node clustering based on the graph structure. We also write it as a data processor:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*46GdQaNFiVIAAAAAAAAAAABkARQnAQ' width=450 height=450 alt='img' />
|
||||
```javascript
|
||||
const clusteringNodes = (data, options = {}, userGraphCore) => {
|
||||
if (!Algorithm?.labelPropagation) return;
|
||||
const clusteredData = Algorithm.louvain(data, false);
|
||||
const clusterMap = new Map();
|
||||
clusteredData.clusters.forEach((cluster, i) => {
|
||||
cluster.nodes.forEach((node) => {
|
||||
clusterMap.set(node.id, `c${i}`);
|
||||
});
|
||||
});
|
||||
data.nodes.forEach((node) => {
|
||||
node.data.cluster = clusterMap.get(node.id);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
```
|
||||
|
||||
The line width of the edges takes effect in the figure above. But the opacity and color setted in the global configurations are lost. The reason is that the global `style` object in graph instance is overrided by the second configure method. The solution is move all the styles to the data:
|
||||
Then register these data processors to the Graph in G6:
|
||||
|
||||
```javascript
|
||||
import { Graph as GraphBase, extend, Extensions } from '@antv/g6';
|
||||
const Graph = extend(GraphBase, {
|
||||
transforms: {
|
||||
'degree-calculator': degreeCalculator,
|
||||
'node-clustering': clusteringNodes,
|
||||
},
|
||||
nodes: {
|
||||
// Note that for package size management, G6 only registers circle-node and rect-node by default. Other built-in or custom node types need to be imported from Extensions and registered as follows
|
||||
'triangle-node': Extensions.TriangleNode,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
In this way, when instantiating the graph, we can configure this data processor on the graph. When data enters the Graph, it will produce data with the degree information through this data processor:
|
||||
|
||||
```javascript
|
||||
const graph = new Graph({
|
||||
// Note that the extended Graph is used here
|
||||
// ... Other graph configurations
|
||||
transforms: [
|
||||
'transform-v4-data', // Built-in converter that converts v4 formatted data to v5 format
|
||||
'degree-calculator', // Custom data processor that calculates the degree of nodes and stores it in data.degree
|
||||
'node-clustering', // Custom data processor that stores clustering results in the data.cluster field of nodes for the theme module to use later
|
||||
{
|
||||
// Built-in node size mapper that maps the value of the field (the degree field generated by the previous processor here) specified by field to the node size, and normalizes the node size to between 16 and 60
|
||||
type: 'map-node-size',
|
||||
field: 'degree',
|
||||
range: [16, 60],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
Now, after the data enters the Graph, it will pass through the data processors specified by `transforms` one by one. Each node of the internal data produced in the internal flow will have some calculated fields, such as:
|
||||
|
||||
```
|
||||
{ "id": "0", "data": { "label": "0", degree: 1, cluster: 'c0' } },
|
||||
{ "id": "1", "data": { "label": "1", degree: 3, cluster: 'c4' } },
|
||||
```
|
||||
|
||||
Then, in the function mapping configuration of the node, we can use these field values:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ...
|
||||
defaultEdge: {
|
||||
// Remove the style here
|
||||
labelCfg: {
|
||||
// The properties for label of edges
|
||||
autoRotate: true, // Whether to rotate the label according to the edges
|
||||
// ... other configurations
|
||||
// transforms: ...
|
||||
// edge: ...
|
||||
// node configuration in the graph configuration
|
||||
node: (model) => {
|
||||
// model is the user input data for the node, transformed and processed internally by G6
|
||||
const { id, data } = model;
|
||||
// Use different node types based on the degree field in the data
|
||||
let type = 'circle-node';
|
||||
if (data.degree === 2) type = 'rect-node';
|
||||
else if (data.degree === 1) type = 'triangle-node';
|
||||
// Badge shapes
|
||||
const badgeShapes = {
|
||||
fontSize: 12,
|
||||
lod: 0,
|
||||
};
|
||||
// Add different badges based on the degree field
|
||||
if (data.degree > 10) {
|
||||
badgeShapes[0] = {
|
||||
color: '#F86254',
|
||||
text: 'Important',
|
||||
position: 'rightTop',
|
||||
};
|
||||
}
|
||||
if (data.degree > 5) {
|
||||
badgeShapes[1] = {
|
||||
text: 'A',
|
||||
textAlign: 'center',
|
||||
color: '#EDB74B',
|
||||
position: 'right',
|
||||
};
|
||||
}
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
// Make sure to copy data here, otherwise other properties in the data may be lost
|
||||
...data,
|
||||
type,
|
||||
// Label shape style
|
||||
labelShape: {
|
||||
position: 'bottom',
|
||||
text: id,
|
||||
},
|
||||
// Label background style, if not undefined, a background shape will appear when there is text. More styling properties such as fill color, padding can be configured.
|
||||
labelBackgroundShape: {},
|
||||
// Icon shape, nodes with degree < 2 do not display an icon
|
||||
iconShape:
|
||||
data.degree <= 2
|
||||
? undefined
|
||||
: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
fill: '#fff',
|
||||
lod: 0,
|
||||
fontSize: data.keyShape.r - 4,
|
||||
},
|
||||
// Badge shapes
|
||||
badgeShapes,
|
||||
// Animation configuration
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
In the above node mapping function, the degree field is used. Clusters can be used with the new theme module provided by G6 v5. All you need to do is to configure it on the graph:
|
||||
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
// ... other graph configurations
|
||||
// transforms: ...
|
||||
// node: ...
|
||||
// edge: ...
|
||||
// Theme configuration
|
||||
theme: {
|
||||
type: 'spec',
|
||||
base: 'light', // Light theme
|
||||
specification: {
|
||||
node: {
|
||||
// Node color mapping based on the data.cluster field
|
||||
dataTypeField: 'cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Traverse the nodes data
|
||||
// const nodes = ...
|
||||
// nodes.forEach ...
|
||||
|
||||
// Traverse the egdes data
|
||||
const edges = remoteData.edges;
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight; // Mapping the weight in data to lineWidth
|
||||
// The styles are moved to here
|
||||
opt.style.opacity = 0.6;
|
||||
opt.style.stroke = 'grey';
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
```
|
||||
|
||||
In the above code, we added four configurations when instantiating the graph: `transform`, `theme`, `node`, and `edge`. The result is shown below:
|
||||
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*K3ADTJct0o4AAAAAAAAAAAAADmJ7AQ/original' width=450 height=450 alt='img' />
|
||||
|
||||
> Figure 3
|
||||
|
||||
As you can see, the nodes in the graph are rendered as circle, rectangle, and triangle based on their degrees. The sizes of the nodes are mapped to the degrees. The colors of the nodes are mapped to their categories. Similarly, we can also apply various styles mappings to the edges. I won't go into detail here.
|
||||
|
||||
## Complete Code
|
||||
|
||||
Here is the complete code:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@ -229,73 +362,162 @@ graph.render();
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<div id="container"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
<script>
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 800,
|
||||
height: 600,
|
||||
fitView: true,
|
||||
fitViewPadding: [20, 40, 50, 20],
|
||||
defaultNode: {
|
||||
size: 30,
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#fff',
|
||||
const { Graph: GraphBase, extend, Extensions } = G6;
|
||||
|
||||
// Custom data processor - degree calculator
|
||||
const degreeCalculator = (data, options, userGraphCore) => {
|
||||
const { edges, nodes } = data;
|
||||
const degreeMap = new Map();
|
||||
edges.forEach(({ source, target }) => {
|
||||
degreeMap.set(source, (degreeMap.get(source) || 0) + 1);
|
||||
degreeMap.set(target, (degreeMap.get(target) || 0) + 1);
|
||||
});
|
||||
nodes.forEach((node) => {
|
||||
node.data.degree = degreeMap.get(node.id) || 0;
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
// Custom data processor - node clustering
|
||||
const clusteringNodes = (data, options = {}, userGraphCore) => {
|
||||
if (!Algorithm?.labelPropagation) return;
|
||||
const clusteredData = Algorithm.louvain(data, false);
|
||||
const clusterMap = new Map();
|
||||
clusteredData.clusters.forEach((cluster, i) => {
|
||||
cluster.nodes.forEach((node) => {
|
||||
clusterMap.set(node.id, `c${i}`);
|
||||
});
|
||||
});
|
||||
data.nodes.forEach((node) => {
|
||||
node.data.cluster = clusterMap.get(node.id);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
const Graph = extend(BaseGraph, {
|
||||
transforms: {
|
||||
'degree-calculator': degreeCalculator,
|
||||
'node-clustering': clusteringNodes,
|
||||
},
|
||||
nodes: {
|
||||
'triangle-node': Extensions.TriangleNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
transforms: [
|
||||
'transform-v4-data',
|
||||
'degree-calculator',
|
||||
'node-clustering',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
field: 'degree',
|
||||
range: [16, 60],
|
||||
},
|
||||
],
|
||||
theme: {
|
||||
type: 'spec',
|
||||
base: 'light',
|
||||
specification: {
|
||||
node: {
|
||||
dataTypeField: 'cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
node: (model) => {
|
||||
const { id, data } = model;
|
||||
let type = 'circle-node';
|
||||
if (data.degree === 2) type = 'rect-node';
|
||||
else if (data.degree === 1) type = 'triangle-node';
|
||||
|
||||
const badgeShapes = {
|
||||
fontSize: 12,
|
||||
lod: 0,
|
||||
};
|
||||
|
||||
if (data.degree > 10) {
|
||||
badgeShapes[0] = {
|
||||
color: '#F86254',
|
||||
text: 'Important',
|
||||
position: 'rightTop',
|
||||
};
|
||||
}
|
||||
if (data.degree > 5) {
|
||||
badgeShapes[1] = {
|
||||
text: 'A',
|
||||
textAlign: 'center',
|
||||
color: '#EDB74B',
|
||||
position: 'right',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
type,
|
||||
labelShape: {
|
||||
position: 'bottom',
|
||||
text: id,
|
||||
},
|
||||
labelBackgroundShape: {},
|
||||
iconShape:
|
||||
data.degree <= 2
|
||||
? undefined
|
||||
: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
fill: '#fff',
|
||||
lod: 0,
|
||||
fontSize: data.keyShape.r - 4,
|
||||
},
|
||||
badgeShapes,
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
edge: {
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
const nodes = remoteData.nodes;
|
||||
const edges = remoteData.edges;
|
||||
nodes.forEach((node) => {
|
||||
if (!node.style) {
|
||||
node.style = {};
|
||||
}
|
||||
node.style.lineWidth = 1;
|
||||
node.style.stroke = '#666';
|
||||
node.style.fill = 'steelblue';
|
||||
switch (node.class) {
|
||||
case 'c0': {
|
||||
node.type = 'circle';
|
||||
break;
|
||||
}
|
||||
case 'c1': {
|
||||
node.type = 'rect';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
case 'c2': {
|
||||
node.type = 'ellipse';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight;
|
||||
edge.style.opacity = 0.6;
|
||||
edge.style.stroke = 'grey';
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
graph.read(remoteData);
|
||||
};
|
||||
main();
|
||||
</script>
|
||||
@ -303,4 +525,4 @@ graph.render();
|
||||
</html>
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️Attention:</strong></span> <br />Replace the url `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` to change the data into yours.
|
||||
**⚠️ Note:** <br /> If you need to replace the data, please replace `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` with the new data file address.
|
||||
|
@ -3,9 +3,9 @@ title: 元素及其配置
|
||||
order: 2
|
||||
---
|
||||
|
||||
图的元素特指图上的**节点** `Node` 和**边** `Edge` 。在上一章节中,我们已经将 **Tutorial 案例**的图绘制了出来,但是各个元素及其 `label` 在视觉上很简陋。本文通过将上一章节中简陋的元素美化成如下效果,介绍元素的属性、配置方法。
|
||||
图的元素特指图上的**节点** `Node` 、**边** `Edge` 和**节点分组** `Combo`。在上一章节中,我们已经将 **Tutorial 案例**的图绘制了出来,但是各个元素及其 `label` 在视觉上很简陋。本文通过将上一章节中简陋的元素美化成如下效果,介绍元素的属性、配置方法。
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*46GdQaNFiVIAAAAAAAAAAABkARQnAQ' width=450 height=450 alt='img'/>
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*K3ADTJct0o4AAAAAAAAAAAAADmJ7AQ/original' width=450 height=450 alt='img'/>
|
||||
|
||||
> 图 1 元素属性配置后的 **Tutorial 案例**。
|
||||
|
||||
@ -13,16 +13,16 @@ order: 2
|
||||
|
||||
### 图的元素
|
||||
|
||||
图的元素特指图上的**节点** `Node` 和**边** `Edge` 。G6 内置了一系列 [内置的节点](/zh/docs/manual/middle/elements/nodes/defaultNode) 和 [内置的边](/zh/docs/manual/middle/elements/edges/defaultEdge),供用户自由选择。G6 不同的内置节点或不同的内置边主要区别在于元素的 [图形 Shape](/zh/docs/manual/middle/elements/shape/shape-keyshape),例如,节点可以是圆形、矩形、图片等。
|
||||
图的元素特指图上的**节点** `Node` 、**边** `Edge` 和**节点分组** `Combo`。G6 内置了一系列 [内置的节点](https://g6-next.antv.antgroup.com/examples#item-defaultNodes),供用户自由选择。
|
||||
|
||||
## 元素的属性
|
||||
|
||||
不论是节点还是边,它们的属性分为两种:
|
||||
|
||||
- **样式属性 `style`**:对应 Canvas 中的各种样式,在元素[状态 State](/zh/docs/manual/middle/states/state) 发生变化时,可以被改变;
|
||||
- **其他属性**:例如图形类型( `type`)、id(`id` )一类在元素[状态 State](/zh/docs/manual/middle/states/state) 发生变化时不能被改变的属性。
|
||||
- **图形样式属性**:对应 Canvas 中的各种样式,在元素状态发生变化时,可以被改变;
|
||||
- **其他属性**:例如图形类型(`type`)、id(`id`)、位置(`x`,`y`)一类在元素状态发生变化时不能被改变的属性。
|
||||
|
||||
例如,G6 设定 hover 或 click 节点,造成节点状态的改变,只能自动改变节点的**样式属性**(如 `fill`、`stroke` 等**)**,**其他属性**(如 `type` 等)不能被改变。如果需要改变其他属性,要通过 [graph.updateItem](/zh/docs/api/graphFunc/item#graphupdateitemitem-model-stack) 手动配置。**样式属性**是一个名为 `style` 的对象, `style` 字段与其他属性并行。
|
||||
例如,G6 设定 hover 或 click 节点时,一般会通过在事件监听中调用 Graph 的设置状态 API `graph.setItemState` 来使节点进入某个状态,e.g. 选中状态。此时节点应当让上一定的样式变化来让响应选中状态。这只能自动改变节点的**图形样式属性**(如 `keyShape` 中的 `fill`、`stroke` 等**)**,**其他属性**(如 `type` 等)不能被改变。如果需要改变其他属性,需要更新数据 `graph.updateData`。**图形样式属性**存储在节点/边/ combo 的配置的 `xxxShape` 对象中,对应了不同图形的样式。
|
||||
|
||||
### 数据结构
|
||||
|
||||
@ -30,193 +30,317 @@ order: 2
|
||||
|
||||
```javascript
|
||||
{
|
||||
id: 'node0', // 元素的 id
|
||||
type: 'circle', // 元素的图形
|
||||
size: 40, // 元素的大小
|
||||
label: 'node0' // 标签文字
|
||||
visible: true, // 控制初次渲染显示与隐藏,若为 false,代表隐藏。默认不隐藏
|
||||
labelCfg: { // 标签配置属性
|
||||
positions: 'center',// 标签的属性,标签在元素中的位置
|
||||
style: { // 包裹标签样式属性的字段 style 与标签其他属性在数据结构上并行
|
||||
fontSize: 12 // 标签的样式属性,文字字体大小
|
||||
// ... // 标签的其他样式属性
|
||||
id: 'node0', // 元素的 id
|
||||
data: {
|
||||
x: 100, // 节点位置,若 graph 未配置 layout,且数据中所有的节点数据都有 x y 信息,则使用该信息绘制节点位置
|
||||
y: 100,
|
||||
type: 'circle-node', // 元素的图形。相比于 v4 多了 -node 后缀
|
||||
label: 'node0' // 标签文字
|
||||
keyShape: { // 主图形的样式属性
|
||||
r: 20 // 主图形的大小,如果是 rect-node 则是 width、height 控制
|
||||
fill: '#000', // 主图形的填充色
|
||||
stroke: '#888', // 主图形的描边色
|
||||
// ... // 主图形的其他样式属性
|
||||
},
|
||||
labelShape: {
|
||||
positions: 'center', // 标签的属性,标签在元素中的位置
|
||||
text: 'node-xxx-label'// 元素标签的文本,若不配置则使用 data.label 填充
|
||||
fontSize: 12 // 标签的样式属性,文字字体大小
|
||||
// ... // 标签的其他样式属性
|
||||
}
|
||||
}
|
||||
// ..., // 其他属性
|
||||
style: { // 包裹样式属性的字段 style 与其他属性在数据结构上并行
|
||||
fill: '#000', // 样式属性,元素的填充色
|
||||
stroke: '#888', // 样式属性,元素的描边色
|
||||
// ... // 其他样式属性
|
||||
}
|
||||
// ..., // 其他属性
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
边元素的属性数据结构与节点元素相似,只是其他属性中多了 `source` 和 `target` 字段,代表起始和终止节点的 `id`。<br />细化在图 1 中 **Tutorial 案例** 的视觉需求,我们需要完成:
|
||||
边元素的属性数据结构与节点元素相似,只是和 `id` 及 `data` 同级的还有 `source` 和 `target` 字段,代表起始和终止节点的 `id`。<br />细化在图 1 中 **Tutorial 案例** 的视觉需求,我们需要完成:
|
||||
|
||||
- 视觉效果:
|
||||
- R1: 节点的描边和填充色,对应节点样式属性:`fill`,`stroke`;
|
||||
- R2: 节点上标签文本的颜色,对应节点其他属性:`labelCfg`;
|
||||
- R3: 边的透明度和颜色,对应边样式属性:`opacity`,`stroke`;
|
||||
- R4: 边上标签文本的方向和颜色,对应边其他属性:`labelCfg`;
|
||||
- R1: 设置不同的节点类型,`'circle-node'`,`'rect-node'`,`'triangle-node'`;
|
||||
- R2: 绘制节点的 icon 和徽标,对应属性:`iconShape`,`badgeShapes`;
|
||||
- R3: 边上的箭头,对应边属性:`keyShape.endArrow`;
|
||||
- 数据与视觉映射:
|
||||
- R5: 根据数据中节点的 `class` 属性映射节点的形状,对应节点其他属性:`type`;
|
||||
- R6: 根据数据中边的 `weight` 属性映射边的粗细,对应边样式属性:`lineWidth`。
|
||||
- R5: 将节点进行聚类,并根据类别映射节点的颜色,对应属性:`keyShape.fill`;
|
||||
- R6: 根据节点的度树,映射到节点的大小上,对应属性:`keyShape.r`
|
||||
|
||||
## 配置属性
|
||||
|
||||
在 G6 中,根据不同的场景需求,有 7 种配置元素属性的方式。这里,我们简单介绍其中的两种:
|
||||
在 G6 中,根据不同的场景需求,有多种配置元素属性的方式。这里,我们介绍在实例化图时进行元素的配置,相比于 v4 graph 上只能配置静态的全局属性,v5 支持了 JSON Spec 属性映射和函数映射的配置方式:
|
||||
|
||||
1. 实例化图时配置元素的全局属性;
|
||||
2. 在数据中配置。
|
||||
|
||||
### 1. 实例化图时全局配置
|
||||
### 1. 实例化图时的 JSON Spec 配置方式
|
||||
|
||||
**适用场景:**所有节点统一的属性配置,所有边统一的属性配置。<br />**使用方式:**使用图的两个配置项:
|
||||
|
||||
- `defaultNode`:节点在默认状态下的**样式属性**(`style`)和**其他属性**;
|
||||
- `defaultEdge`:边在默认状态下的**样式属性**(`style`)和**其他属性**。
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️ 注意:</strong></span> 由于是统一的配置,不能根据数据中的属性(如 `class`、`weight`)等值的不同进行个性化设置,因此只能满足 R1、R2、R3、R4 需求。达到如下效果:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*bufdS75wcmMAAAAAAAAAAABkARQnAQ' width=450 height=450 alt='img' />
|
||||
|
||||
> 图 2 全局配置元素属性后的 **Tutorial 案例**。
|
||||
|
||||
<br />通过如下方式在实例化图时 `defaultNode` 和 `defaultEdge` ,可以完成上图效果:
|
||||
- `node`:节点在默认状态下的**图形样式属性**和**其他属性**;
|
||||
- `edge`:边在默认状态下的**图形样式属性**和**其他属性**。
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ... // 图的其他配置
|
||||
// 节点在默认状态下的样式配置(style)和其他配置
|
||||
defaultNode: {
|
||||
size: 30, // 节点大小
|
||||
// ... // 节点的其他配置
|
||||
// 节点样式配置
|
||||
style: {
|
||||
fill: 'steelblue', // 节点填充色
|
||||
stroke: '#666', // 节点描边色
|
||||
lineWidth: 1, // 节点描边粗细
|
||||
},
|
||||
// 节点在默认状态下的图形样式配置和其他配置
|
||||
node: {
|
||||
type: 'circle-node',
|
||||
keyShape: {
|
||||
r: 16, // 节点大小
|
||||
fill: '#4089FF', // 节点填充色
|
||||
}
|
||||
// 节点上的标签文本配置
|
||||
labelCfg: {
|
||||
// 节点上的标签文本样式配置
|
||||
style: {
|
||||
fill: '#fff', // 节点标签文字颜色
|
||||
},
|
||||
labelShape: {
|
||||
// 所有样式都支持如下的映射,表示根据数据 model.data 中的 label 字段,使用 formatter 返回的结果
|
||||
text: {
|
||||
fields: ['label'],
|
||||
formatter: model => model.data.label
|
||||
}
|
||||
fill: '#000', // 节点标签文字颜色
|
||||
},
|
||||
// 节点的动画配置
|
||||
animates: {
|
||||
// 数据/状态更新时
|
||||
update: [{
|
||||
shapeId: 'haloShape', // 背景光晕图形
|
||||
states: ['selected', 'active'] // 在 selected 和 active 状态变更时
|
||||
fields: ['opacity'], // 在透明度变化时,带动画地变化
|
||||
}, {
|
||||
shapeId: 'keyShape', // 主图形
|
||||
states: ['selected', 'active'] // 在 selected 和 active 状态变更时
|
||||
fields: ['lineWidth'], // 在描述边粗细变化时,带动画地变化
|
||||
}]
|
||||
}
|
||||
},
|
||||
// 边在默认状态下的样式配置(style)和其他配置
|
||||
defaultEdge: {
|
||||
edge: {
|
||||
// ... // 边的其他配置
|
||||
// 边样式配置
|
||||
style: {
|
||||
opacity: 0.6, // 边透明度
|
||||
stroke: 'grey', // 边描边颜色
|
||||
type: 'line-edge',
|
||||
keyShape: {
|
||||
opacity: 0.6, // 边主图形的透明度
|
||||
stroke: 'grey', // 边主图形描边颜色
|
||||
},
|
||||
// 边上的标签文本配置
|
||||
labelCfg: {
|
||||
labelShape: {
|
||||
autoRotate: true, // 边上的标签文本根据边的方向旋转
|
||||
},
|
||||
// 边的动画配置
|
||||
animates: {
|
||||
// 数据/状态更新时
|
||||
update: [{
|
||||
shapeId: 'haloShape', // 背景光晕图形
|
||||
states: ['selected', 'active'] // 在 selected 和 active 状态变更时
|
||||
fields: ['opacity'], // 在透明度变化时,带动画地变化
|
||||
}, {
|
||||
shapeId: 'keyShape', // 主图形
|
||||
states: ['selected', 'active'] // 在 selected 和 active 状态变更时
|
||||
fields: ['lineWidth'], // 在描述边粗细变化时,带动画地变化
|
||||
}]
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 2. 在数据中配置
|
||||
### 2. 实例化图时的函数映射配置方式
|
||||
|
||||
**适用场景:**不同节点/边可以有不同的个性化配置。<br />因此,这种配置方式可以满足 R5、R6 需求。<br />**使用方式:**可以直接将配置写入数据文件;也可以在读入数据后,通过遍历的方式写入配置。这里展示读入数据后,通过遍历的方式写入配置。下面代码展示了如何遍历数据进行属性的配置:
|
||||
**适用场景:**不同节点/边可以有不同的个性化配置。更加灵活。<br />**使用方式:**
|
||||
|
||||
```javascript
|
||||
const nodes = remoteData.nodes;
|
||||
nodes.forEach((node) => {
|
||||
if (!node.style) {
|
||||
node.style = {};
|
||||
}
|
||||
switch (
|
||||
node.class // 根据节点数据中的 class 属性配置图形
|
||||
) {
|
||||
case 'c0': {
|
||||
node.type = 'circle'; // class = 'c0' 时节点图形为 circle
|
||||
break;
|
||||
}
|
||||
case 'c1': {
|
||||
node.type = 'rect'; // class = 'c1' 时节点图形为 rect
|
||||
node.size = [35, 20]; // class = 'c1' 时节点大小
|
||||
break;
|
||||
}
|
||||
case 'c2': {
|
||||
node.type = 'ellipse'; // class = 'c2' 时节点图形为 ellipse
|
||||
node.size = [35, 20]; // class = 'c2' 时节点大小
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
在看函数映射代码之前,我们知道原始数据的每个节点比较简单:
|
||||
|
||||
graph.data(remoteData);
|
||||
```
|
||||
{ "id": "0", "data": { "label": "0" } },
|
||||
{ "id": "1", "data": { "label": "1" } },
|
||||
```
|
||||
|
||||
运行结果如下:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*JU6xRZLKCjcAAAAAAAAAAABkARQnAQ' width=450 height=450 alt='img' />
|
||||
|
||||
> 图 3
|
||||
|
||||
可以看到,图中有一些节点被渲染成了矩形,还有一些被渲染成了椭圆形。除了设置 `type` 属性之外,我们还覆盖了上文全局配置的节点的 `size` 属性,在矩形和椭圆的情况下,`size` 是一个数组;而在默认圆形的情况下,G6 将仍然读取全局配置的 `size` 属性为数字 `30`。也就是说,动态配置属性让我们既可以根据数据的不同配置不同的属性值,也可以有能力覆盖全局静态的属性值。
|
||||
|
||||
进一步地,我们尝试根据数据的比重不同,配置不一样边的粗细:
|
||||
一般图中度数(一跳邻居数量)越大的节点,越重要。我们可以用节点大小来表达这个信息。同时,若度数大到一定程度,还可以用更多额外的图形凸显其地位。我们可以通过数据处理器,提前计算节点的度数,
|
||||
|
||||
```javascript
|
||||
// const nodes = ...
|
||||
|
||||
// 遍历边数据
|
||||
const edges = remoteData.edges;
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
const degreeCalculator = (data, options, userGraphCore) => {
|
||||
const { edges, nodes } = data;
|
||||
const degreeMap = new Map();
|
||||
edges.forEach(({ source, target }) => {
|
||||
degreeMap.set(source, (degreeMap.get(source) || 0) + 1);
|
||||
degreeMap.set(target, (degreeMap.get(target) || 0) + 1);
|
||||
});
|
||||
nodes.forEach((node) => {
|
||||
node.data.degree = degreeMap.get(node.id) || 0;
|
||||
});
|
||||
return data;
|
||||
};
|
||||
```
|
||||
|
||||
结果如下:
|
||||
另外,我们希望可以用颜色表示节点的类别,如果数据中有表示节点类别的字段,后续可以直击诶使用。在这个例子中,我们利用 @antv/algorithm 提供的聚类算法,根据图结构计算节点聚类。同样把它写成一个数据处理器:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*46GdQaNFiVIAAAAAAAAAAABkARQnAQ' width=450 height=450 alt='img' />
|
||||
```javascript
|
||||
const clusteringNodes = (data, options = {}, userGraphCore) => {
|
||||
if (!Algorithm?.labelPropagation) return;
|
||||
const clusteredData = Algorithm.louvain(data, false);
|
||||
const clusterMap = new Map();
|
||||
clusteredData.clusters.forEach((cluster, i) => {
|
||||
cluster.nodes.forEach((node) => {
|
||||
clusterMap.set(node.id, `c${i}`);
|
||||
});
|
||||
});
|
||||
data.nodes.forEach((node) => {
|
||||
node.data.cluster = clusterMap.get(node.id);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
```
|
||||
|
||||
如图所示,边的粗细已经按照数据的比重成功渲染了出来,但是边原有的样式(透明度、颜色)却丢失了。这是因为我们提到过动态配置属性会覆盖全局配置属性,这里配置了 `style.lineWidth`,导致覆盖了全局的 `style` 对象。解决办法是将被覆盖的边的样式都移到动态配置里面来:
|
||||
然后这些数据处理器注册到 G6 的 Graph 中:
|
||||
|
||||
```javascript
|
||||
import { Graph as GraphBase, extend, Extensions } from '@antv/g6';
|
||||
const Graph = extend(GraphBase, {
|
||||
transforms: {
|
||||
'degree-calculator': degreeCalculator,
|
||||
'node-clustering': clusteringNodes,
|
||||
},
|
||||
nodes: {
|
||||
// 需要注意,为包体积管理,G6 只默认注册了 circle-node 和 rect-node,其他内置或自定义节点类型需要从 Extensions 引入并通过如下方式注册
|
||||
'triangle-node': Extensions.TriangleNode,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
这样,我们就可以在实例化图的时候,将这个数据处理器配置到图上。当数据进入 Graph 时,将会通过该数据处理器产出带有 degree 信息的数据:
|
||||
|
||||
```javascript
|
||||
const graph = new Graph({
|
||||
// 注意这里使用的是 extend 返回的 Graph
|
||||
// ... 其他 graph 配置
|
||||
transforms: [
|
||||
'transform-v4-data', // 内置提供的转换器,将 v4 格式数据转换为 v5 格式
|
||||
'degree-calculator', // 自定义的数据处理器,计算节点的度数存储到 data.degree 中
|
||||
'node-clustering', // 自定义的数据处理器,将聚类结果存储到节点的 data.cluster 字段上,方便后续主题模块使用
|
||||
{
|
||||
// 内置提供的节点大小映射器,将 field 指定的字段(这里指定了上一个处理器产生的 degree 字段)的值,映射到节点大小上,节点大小归一化到 16 ~ 60
|
||||
type: 'map-node-size',
|
||||
field: 'degree',
|
||||
range: [16, 60],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
现在,数据进入 Graph 后,将依次经过 `transform` 指定的数据处理器,产出的内部流转数据的每一个节点都会存在一些计算得出的字段,例如:
|
||||
|
||||
```
|
||||
{ "id": "0", "data": { "label": "0", degree: 1, cluster: 'c0' } },
|
||||
{ "id": "1", "data": { "label": "1", degree: 3, cluster: 'c4' } },
|
||||
```
|
||||
|
||||
然后我们在节点的函数映射配置中,可以使用这些字段值:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ...
|
||||
defaultEdge: {
|
||||
// 去掉全局配置的 style
|
||||
labelCfg: {
|
||||
// 边上的标签文本配置
|
||||
autoRotate: true, // 边上的标签文本根据边的方向旋转
|
||||
// ...其他配置项
|
||||
// transforms: ...
|
||||
// edge: ...
|
||||
// graph 配置项中的 node
|
||||
node: (model) => {
|
||||
// model 是该节点用户输入数据,在 transform 作用后的、在 G6 内部流转的数据
|
||||
const { id, data } = model;
|
||||
// 根据数据中的 degree 字段,使用不同的节点类型
|
||||
let type = 'circle-node';
|
||||
if (data.degree === 2) type = 'rect-node';
|
||||
else if (data.degree === 1) type = 'triangle-node';
|
||||
|
||||
// 徽标图形
|
||||
const badgeShapes = {
|
||||
fontSize: 12,
|
||||
lod: 0,
|
||||
};
|
||||
// 根据 degree 字段,增加不同的徽标
|
||||
if (data.degree > 10) {
|
||||
badgeShapes[0] = {
|
||||
color: '#F86254',
|
||||
text: 'Important',
|
||||
position: 'rightTop',
|
||||
};
|
||||
}
|
||||
if (data.degree > 5) {
|
||||
badgeShapes[1] = {
|
||||
text: 'A',
|
||||
textAlign: 'center',
|
||||
color: '#EDB74B',
|
||||
position: 'right',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
// 注意必须复制 data 到此处,否则可能丢失数据中的其他属性
|
||||
...data,
|
||||
type,
|
||||
// 文本图形样式
|
||||
labelShape: {
|
||||
position: 'bottom',
|
||||
text: id,
|
||||
},
|
||||
// 文本背景样式,不为 undefined 就代表在有文本时,出现背景图形。其中还可以配置更多的样式属性,例如 fill 填充色、padding 等
|
||||
labelBackgroundShape: {},
|
||||
// icon 图形,degree < 2 的节点不展示 icon
|
||||
iconShape:
|
||||
data.degree <= 2
|
||||
? undefined
|
||||
: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
fill: '#fff',
|
||||
lod: 0,
|
||||
fontSize: data.keyShape.r - 4,
|
||||
},
|
||||
// 徽标图形
|
||||
badgeShapes,
|
||||
// 动画配置
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
在上面的函数映射中,使用了 degree。cluster 可以被使用的 v5 全新提供的主题模块 theme 中,只需要在 graph 上简单配置:
|
||||
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
// ... 其他图配置
|
||||
// transforms: ...
|
||||
// node: ...
|
||||
// edge: ...
|
||||
// 主题配置
|
||||
theme: {
|
||||
type: 'spec',
|
||||
base: 'light', // 白色主题
|
||||
specification: {
|
||||
node: {
|
||||
// 节点颜色映射 data.cluster 字段
|
||||
dataTypeField: 'cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 遍历点数据
|
||||
// const nodes = ...
|
||||
// nodes.forEach ...
|
||||
|
||||
// 遍历边数据
|
||||
const edges = remoteData.edges;
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
|
||||
// 移到此处
|
||||
edge.style.opacity = 0.6;
|
||||
edge.style.stroke = 'grey';
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
```
|
||||
|
||||
以上,我们在图实例化时增加了四个配置:`transform`,`theme`,`node`,`edge`。运行结果如下:
|
||||
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*K3ADTJct0o4AAAAAAAAAAAAADmJ7AQ/original' width=450 height=450 alt='img' />
|
||||
|
||||
> 图 3
|
||||
|
||||
可以看到,图中节点根据度数不同,被渲染成了圆形、矩形、三角形。且度数被映射到了节点大小上。颜色映射到了节点类别上。同理,我们也可以对边做各种样式映射的处理。这里不一一赘述。
|
||||
|
||||
## 完整代码
|
||||
|
||||
至此,完整代码如下:
|
||||
@ -229,73 +353,162 @@ graph.render();
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<div id="container"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
<script>
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 800,
|
||||
height: 600,
|
||||
fitView: true,
|
||||
fitViewPadding: [20, 40, 50, 20],
|
||||
defaultNode: {
|
||||
size: 30,
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#fff',
|
||||
const { Graph: GraphBase, extend, Extensions } = G6;
|
||||
|
||||
// 自定义数据处理器 - 度数计算
|
||||
const degreeCalculator = (data, options, userGraphCore) => {
|
||||
const { edges, nodes } = data;
|
||||
const degreeMap = new Map();
|
||||
edges.forEach(({ source, target }) => {
|
||||
degreeMap.set(source, (degreeMap.get(source) || 0) + 1);
|
||||
degreeMap.set(target, (degreeMap.get(target) || 0) + 1);
|
||||
});
|
||||
nodes.forEach((node) => {
|
||||
node.data.degree = degreeMap.get(node.id) || 0;
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
// 自定义数据处理器 - 节点聚类
|
||||
const clusteringNodes = (data, options = {}, userGraphCore) => {
|
||||
if (!Algorithm?.labelPropagation) return;
|
||||
const clusteredData = Algorithm.louvain(data, false);
|
||||
const clusterMap = new Map();
|
||||
clusteredData.clusters.forEach((cluster, i) => {
|
||||
cluster.nodes.forEach((node) => {
|
||||
clusterMap.set(node.id, `c${i}`);
|
||||
});
|
||||
});
|
||||
data.nodes.forEach((node) => {
|
||||
node.data.cluster = clusterMap.get(node.id);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
const Graph = extend(BaseGraph, {
|
||||
transforms: {
|
||||
'degree-calculator': degreeCalculator,
|
||||
'node-clustering': clusteringNodes,
|
||||
},
|
||||
nodes: {
|
||||
'triangle-node': Extensions.TriangleNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
transforms: [
|
||||
'transform-v4-data',
|
||||
'degree-calculator',
|
||||
'node-clustering',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
field: 'degree',
|
||||
range: [16, 60],
|
||||
},
|
||||
],
|
||||
theme: {
|
||||
type: 'spec',
|
||||
base: 'light',
|
||||
specification: {
|
||||
node: {
|
||||
dataTypeField: 'cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
node: (model) => {
|
||||
const { id, data } = model;
|
||||
let type = 'circle-node';
|
||||
if (data.degree === 2) type = 'rect-node';
|
||||
else if (data.degree === 1) type = 'triangle-node';
|
||||
|
||||
const badgeShapes = {
|
||||
fontSize: 12,
|
||||
lod: 0,
|
||||
};
|
||||
|
||||
if (data.degree > 10) {
|
||||
badgeShapes[0] = {
|
||||
color: '#F86254',
|
||||
text: 'Important',
|
||||
position: 'rightTop',
|
||||
};
|
||||
}
|
||||
if (data.degree > 5) {
|
||||
badgeShapes[1] = {
|
||||
text: 'A',
|
||||
textAlign: 'center',
|
||||
color: '#EDB74B',
|
||||
position: 'right',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
type,
|
||||
labelShape: {
|
||||
position: 'bottom',
|
||||
text: id,
|
||||
},
|
||||
labelBackgroundShape: {},
|
||||
iconShape:
|
||||
data.degree <= 2
|
||||
? undefined
|
||||
: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
fill: '#fff',
|
||||
lod: 0,
|
||||
fontSize: data.keyShape.r - 4,
|
||||
},
|
||||
badgeShapes,
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
edge: {
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
const nodes = remoteData.nodes;
|
||||
const edges = remoteData.edges;
|
||||
nodes.forEach((node) => {
|
||||
if (!node.style) {
|
||||
node.style = {};
|
||||
}
|
||||
node.style.lineWidth = 1;
|
||||
node.style.stroke = '#666';
|
||||
node.style.fill = 'steelblue';
|
||||
switch (node.class) {
|
||||
case 'c0': {
|
||||
node.type = 'circle';
|
||||
break;
|
||||
}
|
||||
case 'c1': {
|
||||
node.type = 'rect';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
case 'c2': {
|
||||
node.type = 'ellipse';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight;
|
||||
edge.style.opacity = 0.6;
|
||||
edge.style.stroke = 'grey';
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
graph.read(remoteData);
|
||||
};
|
||||
main();
|
||||
</script>
|
||||
@ -303,4 +516,4 @@ graph.render();
|
||||
</html>
|
||||
```
|
||||
|
||||
**⚠️ 注意:** <br /> 若需更换数据,请替换 `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` 为新的数据文件地址。
|
||||
**⚠️ 注意:** <br /> 若需更换数据,请替换 `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` 为新的数据文件地址。
|
||||
|
@ -3,7 +3,7 @@ title: Conclusion
|
||||
order: 7
|
||||
---
|
||||
|
||||
Congratulations! You have created a graph visualization powered by G6. You have learnt:
|
||||
Congratulations! You have successfully created a graph visualization application based on G6 and learned the following:
|
||||
|
||||
- Basic rendering;
|
||||
- Load remote data;
|
||||
@ -12,8 +12,13 @@ Congratulations! You have created a graph visualization powered by G6. You have
|
||||
- Add interaction behavior;
|
||||
- Add tools.
|
||||
|
||||
[Complete Code of Tutorial Demo](https://codepen.io/Yanyan-Wang/pen/mdbYZvZ).
|
||||
[Complete Code of Tutorial Demo](https://codesandbox.io/s/g6-v5-tutorial-j67vnm?file=/index.js).
|
||||
|
||||
If you wish to go further in G6, please check out [Key Concept in G6](/en/docs/manual/middle/graph); For further information, check out [Further Reading](/en/docs/manual/advanced/coordinate-system).
|
||||
The G6 5.0 documentation is still under construction. For now, you can refer to the [API](https://g6-next.antv.antgroup.com/en/apis) for development.
|
||||
|
||||
You can also refer to [G6 API](/en/docs/api/Graph) during the process of development.
|
||||
<iframe src="https://codesandbox.io/embed/g6-v5-tutorial-j67vnm?fontsize=14&hidenavigation=1&theme=light"
|
||||
style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
|
||||
title="g6-v5-tutorial"
|
||||
allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
|
||||
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
||||
></iframe>
|
||||
|
@ -7,13 +7,18 @@ order: 7
|
||||
|
||||
- 基本绘制方法
|
||||
- 远程数据加载
|
||||
- 属性配置
|
||||
- 样式配置
|
||||
- 布局运用
|
||||
- 增加交互
|
||||
- 添加辅助工具
|
||||
|
||||
完整代码见:[Tutorial 案例代码](https://codepen.io/Yanyan-Wang/pen/mdbYZvZ)。
|
||||
完整代码见:<a href='https://codesandbox.io/s/g6-v5-tutorial-j67vnm?file=/index.js' target='_blank'>Tutorial 案例代码</a>。
|
||||
|
||||
如果你希望继续学习深入的 G6 知识,请查看 [G6 核心概念](/zh/docs/manual/middle/graph);关于 G6 的扩展内容,请查看 [G6 扩展阅读](/zh/docs/manual/advanced/coordinate-system)。
|
||||
G6 5.0 文档仍在建设中。目前您可以查阅 [API](https://g6-next.antv.antgroup.com/apis) 进行开发。
|
||||
|
||||
开发过程中可通过 [G6 API](/zh/docs/api/Graph) 快速查询。
|
||||
<iframe src="https://codesandbox.io/embed/g6-v5-tutorial-j67vnm?fontsize=14&hidenavigation=1&theme=light"
|
||||
style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
|
||||
title="g6-v5-tutorial"
|
||||
allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
|
||||
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
||||
></iframe>
|
||||
|
@ -3,181 +3,144 @@ title: Render the Tutorial Demo
|
||||
order: 1
|
||||
---
|
||||
|
||||
In this chapter, we preliminary configure and render the **Tutorial Demo**. You will learn the common configurations of Graph.
|
||||
This article will provide a simple drawing and configuration example for the **Tutorial Example**. Through this article, you will learn about some commonly used configuration options and their functions when creating a general graph.
|
||||
|
||||
## Basic Rendering
|
||||
## Basic Drawing
|
||||
|
||||
### Create a HTML Container
|
||||
### Creating a Container
|
||||
|
||||
Create an HTML container for graph canvas, `div` tag in general. G6 will append a `canvas` tag to it and draw graph on the `canvas`.
|
||||
To contain the G6 graph, you need to create a container in HTML, usually a `div` tag. G6 will append a `canvas` tag under this container and draw the graph in it.
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
|
||||
<div id="container"></div>
|
||||
<!-- Import G6 -->
|
||||
<!-- ... -->
|
||||
</body>
|
||||
```
|
||||
|
||||
### Data Preparation
|
||||
### Preparing the Data
|
||||
|
||||
The data for G6 should be JSON format, includes array properties `nodes` and `edges`:
|
||||
The data source for G6 is a JSON-formatted object. The data format of v5 is different from v4. For more information, please refer to the [Data Format Change](https://g6-next.antv.antgroup.com/en/manual/upgrade#1%EF%B8%8F%E2%83%A3-%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E5%8F%98%E6%9B%B4) section. v5 also provides a data conversion processor for v4 data, which will be explained in the following graph configuration.
|
||||
|
||||
```html
|
||||
<script>
|
||||
// console.log(G6.Global.version);
|
||||
const initData = {
|
||||
// The array of nodes
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1', // String, unique and required
|
||||
x: 100, // Number, the x coordinate
|
||||
y: 200, // Number, the y coordinate
|
||||
label: 'Source', // The label of the node
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 300,
|
||||
y: 200,
|
||||
label: 'Target',
|
||||
},
|
||||
],
|
||||
// The array of edges
|
||||
edges: [
|
||||
// An edge links from node1 to node2
|
||||
{
|
||||
source: 'node1', // String, required, the id of the source node
|
||||
target: 'node2', // String, required, the id of the target node
|
||||
label: 'I am an edge', // The label of the edge
|
||||
},
|
||||
],
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️Attention:</strong></span>
|
||||
|
||||
- `nodes` is an array of nodes, the `id` is an unique and required property; the `x` and `y` are the coordinates of the node;
|
||||
- `edges` is an array of edges, `source` and `target` are required, represent the `id` of the source node and the `id` of the target node respectively.
|
||||
- The properties of node and edge are described in [Built-in Nodes](/en/docs/manual/middle/elements/nodes/defaultNode) and [Built-in Edges](/en/docs/manual/middle/elements/edges/defaultEdge) document.
|
||||
|
||||
### Instantiate the Graph
|
||||
|
||||
The container, width, and height are required configurations when instantiating a Graph:
|
||||
|
||||
```html
|
||||
<script>
|
||||
// const initData = { ... }
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode', // String | HTMLElement, required, the id of DOM element or an HTML node
|
||||
width: 800, // Number, required, the width of the graph
|
||||
height: 500, // Number, required, the height of the graph
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Load the Data and Render
|
||||
|
||||
Load data and render are two separated steps.
|
||||
|
||||
```html
|
||||
<script>
|
||||
// const initData = { ... }
|
||||
// const graph = ...
|
||||
graph.data(data); // Load the data defined in Step 2
|
||||
graph.render(); // Render the graph
|
||||
</script>
|
||||
```
|
||||
|
||||
### The Result
|
||||
|
||||
After calling `graph.render()` , G6 will render the graph according to the data.
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*YTfpQYVGhuEAAAAAAAAAAABkARQnAQ' width=400 alt='img' />
|
||||
|
||||
## Load the Real Data
|
||||
|
||||
In the above demo, we render a graph with two nodes and one edge defined in the code directly. For real scenario, the data might be loaded remotely. We prepare the JSON data for **Tutorial Demo** with the address: <br />`https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json`
|
||||
|
||||
### Load the Remote Data
|
||||
|
||||
Modify index.html to load remote data asynchronously by `fetch`, and pass it to the instance of G6 Graph:
|
||||
|
||||
```html
|
||||
<script>
|
||||
// const graph = ...
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
|
||||
// ...
|
||||
graph.data(remoteData); // Load the remote data
|
||||
graph.render(); // Render the graph
|
||||
};
|
||||
main();
|
||||
</script>
|
||||
```
|
||||
|
||||
> `fetch` allows us to fetch the remote data asynchronously, and controll the process by `async`/`await`. If you are curious about `fetch` and `async`/`await`, please refer to: [async function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
|
||||
|
||||
We will get the following result with the code above:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*KzQaSZKIsQoAAAAAAAAAAABkARQnAQ' width=550 height=350 alt='img' />
|
||||
|
||||
The data has been loaded correctly. But the result is a little bit strange due to the large amount of nodes and edges. Limited by the size of canvas, part of the graph is arranged out of the view. We are going to solve all these problems now.
|
||||
|
||||
Here goes a part of tutorial-data.json. There are `x` and `y` in node data, and some of them are not in the range of `width: 800, height: 600`.
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{ "id": "0", "label": "n0", "class": "c0", "x": 1000, "y": -100 },
|
||||
{ "id": "1", "label": "n1", "class": "c0", "x": 300, "y": -10 }
|
||||
//...
|
||||
],
|
||||
"edges": [
|
||||
//...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
G6 will render the graph according to the position information in the data once G6 finds `x` and `y` in the data. This mechanism satisfies the requirement that rendering the source data. To solve the problem of the graph out of the view port partially, two configurations are provided:
|
||||
|
||||
- `fitView`: Whether to fit the graph to the canvas;
|
||||
- `fitViewPadding`: The padding between the content of the graph and the borders of the canvas.
|
||||
|
||||
We modify the code about instantiating Graph as shown below:
|
||||
The data needs to include the nodes (`nodes`) and edges (`edges`) fields, represented by arrays:
|
||||
|
||||
```javascript
|
||||
const data = {
|
||||
// Nodes
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1', // string | number, required if the node exists, the unique identifier of the node
|
||||
data: {
|
||||
// Store some business attributes or special node configurations
|
||||
name: 'Circle1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'node2', // string | number, required if the node exists, the unique identifier of the node
|
||||
data: {
|
||||
// Store some business attributes or special node configurations
|
||||
name: 'Circle2',
|
||||
},
|
||||
},
|
||||
],
|
||||
// Edges
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1', // string | number, required, the id of the starting point
|
||||
target: 'node2', // string | number, required, the id of the target point
|
||||
data: {}, // Store some business attributes or special edge configurations
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️ Note:</strong></span>
|
||||
|
||||
- The `nodes` array contains node objects. Each node object has a unique and necessary `id` to identify different nodes. `x` and `y` specify the position of the node.
|
||||
- The `edges` array contains edge objects. `source` and `target` are the required properties of each edge, representing the `id` of the starting point and the `id` of the target point, respectively.
|
||||
|
||||
### Graph Instantiation
|
||||
|
||||
When instantiating the graph, at least the container needs to be set for the graph. If you are using the graph data format of G6 v4, you can configure `transforms: ['transform-v4-data']` when instantiating the graph. `'transform-v4-data'` is a built-in data converter in G6 v5, which will format the v4 data into the format required by v5 after reading the data.
|
||||
|
||||
```javascript
|
||||
// const data = { ... }
|
||||
const graph = new G6.Graph({
|
||||
// ...
|
||||
fitView: true,
|
||||
fitViewPadding: [20, 40, 50, 20],
|
||||
container: 'container', // String | HTMLElement, the id of the container created in Step 1 or the container itself
|
||||
width: 800, // number, the width of the graph, use the width of the container if not specified
|
||||
height: 500, // number, the height of the graph, use the height of the container if not specified
|
||||
// transforms: ['transform-v4-data'] //
|
||||
});
|
||||
```
|
||||
|
||||
The result from this code shows that the graph has been fitted to the canvas: <img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*wAXzRJaNTXUAAAAAAAAAAABkARQnAQ' width=850 alt='img' />
|
||||
### Rendering the Graph
|
||||
|
||||
## Common Configuration
|
||||
```javascript
|
||||
// const initData = { ... }
|
||||
// const graph = ...
|
||||
graph.read(data); // Load data
|
||||
```
|
||||
|
||||
The configurations below will be used in the following Tutorial:
|
||||
### Drawing Result
|
||||
|
||||
| Name | Type | Options / Example | Default | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| fitView | Boolean | true / false | false | Whether to fit the graph to the canvas. |
|
||||
| fitViewPadding | Number / Array | 20 / [ 20, 40, 50, 20 ] | 0 | The padding between the content of the graph and the borders of the canvas. |
|
||||
| animate | Boolean | true / false | false | Whether to activate the global animation. |
|
||||
| modes | Object | {<br /> default: [ 'drag-node', 'drag-canvas' ]<br />} | null | The set of graph interaction modes. This is a complicated concept, refer to [Mode](/en/docs/manual/middle/states/mode) for more detial. |
|
||||
| defaultNode | Object | {<br /> type: 'circle',<br /> color: '#000',<br /> style: {<br /> ......<br /> }<br />} | null | The default global properties for nodes, includes styles properties and other properties. |
|
||||
| defaultEdge | Object | {<br /> type: 'polyline',<br /> color: '#000',<br /> style: {<br /> ......<br /> }<br />} | null | The default global properties for edges, includes styles properties and other properties. |
|
||||
| nodeStateStyles | Object | {<br /> hover: {<br /> ......<br /> },<br /> select: {<br /> ......<br /> }<br />} | null | The style properties of nodes in different states except for default state. Such as hover, select. |
|
||||
| edgeStateStyles | Object | {<br /> hover: {<br /> ......<br /> },<br /> select: {<br /> ......<br /> }<br />} | null | The style properties of edges in different states except for default state. Such as hover, select. |
|
||||
After calling the `graph.read(data)` method, G6 will draw the graph based on the loaded data. The result is as follows:
|
||||
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*1ejjTYxD94QAAAAAAAAAAAAADmJ7AQ/original' width=400 alt='img' />
|
||||
|
||||
## Loading Real Data
|
||||
|
||||
In the previous sections, we used data with only two nodes and one edge, and directly defined the data in the code. However, the data in real scenarios is usually loaded from remote interfaces. For convenience, we have prepared a JSON data file for readers. The address is as follows:
|
||||
|
||||
<br />`https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json`
|
||||
|
||||
### Load Remote Data
|
||||
|
||||
Modify index.html to asynchronously load remote data sources using the `fetch` function and pass them into the G6 graph instance:
|
||||
|
||||
```javascript
|
||||
// const graph = ...
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
// ...
|
||||
graph.read(remoteData); // Load remote data
|
||||
};
|
||||
main();
|
||||
```
|
||||
|
||||
> The `fetch` function allows us to make network requests and load data, and its asynchronous process can be better controlled using `async`/`await`. Here, for convenience, we put the main logic inside the `main` function. If you have any questions about fetch and `async`/`await`, you can refer to the <a href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function' target='_blank'>async function</a>, <a href='https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API' target='_blank'>Fetch API</a>.
|
||||
|
||||
After running the code, we get the following result:
|
||||
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*WmGnQKAbmtsAAAAAAAAAAAAADmJ7AQ/original' width=550 alt='img' />
|
||||
|
||||
At first glance, the image looks a bit strange, but the data has actually been correctly loaded. Due to the large amount of data, there are many nodes and edges, and the grid layout, which is the default, does not show the connection between the nodes. Next, we will use more graph configurations to make the data clearer and more visually appealing.
|
||||
|
||||
## Common Configurations
|
||||
|
||||
The configurations used in this article and the common configurations used in subsequent tutorials are as follows:
|
||||
|
||||
| Configuration | Type | Options / Examples | Default | Description |
|
||||
| ------------- | ----------------- | -------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| transforms | Array | ['transform-v4-data', { type: 'map-node-size', field: 'value' }] | null | Data processors. After the original user data enters the Graph, the processors in the transform are executed in order to obtain the processed data. |
|
||||
| |
|
||||
| modes | Object | {<br /> default: [ 'drag-node', 'drag-canvas' ]<br />} | null | The collection of behavior modes on the graph. |
|
||||
| node | Object / Function | {<br /> type: 'circle',<br /> keyShape: {<br /> ......<br /> }<br />} | null | The global attribute mapper of the node, including general attributes and style attributes (style). v5 also supports function mapping. |
|
||||
| edge | Object / Function | {<br /> type: 'polyline',<br /> keyShape: {<br /> ......<br /> }<br />} | null | The global attribute mapper of the edge, including general attributes and style attributes (style). v5 also supports function mapping. |
|
||||
| nodeState | Object | {<br /> hover: {<br /> ......<br /> },<br /> select: {<br /> ......<br /> }<br />} | null | The style attributes (style) of the node in states other than the default state, such as hover and select. |
|
||||
| |
|
||||
| edgeState | Object | {<br /> hover: {<br /> ......<br /> },<br /> select: {<br /> ......<br /> }<br />} | null | The style attributes (style) of the edge in states other than the default state, such as hover and select. |
|
||||
| |
|
||||
| plugins | Array | ['minimap', { type: 'tooltip', itemTypes: ['node'] }] | null | Plugins |
|
||||
|
||||
## Complete Code
|
||||
|
||||
The complete code is as follows:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@ -186,26 +149,21 @@ The configurations below will be used in the following Tutorial:
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<div id="container"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
<script>
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
container: 'container',
|
||||
width: 1000,
|
||||
height: 600,
|
||||
fitView: true,
|
||||
fitViewPadding: [20, 40, 50, 20],
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
graph.read(remoteData);
|
||||
};
|
||||
main();
|
||||
</script>
|
||||
@ -213,4 +171,4 @@ The configurations below will be used in the following Tutorial:
|
||||
</html>
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️ Attention:</strong></span><br /> Replace the url `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` to change the data into yours.
|
||||
**⚠️ Note:** <br /> If you need to replace the data, please replace `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` with the new data file address.
|
||||
|
@ -13,7 +13,7 @@ order: 1
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<div id="container"></div>
|
||||
|
||||
<!-- 引入 G6 -->
|
||||
<!-- ... -->
|
||||
@ -22,100 +22,93 @@ order: 1
|
||||
|
||||
### 数据准备
|
||||
|
||||
引入 G6 的数据源为 JSON 格式的对象。该对象中需要有节点(`nodes`)和边(`edges`)字段,分别用数组表示:
|
||||
引入 G6 的数据源为 JSON 格式的对象。v5 的数据格式与 v4 有所不同,详见[如何升级-数据格式变更](https://g6-next.antv.antgroup.com/manual/upgrade#1%EF%B8%8F%E2%83%A3-%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E5%8F%98%E6%9B%B4)。v5 也提供了 v4 数据的转换处理器,在下面图配置中将进行讲解。
|
||||
|
||||
```html
|
||||
<script>
|
||||
// console.log(G6.Global.version);
|
||||
const initData = {
|
||||
// 点集
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1', // 节点的唯一标识
|
||||
x: 100, // 节点横坐标
|
||||
y: 200, // 节点纵坐标
|
||||
label: '起始点', // 节点文本
|
||||
数据中需要有节点(`nodes`)和边(`edges`)字段,分别用数组表示:
|
||||
|
||||
```javascript
|
||||
const data = {
|
||||
// 点集
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1', // string | number, 该节点存在则必须,节点的唯一标识
|
||||
data: {
|
||||
// 存放一些业务属性,或特殊的节点配置
|
||||
name: 'Circle1',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 300,
|
||||
y: 200,
|
||||
label: '目标点',
|
||||
},
|
||||
{
|
||||
id: 'node2', // string | number, 该节点存在则必须,节点的唯一标识
|
||||
data: {
|
||||
// 存放一些业务属性,或特殊的节点配置
|
||||
name: 'Circle2',
|
||||
},
|
||||
],
|
||||
// 边集
|
||||
edges: [
|
||||
// 表示一条从 node1 节点连接到 node2 节点的边
|
||||
{
|
||||
source: 'node1', // 起始点 id
|
||||
target: 'node2', // 目标点 id
|
||||
label: '我是连线', // 边的文本
|
||||
},
|
||||
],
|
||||
};
|
||||
</script>
|
||||
},
|
||||
],
|
||||
// 边集
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
source: 'node1', // string | number, 必须,起始点 id
|
||||
target: 'node2', // string | number, 必须,目标点 id
|
||||
data: {}, // 存放一些业务属性,或特殊的边配置
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️ 注意:</strong></span>
|
||||
|
||||
- `nodes` 数组中包含节点对象,唯一的 `id` 是每个节点对象中必要的属性,`x`、 `y` 用于定位;
|
||||
- `edges` 数组中包含边对象,`source` 和 `target` 是每条边的必要属性,分别代表了该边的起始点 `id` 与 目标点 `id`。
|
||||
- 点和边的更多属性参见:[内置的节点](/zh/docs/manual/middle/elements/nodes/defaultNode),[内置的边](/zh/docs/manual/middle/elements/edges/defaultEdge) 教程。
|
||||
- `nodes` 数组中包含节点对象。每个节点对象中唯一的、必要的 `id` 以标识不同的节点,`x`、 `y` 指定该节点的位置;
|
||||
- `edges` 数组中包含边对象。`source` 和 `target` 是每条边的必要属性,分别代表了该边的起始点 `id` 与 目标点 `id`。
|
||||
|
||||
### 图实例化
|
||||
|
||||
图实例化时,至少需要为图设置容器、宽、高:
|
||||
图实例化时,至少需要为图设置容器。若您使用的是 G6 v4 的图数据格式,可以在实例化图时配置 `transforms: ['transform-v4-data']`,`'transform-v4-data'` 是 G6 v5 的内置数据转换器,G6 将在读取数据后,将 v4 数据格式化 v5 要求的格式
|
||||
|
||||
```html
|
||||
<script>
|
||||
// const initData = { ... }
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode', // 指定挂载容器
|
||||
width: 800, // 图的宽度
|
||||
height: 500, // 图的高度
|
||||
});
|
||||
</script>
|
||||
```javascript
|
||||
// const data = { ... }
|
||||
const graph = new G6.Graph({
|
||||
container: 'constainer', // String | HTMLElement,必须,在 Step 1 中创建的容器 id 或容器本身
|
||||
width: 800, // number,图的宽度,不指定则使用容器宽度
|
||||
height: 500, // number,图的高度,不指定则使用容器高度
|
||||
// transforms: ['transform-v4-data'] //
|
||||
});
|
||||
```
|
||||
|
||||
### 图的渲染
|
||||
|
||||
数据的加载和图的渲染是两个步骤,可以分开进行。
|
||||
|
||||
```html
|
||||
<script>
|
||||
// const initData = { ... }
|
||||
// const graph = ...
|
||||
graph.data(initData); // 加载数据
|
||||
graph.render(); // 渲染
|
||||
</script>
|
||||
```javascript
|
||||
// const initData = { ... }
|
||||
// const graph = ...
|
||||
graph.read(data); // 加载数据
|
||||
```
|
||||
|
||||
### 绘制结果
|
||||
|
||||
调用 `graph.render()` 方法之后,G6 引擎会根据加载的数据进行图的绘制。结果如下:
|
||||
调用 `graph.read(data)` 方法之后,G6 会根据加载的数据进行图的绘制。结果如下:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*YTfpQYVGhuEAAAAAAAAAAABkARQnAQ' width=400 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*1ejjTYxD94QAAAAAAAAAAAAADmJ7AQ/original' width=400 alt='img' />
|
||||
|
||||
## 真实数据加载
|
||||
|
||||
上文中,我们使用了仅含有两个节点和一条边的数据,直接将数据定义放在了代码中。而真实场景的数据通常是远程接口请求加载的。为了方便,我们已经给读者准备好了一份 JSON 数据文件,地址如下:<br />`https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json`
|
||||
上文中,我们使用了仅含有两个节点和一条边的数据,直接将数据定义放在了代码中。而真实场景的数据通常是远程接口请求加载的。为了方便,我们已经给读者准备好了一份 JSON 数据文件,地址如下:<br />`https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json`
|
||||
|
||||
### 加载远程数据
|
||||
|
||||
修改 index.html,通过 `fetch` 函数异步加载远程的数据源,并传入 G6 的图实例中:
|
||||
|
||||
```html
|
||||
```javascript
|
||||
<script>
|
||||
// const graph = ...
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
|
||||
// ...
|
||||
graph.data(remoteData); // 加载远程数据
|
||||
graph.render(); // 渲染
|
||||
graph.read(remoteData); // 加载远程数据
|
||||
};
|
||||
main();
|
||||
</script>
|
||||
@ -125,58 +118,23 @@ order: 1
|
||||
|
||||
运行后,我们得到了下图结果:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*KzQaSZKIsQoAAAAAAAAAAABkARQnAQ' width=550 height=350 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*WmGnQKAbmtsAAAAAAAAAAAAADmJ7AQ/original' width=550 alt='img' />
|
||||
|
||||
乍看之下,图像有点奇怪,实际上数据已经正确的加载了进来。由于数据量比较大,节点和边都非常的多,显得内容比较杂乱。另外由于画布大小的限制,实际的图被画布截断了,目前只能看见部分内容,这个问题后文会继续解决。
|
||||
|
||||
请看下面摘取自 tutorial-data.json 的部分数据,我们发现数据中节点定义了位置信息 `x` 与 `y`,并且很多节点的 `x` 和 `y` 不在图的宽高(`width: 800, height: 600`)范围内:
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{ "id": "0", "label": "n0", "class": "c0", "x": 1000, "y": -100 },
|
||||
{ "id": "1", "label": "n1", "class": "c0", "x": 300, "y": -10 }
|
||||
//...
|
||||
],
|
||||
"edges": [
|
||||
//...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
由于 G6 在读取数据时,发现了数据中带有位置信息(`x` 和 `y`),就会按照该位置信息进行绘制,这是为了满足按照原始数据绘制的需求设计的。但为优化超出屏幕到问题,G6 提供了图的两个相关配置项:
|
||||
|
||||
- `fitView`:设置是否将图适配到画布中;
|
||||
- `fitViewPadding`:画布上四周的留白宽度。
|
||||
|
||||
我们将实例化图的代码更改为如下形式:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ...
|
||||
fitView: true,
|
||||
fitViewPadding: [20, 40, 50, 20],
|
||||
});
|
||||
```
|
||||
|
||||
上述代码将会生成如下图: <img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*wAXzRJaNTXUAAAAAAAAAAABkARQnAQ' width=850 alt='img' />
|
||||
|
||||
可以看到,图像已经可以自动适配画布的大小,完整的显示了出来。
|
||||
乍看之下,图像有点奇怪,实际上数据已经正确的加载了进来。由于数据量比较大,节点和边都非常的多,默认使用了网格布局看不出节点的连接关系。接下来,我们将使用更多图的配置项使数据更清晰好看。
|
||||
|
||||
## 常用配置
|
||||
|
||||
本文使用到的配置以及后续 Tutorial 将会使用到到常用配置如下:
|
||||
|
||||
| 配置项 | 类型 | 选项 / 示例 | 默认 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| fitView | Boolean | true / false | false | 是否将图适配到画布大小,可以防止超出画布或留白太多。 |
|
||||
| fitViewPadding | Number / Array | 20 / [ 20, 40, 50, 20 ] | 0 | 画布上的四周留白宽度。 |
|
||||
| animate | Boolean | true / false | false | 是否启用图的动画。 |
|
||||
| modes | Object | {<br /> default: [ 'drag-node', 'drag-canvas' ]<br />} | null | 图上行为模式的集合。由于比较复杂,按需参见:[G6 中的 Mode](/zh/docs/manual/middle/states/mode) 教程。 |
|
||||
| defaultNode | Object | {<br /> type: 'circle',<br /> color: '#000',<br /> style: {<br /> ......<br /> }<br />} | null | 节点默认的属性,包括节点的一般属性和样式属性(style)。 |
|
||||
| defaultEdge | Object | {<br /> type: 'polyline',<br /> color: '#000',<br /> style: {<br /> ......<br /> }<br />} | null | 边默认的属性,包括边的一般属性和样式属性(style)。 |
|
||||
| nodeStateStyles | Object | {<br /> hover: {<br /> ......<br /> },<br /> select: {<br /> ......<br /> }<br />} | null | 节点在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。 |
|
||||
| edgeStateStyles | Object | {<br /> hover: {<br /> ......<br /> },<br /> select: {<br /> ......<br /> }<br />} | null | 边在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。 |
|
||||
| 配置项 | 类型 | 选项 / 示例 | 默认 | 说明 |
|
||||
| ---------- | ----------------- | -------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------ |
|
||||
| transforms | Array | ['transform-v4-data', { type: 'map-node-size', field: 'value' }] | null | 数据处理器。在原始用户数据进入 Graph 后将依次执行 transform 中的处理器,得到处理后的数据。 |
|
||||
| modes | Object | {<br /> default: [ 'drag-node', 'drag-canvas' ]<br />} | null | 图上行为模式的集合。 |
|
||||
| node | Object / Function | {<br /> type: 'circle',<br /> keyShape: {<br /> ......<br /> }<br />} | null | 节点全局的属性映射器,包括一般属性和样式属性(style)。v5 支持了函数映射。 |
|
||||
| edge | Object / Function | {<br /> type: 'polyline',<br /> keyShape: {<br /> ......<br /> }<br />} | null | 边全局的属性映射器,包括一般属性和样式属性(style)。v5 支持了函数映射。 |
|
||||
| nodeState | Object | {<br /> hover: {<br /> ......<br /> },<br /> select: {<br /> ......<br /> }<br />} | null | 节点在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。 |
|
||||
| edgeState | Object | {<br /> hover: {<br /> ......<br /> },<br /> select: {<br /> ......<br /> }<br />} | null | 边在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。 |
|
||||
| plugins | Array | ['minimap', { type: 'tooltip', itemTypes: ['node'] }] | null | 插件 |
|
||||
|
||||
## 完整代码
|
||||
|
||||
@ -190,26 +148,21 @@ const graph = new G6.Graph({
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<div id="container"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
<script>
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
container: 'container',
|
||||
width: 1000,
|
||||
height: 600,
|
||||
fitView: true,
|
||||
fitViewPadding: [20, 40, 50, 20],
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
graph.read(remoteData);
|
||||
};
|
||||
main();
|
||||
</script>
|
||||
@ -217,4 +170,4 @@ const graph = new G6.Graph({
|
||||
</html>
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️ 注意:</strong></span><br />若需更换数据,请替换 `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` 为新的数据文件地址。
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️ 注意:</strong></span><br />若需更换数据,请替换 `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` 为新的数据文件地址。
|
||||
|
@ -1,98 +1,74 @@
|
||||
---
|
||||
title: Utilizing Layout
|
||||
title: 使用图布局 Layout
|
||||
order: 3
|
||||
---
|
||||
|
||||
When there is no node position information in the data, or the location information does not meet the requirements, layouts in G6 will help you to arrange the nodes. There are 9 layouts for general graph and 4 layouts for tree graph in G6:
|
||||
When there is no node position information in the data, or when the position information in the data does not meet the requirements, some layout algorithms need to be used to layout the graph. G6 provides 9 general graph layouts and 4 tree graph layouts. In v4, they need to be used separately in graph data and tree data structures. In v5, tree and graph layouts are merged, and now both tree and graph can use the following layout algorithms:
|
||||
|
||||
<br />**Layouts for General Graph:**
|
||||
<br />
|
||||
**General graph layouts:**
|
||||
|
||||
- Random Layout: Randomizes the node positions;
|
||||
- **Force Layout: Classical force-directed layout algorithm:**
|
||||
- Random Layout: random layout;
|
||||
- **Force Layout**: classic force-directed layout:
|
||||
|
||||
> In force-directed layout, items are simulated as physical particals with attractive forces and repulsive forces. Lead by the forces, the nodes will move to appropriate positions to balance the forces. It is suitable for describing the relationships between objects, e.g. relationships between person, computer networks.
|
||||
> Force-directed layout: In a layout network, particles have attractive and repulsive forces between them. From the initial random and unordered layout, it gradually evolves to a balanced and stable layout, which is called force-directed layout. It is suitable for describing relationships between things, such as interpersonal relationships, computer network relationships, etc.
|
||||
|
||||
- Circular Layout: Arranges the nodes on a circle;
|
||||
- Radial Layout: Arranges the nodes radially;
|
||||
- MDS Layout: Multidimensional scaling;
|
||||
- Fruchterman Layout: A kind of force-directed layout;
|
||||
- Dagre Layout: Hierarchical layout;
|
||||
- Concentric Layout: Arranges the nodes on concentrics, while the more important (measure with degree by default), the more center the node will be;
|
||||
- Grid Layout: Arranges the nodes on the grid according with order (data order by default).
|
||||
- Circular Layout;
|
||||
- Radial Layout;
|
||||
- MDS Layout: dimensionality reduction algorithm layout for high-dimensional data;
|
||||
- Fruchterman Layout: a type of force-directed layout;
|
||||
- Dagre Layout: hierarchical layout;
|
||||
- Concentric Layout: placing important (default measured by degree) nodes in the layout center;
|
||||
- Grid Layout: arranging nodes in order (default is data order).
|
||||
|
||||
**Layouts for TreeGraph:**
|
||||
**Tree graph layouts:**
|
||||
|
||||
- Dendrogram Layout;
|
||||
- CompactBox Layout;
|
||||
- Mindmap Layout;
|
||||
- Indented Layout.
|
||||
- Dendrogram Layout: tree layout (leaf node layout aligned to the same layer);
|
||||
- CompactBox Layout: compact tree layout;
|
||||
- Mindmap Layout: mind map layout;
|
||||
- Indented Layout: indented layout.
|
||||
F
|
||||
or detailed introductions and configuration of these layouts, please refer to the [Layout API](https://g6-next.antv.antgroup.com/en/apis/interfaces/layout/force-layout-options). In this tutorial, we use the Force Layout.
|
||||
|
||||
For more information about each layout algorithm, please refer to [Graph Layout API](/en/docs/api/graphLayout/guide) or [TreeGraph Layout API](/en/docs/api/treeGraphLayout/guide). We will utilize Force Layout in the tutorial.
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*qnUwSZVjYOMAAAAAAAAAAABkARQnAQ' width=550 alt='img'/>
|
||||
|
||||
## Turnoff the fitView
|
||||
|
||||
We used `fitView` to fit the graph to the canvas in the previous Tutorial. From now on, we turn it off by note the line of code below to make further improvements.
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ...
|
||||
// fitView: true,
|
||||
// fitViewPadding: [ 20, 40, 50, 20 ]
|
||||
});
|
||||
```
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*lJdeTI0qQa8AAAAAAAAAAAAADmJ7AQ/original' width=550 alt='img' />
|
||||
|
||||
## Default Layout
|
||||
|
||||
When the `layout` is not assigned to graph instance:
|
||||
When instantiating the graph without configuring the layout:
|
||||
|
||||
- If there is position information with `x` and `y` in node data, render with these information;
|
||||
- If there is no position information in node data, arrange the nodes with Random Layout by default.
|
||||
- If the nodes in the data have position information (`x` and `y`), the graph will be drawn according to the position information in the data.
|
||||
|
||||
## Configure the Layout
|
||||
- If the nodes in the data do not have position information, the default Grid Layout will be used.
|
||||
|
||||
It is very simple to configure a layout for a graph in G6. Just assign `layout` to the graph when instantiating. The following code configures the layout with `type: 'force'`, which is the classical force-directed layout algorithm. And set `preventOverlap: true` to avoid node overlappings. More configurations are described in: [Graph Layout API](/en/docs/api/graphLayout/guide) or [TreeGraph Layout API](/en/docs/api/treeGraphLayout/guide).
|
||||
## Configuring the Layout
|
||||
|
||||
Configuring the layout in G6 is very simple. When instantiating the graph, just add the layout configuration. The code below sets the layout method to `type: 'force'`, which is the force-directed graph layout. At the same time,` animated: true` is enabled to render the graph in real-time during the force calculation process, allowing users to observe the animation effects produced by the interaction between nodes. The parameter `preventOverlap: true` is set to prevent node overlap. For more configuration options of the force-directed layout, please refer to the [Layout API](https://g6-next.antv.antgroup.com/en/apis/interfaces/layout/force-layout-options).
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
... // Other configurations
|
||||
layout: { // Object, layout configuration. random by default
|
||||
type: 'force', // Force layout
|
||||
preventOverlap: true, // Prevent node overlappings
|
||||
// nodeSize: 30 // The size of nodes for collide detection. Since we have assigned sizes for each node to their data in last chapter, the nodeSize here is not required any more.
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The result:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*w4ZfRJW3b5YAAAAAAAAAAABkARQnAQ' width=350 alt='img' />
|
||||
|
||||
The layout balances the forces by moving the nodes. But the nodes are too crowded to show the label clearly now. `linkDistance` in the configuration of force layout can be used to scale the edge length to keep a distance between two adjacent nodes:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ...
|
||||
const graph = new Graph({
|
||||
// ... // Other configurations
|
||||
// Object, optional, the layout method and its configuration, defaulting to the grid layout.
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
linkDistance: 100, // The link distance is 100
|
||||
type: 'force', // Specify the force-directed layout
|
||||
preventOverlap: true, // Prevent node overlap
|
||||
linkDistance: 50, // Ideal length of edges
|
||||
// nodeSize: 30 // Node size, used for collision detection in the algorithm to prevent node overlap. By default, the node size in the data will be used.
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The result:
|
||||
The result is as follows:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*AXrdQIm3oCIAAAAAAAAAAABkARQnAQ' width=350 alt='img' />
|
||||
<br />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*lJdeTI0qQa8AAAAAAAAAAAAADmJ7AQ/original' width=350 alt='img' />
|
||||
|
||||
> Transformation between different layouts and configurations are described in: [Layout Transformation](/en/docs/manual/middle/layout/layout-mechanism).
|
||||
> Different layouts and different parameters of the same layout can be dynamically switched and transitioned. For more information, please refer to: [Layout Switching](https://g6-next.antv.antgroup.com/en/examples/net/layoutMechanism/#layoutTranslate).
|
||||
|
||||
**Tips:** <br />The layout algorithm will be executed in `graph.render()`.
|
||||
Note: If there is data in the graph configuration, the layout calculation will be performed after instantiating the graph. If the graph.read(data) API is used to read the data, the layout will be calculated when it is called.
|
||||
|
||||
## Complete Code
|
||||
|
||||
Here is the complete code:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@ -101,77 +77,167 @@ The result:
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<div id="container"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
<script>
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 800,
|
||||
height: 600,
|
||||
defaultNode: {
|
||||
size: 30,
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#fff',
|
||||
const { Graph: GraphBase, extend, Extensions } = G6;
|
||||
|
||||
// Custom data processor - degree calculation
|
||||
const degreeCalculator = (data, options, userGraphCore) => {
|
||||
const { edges, nodes } = data;
|
||||
const degreeMap = new Map();
|
||||
edges.forEach(({ source, target }) => {
|
||||
degreeMap.set(source, (degreeMap.get(source) || 0) + 1);
|
||||
degreeMap.set(target, (degreeMap.get(target) || 0) + 1);
|
||||
});
|
||||
nodes.forEach((node) => {
|
||||
node.data.degree = degreeMap.get(node.id) || 0;
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
// Custom data processor - node clustering
|
||||
const clusteringNodes = (data, options = {}, userGraphCore) => {
|
||||
if (!Algorithm?.labelPropagation) return;
|
||||
const clusteredData = Algorithm.louvain(data, false);
|
||||
const clusterMap = new Map();
|
||||
clusteredData.clusters.forEach((cluster, i) => {
|
||||
cluster.nodes.forEach((node) => {
|
||||
clusterMap.set(node.id, `c${i}`);
|
||||
});
|
||||
});
|
||||
data.nodes.forEach((node) => {
|
||||
node.data.cluster = clusterMap.get(node.id);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
const Graph = extend(BaseGraph, {
|
||||
transforms: {
|
||||
'degree-calculator': degreeCalculator,
|
||||
'node-clustering': clusteringNodes,
|
||||
},
|
||||
nodes: {
|
||||
'triangle-node': Extensions.TriangleNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
transforms: [
|
||||
'transform-v4-data',
|
||||
'degree-calculator',
|
||||
'node-clustering',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
field: 'degree',
|
||||
range: [16, 60],
|
||||
},
|
||||
],
|
||||
layout: {
|
||||
type: 'force',
|
||||
animated: true,
|
||||
linkDistance: 50,
|
||||
},
|
||||
theme: {
|
||||
type: 'spec',
|
||||
base: 'light',
|
||||
specification: {
|
||||
node: {
|
||||
dataTypeField: 'cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
node: (model) => {
|
||||
const { id, data } = model;
|
||||
let type = 'circle-node';
|
||||
if (data.degree === 2) type = 'rect-node';
|
||||
else if (data.degree === 1) type = 'triangle-node';
|
||||
|
||||
const badgeShapes = {
|
||||
fontSize: 12,
|
||||
lod: 0,
|
||||
};
|
||||
|
||||
if (data.degree > 10) {
|
||||
badgeShapes[0] = {
|
||||
color: '#F86254',
|
||||
text: 'Important',
|
||||
position: 'rightTop',
|
||||
};
|
||||
}
|
||||
if (data.degree > 5) {
|
||||
badgeShapes[1] = {
|
||||
text: 'A',
|
||||
textAlign: 'center',
|
||||
color: '#EDB74B',
|
||||
position: 'right',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
type,
|
||||
labelShape: {
|
||||
position: 'bottom',
|
||||
text: id,
|
||||
},
|
||||
labelBackgroundShape: {},
|
||||
iconShape:
|
||||
data.degree <= 2
|
||||
? undefined
|
||||
: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
fill: '#fff',
|
||||
lod: 0,
|
||||
fontSize: data.keyShape.r - 4,
|
||||
},
|
||||
badgeShapes,
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
edge: {
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
type: 'force', // Force layout
|
||||
linkDistance: 100, // The link distance is 100
|
||||
preventOverlap: true, // Prevent node overlappings
|
||||
},
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
|
||||
const nodes = remoteData.nodes;
|
||||
const edges = remoteData.edges;
|
||||
nodes.forEach((node) => {
|
||||
if (!node.style) {
|
||||
node.style = {};
|
||||
}
|
||||
node.style.lineWidth = 1;
|
||||
node.style.stroke = '#666';
|
||||
node.style.fill = 'steelblue';
|
||||
switch (node.class) {
|
||||
case 'c0': {
|
||||
node.type = 'circle';
|
||||
break;
|
||||
}
|
||||
case 'c1': {
|
||||
node.type = 'rect';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
case 'c2': {
|
||||
node.type = 'ellipse';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight;
|
||||
edge.style.opacity = 0.6;
|
||||
edge.style.stroke = 'grey';
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
graph.read(remoteData);
|
||||
};
|
||||
main();
|
||||
</script>
|
||||
@ -179,4 +245,4 @@ The result:
|
||||
</html>
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️Attention:</strong></span> <br />Replace the url `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` to change the data into yours.
|
||||
**⚠️ Note:** <br /> If you need to replace the data, please replace `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` with the new data file address.
|
||||
|
@ -3,7 +3,9 @@ title: 使用图布局 Layout
|
||||
order: 3
|
||||
---
|
||||
|
||||
当数据中没有节点位置信息,或者数据中的位置信息不满足需求时,需要借助一些布局算法对图进行布局。G6 提供了 9 种一般图的布局和 4 种树图的布局:<br />**一般图:**
|
||||
当数据中没有节点位置信息,或者数据中的位置信息不满足需求时,需要借助一些布局算法对图进行布局。G6 提供了 9 种一般图的布局和 4 种树图的布局。在 v4 中,它们需要分别使用的图结构数据和树图结构数据中。v5 将树图和图进行了融合,现在不论是树图还是图,都可以使用如下布局算法:
|
||||
|
||||
<br />**一般图:**
|
||||
|
||||
- Random Layout:随机布局;
|
||||
- **Force Layout:经典力导向布局:**
|
||||
@ -25,70 +27,41 @@ order: 3
|
||||
- Mindmap Layout:脑图布局;
|
||||
- Indented Layout:缩进布局。
|
||||
|
||||
各种布局方法的具体介绍及其配置参见 [图布局 API](/zh/docs/api/graphLayout/guide) 或 [树图布局 API](/zh/docs/api/treeGraphLayout/guide)。本教程中,我们使用的是力导向布局 (Force Layout)。
|
||||
各种布局方法的具体介绍及其配置参见 [Layout API](https://g6-next.antv.antgroup.com/apis/interfaces/layout/force-layout-options)。本教程中,我们使用的是力导向布局 (Force Layout)。
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*qnUwSZVjYOMAAAAAAAAAAABkARQnAQ' width=550 alt='img' />
|
||||
|
||||
## 取消自动适配画布
|
||||
|
||||
我们在之前的教程里面,为了能够将超出画布的图适配到视野中,在实例化图时使用了 `fitView` 配置项。这节开始我们将会去掉这个特性。因为复杂的布局系统会打破适配的规则,反而会造成更多的困扰。让我们将相关的适配代码变为注释:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ...
|
||||
// fitView: true,
|
||||
// fitViewPadding: [ 20, 40, 50, 20 ]
|
||||
});
|
||||
```
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*lJdeTI0qQa8AAAAAAAAAAAAADmJ7AQ/original' width=550 alt='img' />
|
||||
|
||||
## 默认布局
|
||||
|
||||
当实例化图时没有配置布局时:
|
||||
|
||||
- 若数据中节点有位置信息(`x` 和 `y`),则按照数据的位置信息进行绘制;
|
||||
- 若数据中节点没有位置信息,则默认使用 Random Layout 进行布局。
|
||||
- 若数据中节点没有位置信息,则默认使用 Grid Layout 进行布局。
|
||||
|
||||
## 配置布局
|
||||
|
||||
G6 使用布局的方式非常简单,在图实例化的时候,加上 layout 配置即可。下面代码在实例化图时设置了布局方法为 `type: 'force'`,即经典力导向图布局。并设置了参数 `preventOverlap: true` ,表示希望节点不重叠。力导向布局的更多配置项参见:[Layout API](/zh/docs/api/graphLayout/force)。
|
||||
G6 使用布局的方式非常简单,在图实例化的时候,加上 layout 配置即可。下面代码在实例化图时设置了布局方法为 `type: 'force'`,即力导向图布局。同时开启了 `animated: true` 使得在力计算过程中实时渲染图,让用户可以观察到图上节点力相互作用产生的动画效果。并设置了参数 `preventOverlap: true` ,表示希望节点不重叠。力导向布局的更多配置项参见:[Layout API](https://g6-next.antv.antgroup.com/apis/interfaces/layout/force-layout-options)。
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
const graph = new Graph({
|
||||
// ... // 其他配置项
|
||||
// Object,可选,布局的方法及其配置项,默认为 grid 布局。
|
||||
layout: {
|
||||
// Object,可选,布局的方法及其配置项,默认为 random 布局。
|
||||
type: 'force', // 指定为力导向布局
|
||||
preventOverlap: true, // 防止节点重叠
|
||||
// nodeSize: 30 // 节点大小,用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中设置了每个节点的 size 属性,则不需要在此设置 nodeSize。
|
||||
linkDistance: 50, // 边的理想长度
|
||||
// nodeSize: 30 // 节点大小,用于算法中防止节点重叠时的碰撞检测。默认将使用数据中的节点大小。
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
结果如下:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*w4ZfRJW3b5YAAAAAAAAAAABkARQnAQ' width=350 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*lJdeTI0qQa8AAAAAAAAAAAAADmJ7AQ/original' width=350 alt='img' />
|
||||
|
||||
如图所示,节点按照力导向布局自动平衡。但是图中的节点过于拥挤,边上的文字信息被挤占,无法看清。我们希望布局计算边的距离可以更长一些。G6 的力导向布局提供了 `linkDistance` 属性用来指定布局的时候边的距离长度:
|
||||
> 不同布局之间、相同布局不同参数允许动态切换和过渡,具体参见:[布局切换](https://g6-next.antv.antgroup.com/examples/net/layoutMechanism/#layoutTranslate)。
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
// ...
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
linkDistance: 100, // 指定边距离为100
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
结果如下:
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*AXrdQIm3oCIAAAAAAAAAAABkARQnAQ' width=350 alt='img' />
|
||||
<br />
|
||||
|
||||
> 不同布局之间、相同布局不同参数允许动态切换和过渡,具体参见:[布局切换](/zh/docs/manual/middle/layout/layout-mechanism)。
|
||||
|
||||
提示:布局将在调用 `graph.render()` 时执行计算。
|
||||
提示:若图配置中有 data,则在实例化图后进行布局计算。若使用 `graph.read(data)` API 进行数据读取,则将在调用时执行计算。
|
||||
|
||||
## 完整代码
|
||||
|
||||
@ -102,82 +75,173 @@ const graph = new G6.Graph({
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mountNode"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<div id="container"></div>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
<script>
|
||||
const graph = new G6.Graph({
|
||||
container: 'mountNode',
|
||||
width: 800,
|
||||
height: 600,
|
||||
defaultNode: {
|
||||
size: 30,
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#fff',
|
||||
const { Graph: GraphBase, extend, Extensions } = G6;
|
||||
|
||||
// 自定义数据处理器 - 度数计算
|
||||
const degreeCalculator = (data, options, userGraphCore) => {
|
||||
const { edges, nodes } = data;
|
||||
const degreeMap = new Map();
|
||||
edges.forEach(({ source, target }) => {
|
||||
degreeMap.set(source, (degreeMap.get(source) || 0) + 1);
|
||||
degreeMap.set(target, (degreeMap.get(target) || 0) + 1);
|
||||
});
|
||||
nodes.forEach((node) => {
|
||||
node.data.degree = degreeMap.get(node.id) || 0;
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
// 自定义数据处理器 - 节点聚类
|
||||
const clusteringNodes = (data, options = {}, userGraphCore) => {
|
||||
if (!Algorithm?.labelPropagation) return;
|
||||
const clusteredData = Algorithm.louvain(data, false);
|
||||
const clusterMap = new Map();
|
||||
clusteredData.clusters.forEach((cluster, i) => {
|
||||
cluster.nodes.forEach((node) => {
|
||||
clusterMap.set(node.id, `c${i}`);
|
||||
});
|
||||
});
|
||||
data.nodes.forEach((node) => {
|
||||
node.data.cluster = clusterMap.get(node.id);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
const Graph = extend(BaseGraph, {
|
||||
transforms: {
|
||||
'degree-calculator': degreeCalculator,
|
||||
'node-clustering': clusteringNodes,
|
||||
},
|
||||
nodes: {
|
||||
'triangle-node': Extensions.TriangleNode,
|
||||
},
|
||||
});
|
||||
|
||||
const graph = new Graph({
|
||||
container: 'container',
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
transforms: [
|
||||
'transform-v4-data',
|
||||
'degree-calculator',
|
||||
'node-clustering',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
field: 'degree',
|
||||
range: [16, 60],
|
||||
},
|
||||
],
|
||||
layout: {
|
||||
type: 'force',
|
||||
animated: true,
|
||||
linkDistance: 50,
|
||||
},
|
||||
theme: {
|
||||
type: 'spec',
|
||||
base: 'light',
|
||||
specification: {
|
||||
node: {
|
||||
dataTypeField: 'cluster',
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
node: (model) => {
|
||||
const { id, data } = model;
|
||||
let type = 'circle-node';
|
||||
if (data.degree === 2) type = 'rect-node';
|
||||
else if (data.degree === 1) type = 'triangle-node';
|
||||
|
||||
const badgeShapes = {
|
||||
fontSize: 12,
|
||||
lod: 0,
|
||||
};
|
||||
|
||||
if (data.degree > 10) {
|
||||
badgeShapes[0] = {
|
||||
color: '#F86254',
|
||||
text: 'Important',
|
||||
position: 'rightTop',
|
||||
};
|
||||
}
|
||||
if (data.degree > 5) {
|
||||
badgeShapes[1] = {
|
||||
text: 'A',
|
||||
textAlign: 'center',
|
||||
color: '#EDB74B',
|
||||
position: 'right',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
type,
|
||||
labelShape: {
|
||||
position: 'bottom',
|
||||
text: id,
|
||||
},
|
||||
labelBackgroundShape: {},
|
||||
iconShape:
|
||||
data.degree <= 2
|
||||
? undefined
|
||||
: {
|
||||
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
|
||||
fill: '#fff',
|
||||
lod: 0,
|
||||
fontSize: data.keyShape.r - 4,
|
||||
},
|
||||
badgeShapes,
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
edge: {
|
||||
animates: {
|
||||
update: [
|
||||
{
|
||||
fields: ['opacity'],
|
||||
shapeId: 'haloShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
{
|
||||
fields: ['lineWidth'],
|
||||
shapeId: 'keyShape',
|
||||
states: ['selected', 'active'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
type: 'force', // 设置布局算法为 force
|
||||
linkDistance: 100, // 设置边长为 100
|
||||
preventOverlap: true, // 设置防止重叠
|
||||
},
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
|
||||
'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json',
|
||||
);
|
||||
const remoteData = await response.json();
|
||||
|
||||
const nodes = remoteData.nodes;
|
||||
const edges = remoteData.edges;
|
||||
nodes.forEach((node) => {
|
||||
if (!node.style) {
|
||||
node.style = {};
|
||||
}
|
||||
node.style.lineWidth = 1;
|
||||
node.style.stroke = '#666';
|
||||
node.style.fill = 'steelblue';
|
||||
switch (node.class) {
|
||||
case 'c0': {
|
||||
node.type = 'circle';
|
||||
break;
|
||||
}
|
||||
case 'c1': {
|
||||
node.type = 'rect';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
case 'c2': {
|
||||
node.type = 'ellipse';
|
||||
node.size = [35, 20];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
edges.forEach((edge) => {
|
||||
if (!edge.style) {
|
||||
edge.style = {};
|
||||
}
|
||||
edge.style.lineWidth = edge.weight;
|
||||
edge.style.opacity = 0.6;
|
||||
edge.style.stroke = 'grey';
|
||||
});
|
||||
|
||||
graph.data(remoteData);
|
||||
graph.render();
|
||||
graph.read(remoteData);
|
||||
};
|
||||
|
||||
main();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️ 注意:</strong></span><br /> 若需更换数据,请替换 `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` 为新的数据文件地址。
|
||||
**⚠️ 注意:** <br /> 若需更换数据,请替换 `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` 为新的数据文件地址。
|
||||
|
@ -3,197 +3,102 @@ title: Plugins and Tools
|
||||
order: 5
|
||||
---
|
||||
|
||||
To assist user to exploration a graph, G6 provides some tools, including plugins and interaction tools.
|
||||
This article will add the Minimap plugin and tooltip node prompt box to the **Tutorial Example**.
|
||||
When using plugins, there are three steps:
|
||||
|
||||
Now, we are going to add minimap, grid, node tooltip, and edge tooltip to **Tutorial Demo**.
|
||||
- Step 1: Import the plugin;
|
||||
- Step 2: Register the plugin;
|
||||
- Step 3: Configure the plugin instance on the graph when instantiating the graph.
|
||||
|
||||
## Plugin
|
||||
## Minimap
|
||||
|
||||
Apply plugins with three steps:<br /> Step 1: Import the plugin;<br /> Step 2: Instantiate the plugin;<br /> Step 3: Configure plugin onto the instance of Graph.
|
||||
The Minimap is a common tool used for quick preview and exploration of graphs. It can serve as a navigation aid for users to explore large-scale graphs.
|
||||
|
||||
### Minimap
|
||||
Now, let's configure a Minimap for the **Tutorial Example**:
|
||||
|
||||
Minimap is a tool for quick preview and exploration on large graph.
|
||||
**Expected Result**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*kGesRLgy1CsAAAAAAAAAAABkARQnAQ' width=520 alt='img'/>
|
||||
|
||||
Now, we are goint to configure a minimap to **Tutorial Demo**.
|
||||
|
||||
**Expected Effect**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*SI8ZSpcqecgAAAAAAAAAAABkARQnAQ' width=350 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*9VQjTp0Ipi8AAAAAAAAAAAAADmJ7AQ/original' width=350 alt='img' />
|
||||
|
||||
**Usage**
|
||||
|
||||
In G6, Minimap is a plugin. You only need to instantiate it and configure the minimap onto the instance of Graph:
|
||||
The Minimap is one of the plugins provided by G6, but it is not registered in advance. It needs to be imported from Extensions and registered before being configured on the graph:
|
||||
|
||||
```javascript
|
||||
// Instantiate the Minimap
|
||||
const minimap = new G6.Minimap({
|
||||
size: [100, 100],
|
||||
className: 'minimap',
|
||||
type: 'delegate',
|
||||
const { Graph as GraphBase, extend, Extensions } = G6;
|
||||
const ExtGraph = extend(GraphBase, {
|
||||
// ... Other extension registrations
|
||||
// Plugin registration
|
||||
plugins: {
|
||||
minimap: Extensions.Minimap,
|
||||
}
|
||||
});
|
||||
|
||||
// Instantiate the Graph
|
||||
const graph = new G6.Graph({
|
||||
// ... // Other configurations
|
||||
plugins: [minimap], // Configure minimap to the graph
|
||||
});
|
||||
```
|
||||
|
||||
### Image Minimap
|
||||
|
||||
The theory of the [Minimap](#minimap) is copy the graphics from the main graph onto the canvas of the minimap, which will lead to double rendering cost. To alleviate this problem, G6 provides another Image Minimap which is drawn by one `<img />` instead of canvas. But you have to provide the `graphImg` which is the url or base64 string of the main graph's screenshot image, and the image is controlled by yourself totally, which means you might need to update the image by calling `minimap.updateGraphImg` manually when the content of the main graph is changed.
|
||||
|
||||
**预期效果**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*v1svQLkEPrUAAAAAAAAAAABkARQnAQ' width=300 alt='img'/>
|
||||
|
||||
**Usage**
|
||||
|
||||
`graphImg` is required when instantiating the Image Minimap. Update the `graphImg` for the minimap. We recommand you to update the graphImg when the main graph is updated.
|
||||
|
||||
```javascript
|
||||
// Instantiate the Image Minimap plugin
|
||||
const imageMinimap = new G6.ImageMinimap({
|
||||
width: 200,
|
||||
graphImg: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*eD7nT6tmYgAAAAAAAAAAAABkARQnAQ'
|
||||
});
|
||||
const graph = new G6.Graph({
|
||||
//... Other configurations
|
||||
plugins: [imageMinimap], // Configure imageMinimap
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render()
|
||||
|
||||
... // Some operations which update the main graph
|
||||
imageMinimap.updateGraphImg(img); // Update the minimap's image (generated by yourself)
|
||||
|
||||
```
|
||||
|
||||
### Grid
|
||||
|
||||
Grid helps to align the node while user drags it.
|
||||
|
||||
**Expected Effect**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*y8u6Rrc78uIAAAAAAAAAAABkARQnAQ' width=300 alt='img' />
|
||||
|
||||
**Usage** Configure it onto the graph:
|
||||
|
||||
```javascript
|
||||
// const minimap = ...
|
||||
|
||||
// Instantiate grid
|
||||
const grid = new G6.Grid();
|
||||
|
||||
// Instantiate the Graph
|
||||
const graph = new G6.Graph({
|
||||
// ... // Other configurations
|
||||
plugins: [minimap, grid], // Configure grid onto the graph
|
||||
});
|
||||
```
|
||||
|
||||
## Interaction Tool
|
||||
|
||||
Interaction tools assist user interact a graph. Two steps are required: <br /> Step 1: Configure `modes` when instantiating a graph; <br /> Step 2: Define the styles for the tools.
|
||||
|
||||
### Tooltip for Node
|
||||
|
||||
Node tooltip shows the detail information when mouse enters a node. More configurations are in [Built-in tooltip](/en/docs/manual/middle/states/defaultBehavior#tooltip).
|
||||
|
||||
**Expected Effect**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*v1svQLkEPrUAAAAAAAAAAABkARQnAQ' width=300 alt='img' />
|
||||
|
||||
**Usage**
|
||||
|
||||
Configure `'tooltip'` to `modes` when instantiating the Graph:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
modes: {
|
||||
default: [
|
||||
// ...
|
||||
{
|
||||
type: 'tooltip', // Tooltip
|
||||
formatText(model) {
|
||||
// The content of tooltip
|
||||
const text = 'label: ' + model.label + '<br/> class: ' + model.class;
|
||||
return text;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Actually, tooltip is a floating `<div>` tag of HTML. Thus, you can define the CSS style for it in `<style>` tag:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Tutorial Demo</title>
|
||||
|
||||
<style>
|
||||
/* The style of the tooltip */
|
||||
.g6-tooltip {
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #545454;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
padding: 10px 8px;
|
||||
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
|
||||
// Instantiate the graph, note that the extended Graph is used here
|
||||
const graph = new ExtGraph({
|
||||
// ... Other configurations
|
||||
plugins: [
|
||||
// If using default configurations, you can simply write a string 'minimap'
|
||||
{
|
||||
type: 'minimap',
|
||||
// ... Other configurations
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
```
|
||||
|
||||
### Tooltip for Edge
|
||||
|
||||
Edge tooltip shows the detail information when mouse enters a edge. More configurations are in [Built-in edge-tooltip](/en/docs/manual/middle/states/defaultBehavior#edge-tooltip).
|
||||
|
||||
**Expected Effect**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Uk10SYFNNi8AAAAAAAAAAABkARQnAQ' width=300 alt='img' />
|
||||
|
||||
**Usage**
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
modes: {
|
||||
default: [
|
||||
// ...
|
||||
{
|
||||
type: 'tooltip', // Node tooltip
|
||||
// ...
|
||||
},
|
||||
{
|
||||
type: 'edge-tooltip', // Edge tooltip
|
||||
formatText(model) {
|
||||
// The content of the edge tooltip
|
||||
const text =
|
||||
'source: ' +
|
||||
model.source +
|
||||
'<br/> target: ' +
|
||||
model.target +
|
||||
'<br/> weight: ' +
|
||||
model.weight;
|
||||
return text;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
The same as node tooltip, edge-tooltip is a floating `<div>` tag in HTML. Thus, you can define the CSS style for it in `<style>` tag:
|
||||
## Tooltip
|
||||
|
||||
The tooltip node prompt box can be used to display detailed information about nodes. When the mouse hovers over a node, a floating layer is displayed to provide detailed information about the node. For more configurations, see [Tooltip Plugin](https://g6-next.antv.antgroup.com/en/apis/interfaces/plugins/tooltip-config).
|
||||
|
||||
**Expected Result**
|
||||
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*9VQjTp0Ipi8AAAAAAAAAAAAADmJ7AQ/original' width=300 alt='img' />
|
||||
|
||||
**Usage**
|
||||
|
||||
The Tooltip is one of the plugins provided by G6, but it is not registered in advance. It needs to be imported from Extensions and registered before being configured on the graph:
|
||||
|
||||
```javascript
|
||||
const { Graph as GraphBase, extend, Extensions } = G6;
|
||||
const ExtGraph = extend(GraphBase, {
|
||||
// ... Other extension registrations
|
||||
// Plugin registration
|
||||
plugins: {
|
||||
minimap: Extensions.Minimap,
|
||||
tooltip: Extensions.Tooltip,
|
||||
}
|
||||
});
|
||||
// Instantiate the graph, note that the extended Graph is used here
|
||||
const graph = new ExtGraph({
|
||||
// ... Other configurations
|
||||
plugins: [
|
||||
// If using default configurations, you can simply write a string 'minimap'
|
||||
'minimap',
|
||||
// If using default configurations, you can simply write a string 'tooltip'
|
||||
{
|
||||
type: "tooltip",
|
||||
key: "my-tooltip", // Unique identifier
|
||||
itemTypes: ["node"], // Only effective for nodes, can be configured as ['node', 'edge'] to make it effective for both nodes and edges
|
||||
getContent: (e) => { // Custom content
|
||||
const model = graph.getNodeData(e.itemId);
|
||||
return `ID: ${e.itemId}<br/>Degree: ${model.data.degree}`;
|
||||
}
|
||||
// ... Other configurations
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
## Complete Code
|
||||
|
||||
**Tutorial Demo** is done now. For complete code, see: [Code of Tutorial Demo](https://codepen.io/Yanyan-Wang/pen/mdbYZvZ).
|
||||
With this, the Tutorial Example is complete. The complete code can be found here: a href='https://codesandbox.io/s/g6-v5-tutorial-j67vnm?file=/index.js' target='\_blank'>Tutorial Example Code</a>.
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️Attention:</strong></span> <br />Replace the url `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` to change the data into yours.
|
||||
**⚠️ Note:** <br /> If you need to replace the data, please replace `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` with the new data file address.
|
||||
|
||||
<iframe src="https://codesandbox.io/embed/g6-v5-tutorial-j67vnm?fontsize=14&hidenavigation=1&theme=light"
|
||||
style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
|
||||
title="g6-v5-tutorial"
|
||||
allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
|
||||
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
||||
></iframe>
|
||||
|
@ -3,197 +3,102 @@ title: 插件与工具
|
||||
order: 5
|
||||
---
|
||||
|
||||
为辅助用户在图上探索,G6 提供了一些辅助工具,其中一部分是插件工具,另一部分是交互工具。
|
||||
本文将为 **Tutorial 案例** 添加缩略图插件、节点提示框。
|
||||
|
||||
本文将为 **Tutorial 案例** 添加缩略图插件、网格插件、节点提示框、边提示框。
|
||||
使用插件时,有三个步骤:<br /> Step 1: 引入插件;<br /> Step 2: 注册插件;<br /> Step 3: 在实例化图时将插件的实例配置到图上。
|
||||
|
||||
## 插件
|
||||
|
||||
使用插件时,有三个步骤:<br /> Step 1: 引入插件;<br /> Step 2: 实例化插件;<br /> Step 3: 在实例化图时将插件的实例配置到图上。
|
||||
|
||||
### Minimap
|
||||
## Minimap
|
||||
|
||||
缩略图 (Minimap) 是一种常见的用于快速预览和探索图的工具,可作为导航辅助用户探索大规模图。
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*kGesRLgy1CsAAAAAAAAAAABkARQnAQ' width=520 alt='img' />
|
||||
|
||||
现在,我们为 **Tutorial 案例** 配置一个 Minimap:
|
||||
|
||||
**预期效果**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*SI8ZSpcqecgAAAAAAAAAAABkARQnAQ' width=350 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*9VQjTp0Ipi8AAAAAAAAAAAAADmJ7AQ/original' width=350 alt='img' />
|
||||
|
||||
**使用方法**
|
||||
|
||||
Minimap 是 G6 的插件之一,引入 G6 后可以直接使用。实例化 Minimap 对象,并将其配置到图实例的插件列表里即可:
|
||||
Minimap 是 G6 的提供的插件之一,但未提前注册,需要从 Extensions 引入并注册后,再配置到图上:
|
||||
|
||||
```javascript
|
||||
// 实例化 minimap 插件
|
||||
const minimap = new G6.Minimap({
|
||||
size: [100, 100],
|
||||
className: 'minimap',
|
||||
type: 'delegate',
|
||||
cosnt { Graph as GraphBase, extend, Extensions } = G6;
|
||||
|
||||
const ExtGraph = extend(GraphBase, {
|
||||
// ... 其他扩展的注册
|
||||
// 插件注册
|
||||
plugins: {
|
||||
minimap: Extensions.Minimap,
|
||||
}
|
||||
});
|
||||
|
||||
// 实例化图
|
||||
const graph = new G6.Graph({
|
||||
// 实例化图,注意这里使用的是 extend 后的 Graph
|
||||
const graph = new Graph({
|
||||
// ... // 其他配置项
|
||||
plugins: [minimap], // 将 minimap 实例配置到图上
|
||||
});
|
||||
```
|
||||
|
||||
### Image Minimap
|
||||
|
||||
由于 [Minimap](#minimap) 的原理是将主画布内容复制到 minimap 的画布上,在大数据量下可能会造成双倍的绘制效率成本。为缓解该问题,Image Minimap 采用另一种机制,根据提供的图片地址或 base64 字符串 `graphImg` 绘制 `<img />` 代替 minimap 上的 canvas。该方法可以大大减轻两倍 canvas 绘制的压力。但 `graphImg` 完全交由 G6 的用户控制,需要注意主画布更新时需要使用 `updateGraphImg` 方法替换 `graphImg`。
|
||||
|
||||
**预期效果**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*v1svQLkEPrUAAAAAAAAAAABkARQnAQ' width=300 alt='img'/>
|
||||
|
||||
**使用方法**
|
||||
|
||||
实例化 Image Minimap 插件时,`graphImg` 是必要参数。建议在主画布更新时使用 `updateGraphImg(img)` 同步更新 minimap 图片,其中参数 `img` 是图片地址或 base64 文本。
|
||||
|
||||
```javascript
|
||||
// 实例化 Image Minimap 插件
|
||||
const imageMinimap = new G6.ImageMinimap({
|
||||
width: 200,
|
||||
graphImg: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*eD7nT6tmYgAAAAAAAAAAAABkARQnAQ'
|
||||
});
|
||||
const graph = new G6.Graph({
|
||||
//... 其他配置项
|
||||
plugins: [imageMinimap], // 配置 imageMinimap 插件
|
||||
});
|
||||
|
||||
graph.data(data);
|
||||
graph.render()
|
||||
|
||||
... // 一些主画布更新操作
|
||||
imageMinimap.updateGraphImg(img); // 使用新的图片(用户自己生成)替换 minimap 图片
|
||||
|
||||
```
|
||||
|
||||
### Grid 网格
|
||||
|
||||
网格可用于辅助用户在拖拽节点时对齐到网格。
|
||||
|
||||
**预期效果**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*y8u6Rrc78uIAAAAAAAAAAABkARQnAQ' width=300 alt='img' />
|
||||
|
||||
**使用方法** 实例化插件和配置插件到图上:
|
||||
|
||||
```javascript
|
||||
// const minimap = ...
|
||||
|
||||
// 实例化 grid 插件
|
||||
const grid = new G6.Grid();
|
||||
|
||||
// 实例化图
|
||||
const graph = new G6.Graph({
|
||||
// ... // 其他配置项
|
||||
plugins: [minimap, grid], // 将 grid 实例配置到图上
|
||||
});
|
||||
```
|
||||
|
||||
## 交互工具
|
||||
|
||||
交互工具是指配置到图上交互模式中的工具。使用交互工具时,有两个步骤:<br /> Step 1: 在实例化图时配置 `modes`;<br /> Step 2: 为交互工具定义样式。
|
||||
|
||||
### tooltip 节点提示框
|
||||
|
||||
节点提示框可以用在边的详细信息的展示。当鼠标滑过节点时,显示一个浮层告知节点的详细信息。更多配置参见 [内置交互 tooltip](/zh/docs/manual/middle/states/defaultBehavior#tooltip)。
|
||||
|
||||
**预期效果**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*v1svQLkEPrUAAAAAAAAAAABkARQnAQ' width=300 alt='img' />
|
||||
|
||||
**使用方法**
|
||||
|
||||
实例化图时配置 `'tooltip'` 到 `modes` 中:
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
modes: {
|
||||
default: [
|
||||
// ...
|
||||
{
|
||||
type: 'tooltip', // 提示框
|
||||
formatText(model) {
|
||||
// 提示框文本内容
|
||||
const text = 'label: ' + model.label + '<br/> class: ' + model.class;
|
||||
return text;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
由于 tooltip 实际上是一个悬浮的 `<div>` 标签,因此可在 HTML 的 `<style>` 标签或 CSS 中设置样式。下面展示在 HTML 中设置样式:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Tutorial Demo</title>
|
||||
|
||||
<style>
|
||||
/* 提示框的样式 */
|
||||
.g6-tooltip {
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #545454;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
padding: 10px 8px;
|
||||
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
|
||||
plugins: [
|
||||
// 若使用默认配置,可以只写一个字符串 'minimap'
|
||||
{
|
||||
type: 'minimap',
|
||||
// ... 其他配置项
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
```
|
||||
|
||||
### edge-tooltip 边提示框
|
||||
|
||||
边提示框可以用在边的详细信息的展示。当鼠标滑过边时,显示一个浮层告知边的详细信息。更多配置参见 [内置交互 edge-tooltip](/zh/docs/manual/middle/states/defaultBehavior#edge-tooltip)。
|
||||
|
||||
**预期效果**
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Uk10SYFNNi8AAAAAAAAAAABkARQnAQ' width=300 alt='img' />
|
||||
|
||||
**使用方法**
|
||||
|
||||
```javascript
|
||||
const graph = new G6.Graph({
|
||||
modes: {
|
||||
default: [
|
||||
// ...
|
||||
{
|
||||
type: 'tooltip', // 节点提示框
|
||||
// ...
|
||||
},
|
||||
{
|
||||
type: 'edge-tooltip', // 边提示框
|
||||
formatText(model) {
|
||||
// 边提示框文本内容
|
||||
const text =
|
||||
'source: ' +
|
||||
model.source +
|
||||
'<br/> target: ' +
|
||||
model.target +
|
||||
'<br/> weight: ' +
|
||||
model.weight;
|
||||
return text;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
与 tooltip 相同,edge-tooltip 是一个悬浮的 `<div>` 标签,可以使用与 tooltip 相同的方法设置其悬浮框的样式。
|
||||
## tooltip 节点提示框
|
||||
|
||||
节点提示框可以用在边的详细信息的展示。当鼠标滑过节点时,显示一个浮层告知节点的详细信息。更多配置参见 [Tooltip 插件](https://g6-next.antv.antgroup.com/apis/interfaces/plugins/tooltip-config)。
|
||||
|
||||
**预期效果**
|
||||
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*9VQjTp0Ipi8AAAAAAAAAAAAADmJ7AQ/original' width=300 alt='img' />
|
||||
|
||||
**使用方法**
|
||||
|
||||
Tooltip 是 G6 的提供的插件之一,但未提前注册,需要从 Extensions 引入并注册后,再配置到图上:
|
||||
|
||||
```javascript
|
||||
cosnt { Graph as GraphBase, extend, Extensions } = G6;
|
||||
|
||||
const ExtGraph = extend(GraphBase, {
|
||||
// ... 其他扩展的注册
|
||||
// 插件注册
|
||||
plugins: {
|
||||
minimap: Extensions.Minimap,
|
||||
tooltip: Extensions.Tooltip,
|
||||
}
|
||||
});
|
||||
|
||||
// 实例化图,注意这里使用的是 extend 后的 Graph
|
||||
const graph = new Graph({
|
||||
// ... // 其他配置项
|
||||
plugins: [
|
||||
// 若使用默认配置,可以只写一个字符串 'minimap'
|
||||
'minimap',,
|
||||
// 若使用默认配置,可以只写一个字符串 'tooltip'
|
||||
{
|
||||
type: "tooltip",
|
||||
key: "my-tooltip", // 唯一标志
|
||||
itemTypes: ["node"], // 只对节点生效,可以配置为 ['node', 'edge'] 使其同时对边生效
|
||||
getContent: (e) => { // 自定义内容
|
||||
const model = graph.getNodeData(e.itemId);
|
||||
return `ID: ${e.itemId}<br/>Degree: ${model.data.degree}`;
|
||||
}
|
||||
// ... 其他配置项
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## 完整代码
|
||||
|
||||
至此,**Tutorial 案例** 完成,完整代码见:<a href='https://codepen.io/Yanyan-Wang/pen/mdbYZvZ' target='_blank'>Tutorial 案例代码</a>。
|
||||
至此,**Tutorial 案例** 完成,完整代码见:<a href='https://codesandbox.io/s/g6-v5-tutorial-j67vnm?file=/index.js' target='_blank'>Tutorial 案例代码</a>。
|
||||
|
||||
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"><strong>⚠️ 注意:</strong></span><br /> 若需更换数据,请替换 `'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json'` 为新的数据文件地址。
|
||||
**⚠️ 注意:** <br /> 若需更换数据,请替换 `'https://raw.githubusercontent.com/antvis/G6/v5-demo-refine/packages/g6/tests/datasets/force-data.json'` 为新的数据文件地址。
|
||||
|
||||
<iframe src="https://codesandbox.io/embed/g6-v5-tutorial-j67vnm?fontsize=14&hidenavigation=1&theme=light"
|
||||
style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
|
||||
title="g6-v5-tutorial"
|
||||
allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
|
||||
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
||||
></iframe>
|
||||
|
@ -5,40 +5,43 @@ order: 0
|
||||
|
||||
## What is G6
|
||||
|
||||
[G6](https://github.com/antvis/g6) is a graph visualization engine, which provides a set of basic mechanisms, including rendering, layout, analysis, interaction, animation, and other auxiliary tools. G6 aims to simplify the complex relationships, and help people to obtain the insight of relational data.
|
||||
G6 is a graph visualization engine. It provides basic graph visualization capabilities such as drawing, layout, analysis, interaction, animation, etc. It aims to make relationships transparent and simple, allowing users to gain insights into relationship data.
|
||||
|
||||
## Introduction to The Tutorial
|
||||
## Introduction to the Tutorial
|
||||
|
||||
We will build a simple graph visualization during this tutorial. We call this demo **Tutorial Demo** in the following Tutorial. <a href='https://codepen.io/Yanyan-Wang/pen/mdbYZvZ' target='_blank'>Complete Code</a>.
|
||||
In this tutorial, we will create a simple graph visualization as shown in the following image, which we will refer to as the **Tutorial Example**, <a href='https://codepen.io/Yanyan-Wang/pen/mdbYZvZ' target='_blank'>Full Code</a>.
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*YlTVS54xV3EAAAAAAAAAAABkARQnAQ' width=500 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*9VQjTp0Ipi8AAAAAAAAAAAAADmJ7AQ/original' style="text-align: center;" width=500 alt='img' />
|
||||
|
||||
<div style="text-align: center;"><b>Tutorial Demo</b> result</div>
|
||||
<div style="text-align: center;"><b>Tutorial Example</b></div>
|
||||
|
||||
## Preface
|
||||
## Introduction
|
||||
|
||||
This tutorial introduces how to combine creating and rendering a graph, configuring items, layout, interaction, animation, and other tools to complete the final **Tutorial Demo**. The readers will learn the basic and key concepts of G6 in this tutorial.
|
||||
In this tutorial, we will complete the creation, rendering, element configuration, layout, interaction, animation, and tooling of a graph in the Tutorial Example. In this tutorial, readers will learn the basic and core functionalities of G6. After mastering the content of this tutorial, readers will have a preliminary understanding of G6 and lay a foundation for a deeper understanding.
|
||||
|
||||
There are 6 chapters in this tutorial:
|
||||
This tutorial will be divided into the following sections:
|
||||
|
||||
- Create & Render a Graph
|
||||
- Items & Their Configurations
|
||||
- Utilize Layout
|
||||
- Interaction Behaviors
|
||||
- Plugins & Tools
|
||||
- \*Animation (not Required)
|
||||
- Quick Start
|
||||
- Creating a Graph
|
||||
- Elements and their Configuration
|
||||
- Using Graph Layouts
|
||||
- Graph Interaction Behavior
|
||||
- Plugins and Tools
|
||||
- Animation
|
||||
|
||||
**Tips:** <br />This tutorial is designed for people who prefer to learn by doing. If you prefer learning concepts from the ground up, check out our [Key Concepts](/en/docs/manual/middle/graph). You might find this tutorial and the guide complementary to each other.
|
||||
`Note`
|
||||
<br />
|
||||
This tutorial is designed for readers who want to learn while doing. More tutorials for G6 5.0 are under development. For now, readers can refer to the [API Doc](https://g6-next.antv.antgroup.com/en/apis).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
It doesn't matter if you're not familiar with G6. But we’ll assume that you have some familiarity with HTML and JavaScript, but you should be able to follow along even if you’re coming from a different programming language. You might be tempted to skip if you already know the basics of G6.
|
||||
This tutorial demonstrates how to use G6 to create a complete graph visualization application. Before learning, we assume that readers have some knowledge of HTML and JavaScript, but no prior knowledge of G6 is required. If readers are already familiar with the basics of G6, they can skip some content and focus on important points.
|
||||
|
||||
## Setup
|
||||
## Environment Setup
|
||||
|
||||
Any code editor works for this Tutorial. We recommend to run this demo in Chrome. In this tutorial, we import G6 V3.7.1 by CDN. We simplified the code to make it easy. For other environments, please refer to the installation guide in [Getting Started](/en/docs/manual/getting-started).
|
||||
It is recommended to use the latest version of the Chrome browser as the runtime environment and any code editor for code writing. This tutorial assumes that G6 is directly imported via CDN. The version used is 3.7.1, which simplifies many features in our code. If readers want to try this tutorial in a different environment, they can refer to the installation and configuration section in the [Quick Start](https://g6-next.antv.antgroup.com/en/manual/getting-started) for guidance.
|
||||
|
||||
New an index.html file, and add the code below:
|
||||
Create a new index.html file and add the following code:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
@ -48,16 +51,14 @@ New an index.html file, and add the code below:
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- import G6 by CDN -->
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<!-- Import G6 5.0 beta -->
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
|
||||
<script>
|
||||
console.log(G6.Global.version);
|
||||
console.log(G6);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Open index.html with your browser. It is success if there is the version number of G6 printed in the console.
|
||||
Open the index.html file in a browser, open the console, and you should see G6 printed, indicating successful import.
|
||||
|
@ -9,9 +9,9 @@ G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、
|
||||
|
||||
## 入门教程简介
|
||||
|
||||
我们在本入门教程中将会完成一个如下图所示简单的图可视化,我们将在后文中称其为 **Tutorial 案例**,<a href='https://codepen.io/Yanyan-Wang/pen/mdbYZvZ' target='_blank'>完整代码</a>。
|
||||
在本入门教程将会完成一个如下图所示简单的图可视化,我们将在后文中称其为 **Tutorial 案例**,<a href='https://codepen.io/Yanyan-Wang/pen/mdbYZvZ' target='_blank'>完整代码</a>。
|
||||
|
||||
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*YlTVS54xV3EAAAAAAAAAAABkARQnAQ' width=500 alt='img' />
|
||||
<img src='https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*9VQjTp0Ipi8AAAAAAAAAAAAADmJ7AQ/original' style="text-align: center;" width=500 alt='img' />
|
||||
|
||||
<div style="text-align: center;"><b>Tutorial 案例</b> 效果图</div>
|
||||
|
||||
@ -29,7 +29,7 @@ G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、
|
||||
- 插件 & 工具
|
||||
- \*动画(选读)
|
||||
|
||||
`提示` <br />该入门教程是为希望“边学边做”的读者设计的。如果您更希望从底层概念开始学习 G6,您可以参见:[核心概念](/zh/docs/manual/middle/graph)。
|
||||
`提示` <br />该入门教程是为希望“边学边做”的读者设计的。G6 5.0 的更多教程正在建设中,目前可以参考 [API 文档](https://g6-next.antv.antgroup.com/apis)。
|
||||
|
||||
## 基础知识
|
||||
|
||||
@ -37,7 +37,7 @@ G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、
|
||||
|
||||
## 环境准备
|
||||
|
||||
建议使用新版的 Chrome 浏览器作为运行环境,用任意的代码编辑器进行代码的编写即可。本教程默认采用 CDN 的方式直接引入 G6 类库,引入的版本是 3.7.1,此版本很多特性会大大简化我们的代码。如果希望在其他环境尝试本教程的学习,读者可以参考 [快速上手](/zh/docs/manual/getting-started) 中的安装配置部分。
|
||||
建议使用新版的 Chrome 浏览器作为运行环境,用任意的代码编辑器进行代码的编写即可。本教程默认采用 CDN 的方式直接引入 G6 类库,引入的版本是 3.7.1,此版本很多特性会大大简化我们的代码。如果希望在其他环境尝试本教程的学习,读者可以参考 [快速上手](https://g6-next.antv.antgroup.com/manual/getting-started) 中的安装配置部分。
|
||||
|
||||
新建 index.html 文件,并添加如下代码:
|
||||
|
||||
@ -49,16 +49,14 @@ G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、
|
||||
<title>Tutorial Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 引入 G6 -->
|
||||
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
|
||||
<!-- 4.x and later versions -->
|
||||
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
|
||||
<!-- 引入 G6 5.0 beta -->
|
||||
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/5.0.0-beta.0/dist/g6.min.js"></script>
|
||||
|
||||
<script>
|
||||
console.log(G6.Global.version);
|
||||
console.log(G6);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
使用浏览器打开 index.html 文件,打开控制台,可以看到 G6 的版本号,说明 G6 已成功引入。
|
||||
使用浏览器打开 index.html 文件,打开控制台,可以看到打印出的 G6,说明成功引入。
|
||||
|
@ -29,7 +29,7 @@ To achieve data layering, prevent data pollution, and better avoid the mixture o
|
||||
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
// ... other configurations
|
||||
data: v4data, // A set of data in v4 format
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ order: 3
|
||||
|
||||
```typescript
|
||||
const graph = new Graph({
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
// ... 其他配置
|
||||
data: v4data, // 一份 v4 格式的数据
|
||||
});
|
||||
|
@ -28,7 +28,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
|
@ -160,7 +160,7 @@ const create3DGraph = async (data) => {
|
||||
width,
|
||||
height,
|
||||
renderer: 'webgl-3d',
|
||||
transform: [
|
||||
transforms: [
|
||||
'data-format',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
@ -250,7 +250,7 @@ const create2DGraph = (renderer, data) => {
|
||||
width,
|
||||
height,
|
||||
renderer,
|
||||
transform: [
|
||||
transforms: [
|
||||
'data-format',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { Graph, Extensions, extend } from '@antv/g6';
|
||||
|
||||
// This site has mounted Algorithm to window. 本站点已将 Algorithm 挂载到 window 上
|
||||
// Import Algorithm for node clustering in your project. 在你的项目中需要引入 Algorithm 用于下面的节点聚类计算
|
||||
// import Algorithm from '@antv/layout'
|
||||
|
||||
const container = document.getElementById('container');
|
||||
const width = container.scrollWidth;
|
||||
const height = container.scrollHeight || 500;
|
||||
@ -222,13 +226,13 @@ const graph = new ExtGraph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: [
|
||||
transforms: [
|
||||
'data-format',
|
||||
'clustering-node',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
field: 'degree',
|
||||
range: [2, 24],
|
||||
range: [4, 24],
|
||||
},
|
||||
],
|
||||
modes: {
|
||||
|
@ -18,7 +18,7 @@ const graph = new ExtGraph({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'collapse-expand-tree', 'click-select'],
|
||||
},
|
||||
|
@ -60,8 +60,8 @@ fetch('https://assets.antv.antgroup.com/g6/eva-3d.json')
|
||||
data: {
|
||||
...innerModel.data,
|
||||
keyShape: {
|
||||
lineWidth: 0.6,
|
||||
opacity: 0.6,
|
||||
lineWidth: 0.4,
|
||||
opacity: 0.4,
|
||||
stroke: '#fff',
|
||||
},
|
||||
type: 'line-edge',
|
||||
|
@ -46,7 +46,7 @@ const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['double-finger-drag-canvas'],
|
||||
},
|
||||
|
@ -93,7 +93,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/70cde3be-22e8-4291-98f1-4d5
|
||||
width,
|
||||
height,
|
||||
autoFit: 'view',
|
||||
transform: ['transform-v4-data', 'edge-cluster'],
|
||||
transforms: ['transform-v4-data', 'edge-cluster'],
|
||||
layout: { type: 'line-layout' },
|
||||
theme: {
|
||||
type: 'spec',
|
||||
|
@ -30,7 +30,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/70cde3be-22e8-4291-98f1-4d5
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: [
|
||||
transforms: [
|
||||
'transform-v4-data',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
|
@ -53,7 +53,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
|
@ -98,7 +98,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['collapse-expand-tree', 'drag-canvas', 'zoom-canvas'],
|
||||
},
|
||||
|
@ -53,7 +53,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
|
@ -32,7 +32,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
|
@ -37,7 +37,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'collapse-expand-tree', 'activate-relations'],
|
||||
},
|
||||
|
@ -17,7 +17,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node'],
|
||||
},
|
||||
|
@ -97,7 +97,7 @@ const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: [
|
||||
transforms: [
|
||||
'transform-v4-data',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
|
@ -62,7 +62,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
animated: true,
|
||||
|
@ -7,7 +7,7 @@ const graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
|
@ -447,7 +447,7 @@ const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node', 'click-select', 'zoom-canvas'],
|
||||
},
|
||||
|
@ -17,7 +17,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
node: {
|
||||
labelShape: {
|
||||
text: {
|
||||
|
@ -21,7 +21,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/7bacd7d1-4119-4ac1-8be3-4c4
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
},
|
||||
|
@ -21,7 +21,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/7bacd7d1-4119-4ac1-8be3-4c4
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
},
|
||||
|
@ -23,7 +23,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/7bacd7d1-4119-4ac1-8be3-4c4
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node', 'zoom-canvas'],
|
||||
},
|
||||
|
@ -17,7 +17,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'collapse-expand-tree'],
|
||||
},
|
||||
|
@ -17,7 +17,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'collapse-expand-tree'],
|
||||
},
|
||||
|
@ -121,7 +121,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
|
@ -13,7 +13,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/7bacd7d1-4119-4ac1-8be3-4c4
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
|
@ -44,7 +44,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: layoutConfigs.Circular,
|
||||
modes: {
|
||||
default: ['zoom-canvas', 'drag-canvas', 'drag-node', 'click-select', 'brush-select'],
|
||||
|
@ -56,7 +56,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
|
@ -75,7 +75,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
layout: {
|
||||
type: 'force',
|
||||
preventOverlap: true,
|
||||
|
@ -41,7 +41,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'collapse-expand-tree'],
|
||||
},
|
||||
|
@ -56,7 +56,7 @@ const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: [
|
||||
transforms: [
|
||||
'data-format',
|
||||
{
|
||||
type: 'map-node-size',
|
||||
@ -65,7 +65,16 @@ const graph = new ExtGraph({
|
||||
},
|
||||
],
|
||||
modes: {
|
||||
default: ['brush-select', 'zoom-canvas', 'activate-relations', 'drag-canvas', 'drag-node'],
|
||||
default: [
|
||||
'brush-select',
|
||||
{
|
||||
type: 'zoom-canvas',
|
||||
enableOptimize: true,
|
||||
},
|
||||
{ type: 'drag-canvas', enableOptimize: true },
|
||||
'activate-relations',
|
||||
'drag-node',
|
||||
],
|
||||
},
|
||||
node: (model) => {
|
||||
const { id, data } = model;
|
||||
|
@ -36,14 +36,6 @@
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*mysRTLxipwMAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "moreData.js",
|
||||
"title": {
|
||||
"zh": "50000+ 图元使用 Canvas 渲染",
|
||||
"en": "50000+ Graphic Shapes Rendered by Canvas"
|
||||
},
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*6U-cSrdpBjYAAAAAAAAAAABkARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "moreDataWebGL.js",
|
||||
"title": {
|
||||
|
@ -18,17 +18,17 @@ const graph = new ExtGraph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: [
|
||||
{
|
||||
type: 'zoom-canvas',
|
||||
enableOptimize: false,
|
||||
enableOptimize: true,
|
||||
optimizeZoom: 0.9,
|
||||
},
|
||||
{
|
||||
type: 'drag-canvas',
|
||||
enableOptimize: false,
|
||||
enableOptimize: true,
|
||||
},
|
||||
'drag-node',
|
||||
'brush-select',
|
||||
|
@ -19,17 +19,17 @@ const graph = new ExtGraph({
|
||||
width,
|
||||
height,
|
||||
renderer: 'webgl',
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: [
|
||||
{
|
||||
type: 'zoom-canvas',
|
||||
enableOptimize: false,
|
||||
enableOptimize: true,
|
||||
optimizeZoom: 0.9,
|
||||
},
|
||||
{
|
||||
type: 'drag-canvas',
|
||||
enableOptimize: false,
|
||||
enableOptimize: true,
|
||||
},
|
||||
'drag-node',
|
||||
'brush-select',
|
||||
|
@ -17,7 +17,7 @@ fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/algorithm-category.j
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node', 'collapse-expand-tree'],
|
||||
},
|
||||
|
@ -50,7 +50,7 @@ const graph = new Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
transform: ['transform-v4-data'],
|
||||
transforms: ['transform-v4-data'],
|
||||
plugins: [fisheye],
|
||||
node: (model) => {
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user