fix: graph disapears when it is dragged out of the view. fix: when the graph is out of the view, it can not be dragged back. fix: linkPoints size and radius problem. feat: focusItem with animation. docs: animateCfg.

This commit is contained in:
Yanyan-Wang 2020-06-09 10:09:11 +08:00 committed by Yanyan Wang
parent 9d521e1a35
commit b54dd5ada1
33 changed files with 293 additions and 280 deletions

View File

@ -34,7 +34,7 @@ The life cycle of an instance of Graph is: Initialize -> Load data -> Render ->
| defaultCombo | Object | {} | Default combo configurations in global, including type, size, color and so on. Its priority is lower than the configurations in data. It is a new feature of G6 3.5. |
| plugins | Array | [] | Plugins for graph. Please refer to [Plugin](/en/docs/manual/tutorial/plugins##plugin) for detail. |
| animate | Boolean | false | Wheter activate the global animation. Which will take effect while changing layouts, changing data, and other global operations. |
| animateCfg | Object | | The configurations for global animation. Takes effect only when `animate: true`. |
| animateCfg | Object | | The configurations for global animation. Takes effect only when `animate: true`. For more detail about animateCfg, see [Basic Animation Docs](/en/docs/manual/advanced/animation#animatecfg). |
| animateCfg.<br />onFrame | Function | null | The callback function for every frame of animation. The path of custom animation for node can be defined here. The nodes will move linearly when `onFrame` is null. |
| animateCfg.<br />duration | Number | 500 | Duration of animation with unit millisecond. |
| animateCfg.<br />easing | string | easeLinear | The easing function name of animation. Please refer to ease in d3. |
@ -1224,7 +1224,7 @@ graph.zoomTo(3, { x: 100, y: 100 });
graph.zoomTo(0.5);
```
### focusItem(item)
### focusItem(item, animate)
Move the graph to center at the item. This operation can be used as easing animation after searching a node.
@ -1233,11 +1233,16 @@ Move the graph to center at the item. This operation can be used as easing anima
| Name | Type | Required | Description |
| ---- | --------------- | -------- | ----------------------------------- |
| item | string / Object | true | The id or the instance of the item. |
| animate | boolean | false | Whether move the graph with animation. |
| animateCfg | Object | false | The animation's configuraiton. Its configurations can be found in [Basic Animation Docs](/en/docs/manual/advanced/animation#animatecfg) |
**Usage**
```javascript
graph.focusItem(item);
// focus with animation
graph.focusItem(item, true);
```
### changeSize(width, height)

View File

@ -34,7 +34,7 @@ Graph 的生命周期为:初始化—>加载数据—>渲染—>更新—>销
| defaultCombo | Object | {} | 默认状态下 Combo 的配置,比如 `type`, `color`。会被写入的 data 覆盖。3.5 版本新增。 |
| plugins | Array | [] | 向 graph 注册插件。插件机制请见:[插件](/zh/docs/manual/tutorial/plugins#插件) |
| animate | Boolean | false | 是否启用全局动画。 |
| animateCfg | Object | | 动画配置项,仅在 `animate``true` 时有效。 |
| animateCfg | Object | | 动画配置项,仅在 `animate``true` 时有效。关于 `animateCfg` 的更多配置项参见[基础动画教程](/zh/docs/manual/advanced/animation#animatecfg)。 |
| animateCfg.<br />onFrame | Function | null | 回调函数,用于自定义节点运动路径,为空时线性运动。 |
| animateCfg.<br />duration | Number | 500 | 动画时长,单位为毫秒。 |
| animateCfg.<br />easing | string | easeLinear | 动画动效,可参见 d3 ease。 |
@ -1213,18 +1213,23 @@ graph.zoomTo(0.5);
### focusItem(item)
将元素移动到视口中心,该方法可用于做搜索后的缓动动画。
移动图,使得 item 对齐到视口中心,该方法可用于做搜索后的缓动动画。
**参数**
| 名称 | 类型 | 是否必选 | 描述 |
| ---- | --------------- | -------- | ------------------ |
| item | string / Object | true | 元素 ID 或元素实例 |
| animate | boolean | false | 是否带有动画 |
| animateCfg | Object | false | 若带有动画,可配置动画,参见[基础动画教程](/zh/docs/manual/advanced/animation#animatecfg) |
**用法**
```javascript
graph.focusItem(item);
// 动画地移动
graph.focusItem(item, true);
```
### changeSize(width, height)

View File

@ -9,13 +9,14 @@ There are two levels of animation in G6:
- Item animation: The animation on a node or an edge.
<br />
## Global Animation
The global animation is controlled by Graph instance. It takes effect when some global changes happen, such as:
- `graph.updateLayout(cfg)` change the layout;
- `graph.changeData()` change the data.
Configure `animate: true` when instantiating a graph to achieve it. <br />
Configure `animate: true` when instantiating a graph to achieve it. And the `animateCfg` is the configurations for the animate, see [animateCfg](#animateCfg) for more detail. <br />
```javascript
const graph = new G6.Graph({
@ -28,9 +29,6 @@ const graph = new G6.Graph({
});
```
G6 supports all the easing functions in d3.js. Thus, the options of `easing` in `animateCfg`: <br />`'easeLinear'`, <br />`'easePolyIn'`, `'easePolyOut'`, `'easePolyInOut'` , <br />`'easeQuad'`, `'easeQuadIn'`, `'easeQuadOut'`, `'easeQuadInOut'` .
For more detail of the easing functions, please refer to: <a href='https://github.com/d3/d3/blob/master/API.md#easings-d3-ease' target='_blank'>d3 Easings</a>.
## Item Animation
@ -57,7 +55,7 @@ In this example, we are going to magnify and shrink the node. <br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*aAjWQ4n_OOEAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
We first find the graphics shape to be animated by `group.get('children')[0]`. Here we find the 0th graphics shape of this type of node. Then, we call `animate` for the node to define the properties for each frame(The first parameter is a function which returns the properties of each frame, the second parameter defines the configuration for animation).
We first find the graphics shape to be animated by `group.get('children')[0]`. Here we find the 0th graphics shape of this type of node. Then, we call `animate` for the node to define the properties for each frame(The first parameter is a function which returns the properties of each frame; the second parameter defines the configuration for animation, see [animateCfg](#animateCfg)).
```javascript
// Magnify and shrink animation
@ -96,7 +94,7 @@ G6.registerNode(
You can add extra shape with animation in `afterDraw`.<br />
In `afterDraw` of this demo, we draw three background circle shape with different filling colors. And the `animate` is called for magnifying and fading out the three circles. We do not use set the first parameter as a function here, but assign the target style for each animation to the input paramter: magify the radius to 10 and reduce the opacity to 0.1. The second parameter defines the configuration for the animation.<br />
In `afterDraw` of this demo, we draw three background circle shape with different filling colors. And the `animate` is called for magnifying and fading out the three circles. We do not use set the first parameter as a function here, but assign the target style for each animation to the input paramter: magify the radius to 10 and reduce the opacity to 0.1. The second parameter defines the configuration for the animation, see [animateCfg](#animateCfg).<br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*FxDJQ5eY-5oAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
@ -186,7 +184,7 @@ G6.registerNode('background-animate', {
#### Partial Animation
In this demo, we add extra graphics shape(an image) in `afterDraw`, and set a rotation animation for it. Note that the rotation animation is a little complicated, which should be manipulated by matrix.<br />
In this demo, we add extra graphics shape(an image) in `afterDraw`, and set a rotation animation for it. Note that the rotation animation is a little complicated, which should be manipulated by matrix. The first parameter of `animate()` is a function which returns the properties of each frame; the second parameter defines the configuration for animation, see [animateCfg](#animateCfg)<br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*uFQsQqxIa_QAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
@ -252,7 +250,7 @@ The code of the three demo can be found in: <a href='/en/examples/scatter/edge'
#### A Moving Circle
In this demo, we add a circle shape with moving animation in `afterDraw`. In each frame, we return the relative position of the circle on the edge.<br />
In this demo, we add a circle shape with moving animation in `afterDraw`. In each frame, we return the relative position of the circle on the edge. The first parameter of `animate()` is a function which returns the properties of each frame; the second parameter defines the configuration for animation, see [animateCfg](#animateCfg)<br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*OAGPRZbYpw4AAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
@ -303,58 +301,37 @@ G6.registerEdge(
#### Running Dashed Line
The running dashed line is achieved by modifying the `lineDash` in every frame.<br />
The running dashed line is achieved by modifying the `lineDash` in every frame. The first parameter of `animate()` is a function which returns the properties of each frame; the second parameter defines the configuration for animation, see [animateCfg](#animateCfg)<br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*VUgETK6aMzcAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
```javascript
// The values of the lineDash. It can be calculated by util
const dashArray = [
[0, 1],
[0, 2],
[1, 2],
[0, 1, 1, 2],
[0, 2, 1, 2],
[1, 2, 1, 2],
[2, 2, 1, 2],
[3, 2, 1, 2],
[4, 2, 1, 2],
];
const lineDash = [4, 2, 1, 2];
const interval = 9; // The total length of the lineDash
G6.registerEdge(
'line-dash',
{
afterDraw(cfg, group) {
// Get the first graphics shape of this type of edge, which is the edge's path
const shape = group.get('children')[0];
// The start point of the edge's path
const length = shape.getTotalLength();
let totalArray = [];
// Calculate the lineDash for the whole line
for (var i = 0; i < length; i += interval) {
totalArray = totalArray.concat(lineDash);
}
let index = 0;
// Define the animation
shape.animate(
ratio => {
// Returns the properties for each frame. The input parameter ratio is a number that range from 0 to 1. The return value is an object that defines the properties for this frame
const cfg = {
lineDash: dashArray[index].concat(totalArray),
() => {
index++;
if (index > 9) {
index = 0;
}
const res = {
lineDash,
lineDashOffset: -index,
};
// Move 1 each frame
index = (index + 1) % interval;
// Return the properties of this frame, lineDash for this demo
return cfg;
// Returns the configurations to be modified in this frame. Here the return value contains lineDash and lineDashOffset
return res;
},
{
repeat: true, // Play the animation repeatly
duration: 3000, // The duration for one animation
repeat: true, // whether executed repeatly
duration: 3000, // animation's duration
},
); // The duration for one animation
);
},
},
'cubic',
@ -363,7 +340,7 @@ G6.registerEdge(
#### A Growing Edge
A growing edge can also be implemented by calculating the `lineDash`. <br />
A growing edge can also be implemented by calculating the `lineDash`. The first parameter of `animate()` is a function which returns the properties of each frame; the second parameter defines the configuration for animation, see [animateCfg](#animateCfg) <br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*-l9lQ7Ck1QcAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
@ -406,25 +383,13 @@ This kind of animation is related to the [State](/en/docs/manual/middle/states/s
The code below is a part of the code in <a href='/en/examples/scatter/stateChange' target='_blank'>Animation of State Changing</a>. Please note that we have omit some code to emphasize the code related to the animation.
The first parameter of `animate()` is a function which returns the properties of each frame; the second parameter defines the configuration for animation, see [animateCfg](#animateCfg)
```javascript
// const data = ...
// const graph = new G6.Graph({...});
// The values of the lineDash. It can be calculated by util
const dashArray = [
[0, 1],
[0, 2],
[1, 2],
[0, 1, 1, 2],
[0, 2, 1, 2],
[1, 2, 1, 2],
[2, 2, 1, 2],
[3, 2, 1, 2],
[4, 2, 1, 2],
];
const lineDash = [4, 2, 1, 2];
const interval = 9; // The total length of the lineDash
// Register a type of edge named 'can-running'
G6.registerEdge(
@ -437,24 +402,23 @@ G6.registerEdge(
if (name === 'running') {
// When the running state is turned to be true
if (value) {
const length = shape.getTotalLength();
let totalArray = [];
for (var i = 0; i < length; i += interval) {
totalArray = totalArray.concat(lineDash);
}
let index = 0;
shape.animate(
ratio => {
// Returns the properties for each frame. The input parameter ratio is a number that range from 0 to 1. The return value is an object that defines the properties for this frame
const cfg = {
lineDash: dashArray[index].concat(totalArray),
() => {
index++;
if (index > 9) {
index = 0;
}
const res = {
lineDash,
lineDashOffset: -index,
};
index = (index + 1) % interval;
return cfg;
// Returns the configurations to be modified in this frame. Here the return value contains lineDash and lineDashOffset
return res;
},
{
repeat: true, // Play the animation repeatly
duration: 3000, // The duration for one animation
repeat: true, // whether executed repeatly
duration: 3000, // animation's duration
},
);
} else {
@ -495,3 +459,23 @@ graph.on('node:mouseleave', ev => {
```
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"> &nbsp;&nbsp;<strong>Attention:</strong></span> When `running` is turned to be `false`, the animation should be stopped and the `lineDash` should be cleared.
## animateCfg
| Configuration | Type | Default Value | Description |
| ---- | --------------- | -------- | ----------------------------------- |
| duration | Number | 500 | The duration for animating once |
| easing | boolean | 'linearEasing' | The easing function for the animation, see [Easing Function](#easing-Function) for more detail |
| delay | Number | 0 | Execute the animation with delay |
| repeat | boolean | false | Whether execute the animation repeatly |
| callback | Function | undefined | Callback function after the animation finish |
| pauseCallback | Function | undefined | Callback function after the animation is paused by shape.pause() |
| resumeCallback | Function | undefined | Callback function after the animation is resume by shape.resume() |
### Easing Function
G6 supports all the easing functions in d3.js. Thus, the options of `easing` in `animateCfg`: <br />`'easeLinear'`, <br />`'easePolyIn'`, `'easePolyOut'`, `'easePolyInOut'` , <br />`'easeQuad'`, `'easeQuadIn'`, `'easeQuadOut'`, `'easeQuadInOut'` .
For more detail of the easing functions, please refer to: <a href='https://github.com/d3/d3/blob/master/API.md#easings-d3-ease' target='_blank'>d3 Easings</a>.

View File

@ -9,13 +9,14 @@ G6 中的动画分为两个层次:
- 元素(边和节点)动画:节点或边上的独立动画。
<br />
## 全局动画
G6 的全局动画指通过图实例进行某些全局操作时,产生的动画效果。例如:
- `graph.updateLayout(cfg)` 布局的变化
- `graph.changeData()` 数据的变化
通过实例化图时配置 `animate: true`,可以达到每次进行上述操作时,动画效果变化的目的。配合 `animateCfg` 配置动画参数<br />
通过实例化图时配置 `animate: true`,可以达到每次进行上述操作时,动画效果变化的目的。配合 `animateCfg` 配置动画参数`animateCfg` 具体配置参见 [animateCfg](#animateCfg)<br />
```javascript
const graph = new G6.Graph({
@ -28,12 +29,6 @@ const graph = new G6.Graph({
});
```
### easing 函数
easing 函数是指动画的函数。例如线性插值、先快后慢等。<br />G6 支持所有 d3.js 中的动画函数。因此,上面代码中 `animateCfg` 配置中的 String 类型的 `easing` 可以取值有:<br />`'easeLinear'` <br />`'easePolyIn'` `'easePolyOut'` `'easePolyInOut'` <br />`'easeQuad'` `'easeQuadIn'` `'easeQuadOut'`  `'easeQuadInOut'`
更多取值及所有取值含义参见:<a href='https://github.com/d3/d3/blob/master/API.md#easings-d3-ease' target='_blank'>d3 Easings</a>
## 元素动画
由于 G6 的内置节点和边是没有动画的,需要实现节点和边上的动画需要通过[自定义节点](/zh/docs/manual/advanced/custom-node)、[自定义边](/zh/docs/manual/advanced/custom-edge)时复写  `afterDraw`  实现。
@ -59,7 +54,7 @@ easing 函数是指动画的函数。例如线性插值、先快后慢等。<br
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*aAjWQ4n_OOEAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
本例实现节点放大缩小,通过  `group.get('children')[0]` 找到需要更新的图形(这里找到该节点上第 0 个图形),然后调用该图形的 `animate` 方法指定动画的参数及每一帧的变化(  第一个参数是返回每一帧需要变化的参数集的函数,其参数 `ratio` 是当前正在进行的一次动画的进度,范围 [0, 1],第二个参数是动画的参数)。
本例实现节点放大缩小,通过  `group.get('children')[0]` 找到需要更新的图形(这里找到该节点上第 0 个图形),然后调用该图形的 `animate` 方法指定动画的参数及每一帧的变化( 第一个参数是返回每一帧需要变化的参数集的函数,其参数 `ratio` 是当前正在进行的一次动画的进度,范围 [0, 1];第二个参数是动画的参数,动画参数的具体配置参见 [animateCfg](#animateCfg))。
```javascript
// 放大、变小动画
@ -99,7 +94,7 @@ G6.registerNode(
`afterDraw` 方法中为已有节点添加额外的 shape ,并为这些新增的图形设置动画。<br />
本例在 `afterDraw` 方法中,绘制了三个背景 circle ,分别使用不同的颜色填充,再调用 `animate` 方法实现这三个 circle 逐渐变大、变淡的动画。本例中没有使用函数参数的形式,直接在 `animate` 函数的第一个参数中设置每次动画结束时的最终目标样式,即半径增大 10透明度降为 0.1。第二个参数设置动画的配置。<br />
本例在 `afterDraw` 方法中,绘制了三个背景 circle ,分别使用不同的颜色填充,再调用 `animate` 方法实现这三个 circle 逐渐变大、变淡的动画。本例中没有使用函数参数的形式,直接在 `animate` 函数的第一个参数中设置每次动画结束时的最终目标样式,即半径增大 10透明度降为 0.1。第二个参数设置动画的配置,动画参数的具体配置参见 [animateCfg](#animateCfg)<br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*FxDJQ5eY-5oAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
@ -189,7 +184,7 @@ G6.registerNode('background-animate', {
#### 部分图形旋转动画
这一例也是在 `afterDraw` 方法中为已有节点添加额外的 shape (本例中为 image并为这些新增的图形设置旋转动画。旋转动画较为复杂需要通过矩阵的操作实现。<br />
这一例也是在 `afterDraw` 方法中为已有节点添加额外的 shape (本例中为 image并为这些新增的图形设置旋转动画。旋转动画较为复杂需要通过矩阵的操作实现。`animate` 函数的第一个参数是返回每一帧需要变化的参数集的函数,其参数 `ratio` 是当前正在进行的一次动画的进度,范围 [0, 1];第二个参数是动画的参数,动画参数的具体配置参见 [animateCfg](#animateCfg)<br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*uFQsQqxIa_QAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
@ -255,7 +250,7 @@ G6.registerNode(
#### 圆点运动
本例通过在 `afterDraw` 方法中为边增加了一个 circle 图形,该图形沿着线运动。沿着线运动的原理:设定每一帧中,该 circle 在线上的相对位置。<br />
本例通过在 `afterDraw` 方法中为边增加了一个 circle 图形,该图形沿着线运动。沿着线运动的原理:设定每一帧中,该 circle 在线上的相对位置。`animate` 函数的第一个参数是返回每一帧需要变化的参数集的函数,其参数 `ratio` 是当前正在进行的一次动画的进度,范围 [0, 1];第二个参数是动画的参数,动画参数的具体配置参见 [animateCfg](#animateCfg)<br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*OAGPRZbYpw4AAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
@ -306,58 +301,39 @@ G6.registerEdge(
#### 虚线运动的效果
虚线运动的效果是通过计算线的 `lineDash` ,并在每一帧中不断修改实现。<br />
虚线运动的效果是通过计算线的 `lineDash` ,并在每一帧中不断修改实现。`animate` 函数的第一个参数是返回每一帧需要变化的参数集的函数,其参数 `ratio` 是当前正在进行的一次动画的进度,范围 [0, 1];第二个参数是动画的参数,动画参数的具体配置参见 [animateCfg](#animateCfg)<br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*VUgETK6aMzcAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
```javascript
// lineDash 的差值,可以在后面提供 util 方法自动计算
const dashArray = [
[0, 1],
[0, 2],
[1, 2],
[0, 1, 1, 2],
[0, 2, 1, 2],
[1, 2, 1, 2],
[2, 2, 1, 2],
[3, 2, 1, 2],
[4, 2, 1, 2],
];
const lineDash = [4, 2, 1, 2];
const interval = 9; // lineDash 的和
G6.registerEdge(
'line-dash',
{
afterDraw(cfg, group) {
// 获得该边的第一个图形,这里是边的 path
const shape = group.get('children')[0];
// 获得边的 path 的总长度
const length = shape.getTotalLength();
let totalArray = [];
// 计算出整条线的 lineDash
for (var i = 0; i < length; i += interval) {
totalArray = totalArray.concat(lineDash);
}
let index = 0;
// 边 path 图形的动画
shape.animate(
ratio => {
// 每一帧的操作,入参 ratio这一帧的比例值Number。返回值这一帧需要变化的参数集Object
const cfg = {
lineDash: dashArray[index].concat(totalArray),
() => {
index++;
if (index > 9) {
index = 0;
}
const res = {
lineDash,
lineDashOffset: -index,
};
// 每次移动 1
index = (index + 1) % interval;
// 返回需要修改的参数集,这里只修改了 lineDash
return cfg;
// 返回需要修改的参数集,这里修改了 lineDash,lineDashOffset
return res;
},
{
repeat: true, // 动画重复
duration: 3000,
duration: 3000, // 一次动画的时长为 3000
},
); // 一次动画的时长为 3000
);
},
},
'cubic',
@ -366,7 +342,7 @@ G6.registerEdge(
#### 线从无到有
线从无到有的动画效果,同样可以通过计算 `lineDash` 来实现。<br />
线从无到有的动画效果,同样可以通过计算 `lineDash` 来实现。`animate` 函数的第一个参数是返回每一帧需要变化的参数集的函数,其参数 `ratio` 是当前正在进行的一次动画的进度,范围 [0, 1];第二个参数是动画的参数,动画参数的具体配置参见 [animateCfg](#animateCfg)<br />
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*-l9lQ7Ck1QcAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
@ -379,17 +355,16 @@ G6.registerEdge(
const length = group.getTotalLength();
shape.animate(
ratio => {
// 每一帧的操作,入参 ratio这一帧的比例值Number。返回值这一帧需要变化的参数集Object
const startLen = ratio * length;
// 计算线的lineDash
// 计算 lineDash
const cfg = {
lineDash: [startLen, length - startLen],
};
return cfg;
},
{
repeat: true, // 动画重复
duration: 2000, // 一次动画的时长为 2000
repeat: true, // 是否重复执行
duration: 2000, // 一次动画持续时长
},
);
},
@ -398,6 +373,7 @@ G6.registerEdge(
); // 该自定义边继承了内置三阶贝塞尔曲线边 cubic
```
### 交互动画
在交互的过程中也可以添加动画。如下图所示,当鼠标移到节点上时,所有与该节点相关联的边都展示虚线运动的动画。<br />![交互动画.gif](https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*-90pSrm4hkUAAAAAAAAAAABkARQnAQ)<br />上图完整 demo 即代码参见:<a href='/zh/examples/scatter/stateChange' target='_blank'>状态切换动画</a>
@ -407,27 +383,13 @@ G6.registerEdge(
- 自定义边中复写 `setState` 方法监听该边的状态,以及某状态下的动画效果;
- 监听中间的节点的 `mouseenter``mouseleave` 事件,触发相关边的状态变化。
下面代码节选自 demo <a href='/zh/examples/scatter/stateChange' target='_blank'>状态切换动画</a>,请注意省略了部分代码,只展示了交互相关以及边动画相关的代码。
下面代码节选自 demo <a href='/zh/examples/scatter/stateChange' target='_blank'>状态切换动画</a>,请注意省略了部分代码,只展示了交互相关以及边动画相关的代码。`animate` 函数的第一个参数是返回每一帧需要变化的参数集的函数,其参数 `ratio` 是当前正在进行的一次动画的进度,范围 [0, 1];第二个参数是动画的参数,动画参数的具体配置参见 [animateCfg](#animateCfg)
```javascript
// const data = ...
// const graph = new G6.Graph({...});
// lineDash 的差值,可以在后面提供 util 方法自动计算
const dashArray = [
[0, 1],
[0, 2],
[1, 2],
[0, 1, 1, 2],
[0, 2, 1, 2],
[1, 2, 1, 2],
[2, 2, 1, 2],
[3, 2, 1, 2],
[4, 2, 1, 2],
];
const lineDash = [4, 2, 1, 2];
const interval = 9; // lineDash 的总长度。
// 注册名为 'can-running' 的边
G6.registerEdge(
@ -440,20 +402,19 @@ G6.registerEdge(
if (name === 'running') {
// running 状态为 true 时
if (value) {
const length = shape.getTotalLength();
let totalArray = [];
for (var i = 0; i < length; i += interval) {
totalArray = totalArray.concat(lineDash);
}
let index = 0;
let index = 0;// 边 path 图形的动画
shape.animate(
ratio => {
// 每一帧的操作,入参 ratio这一帧的比例值Number。返回值这一帧需要变化的参数集Object
const cfg = {
lineDash: dashArray[index].concat(totalArray),
() => {
index++;
if (index > 9) {
index = 0;
}
const res = {
lineDash,
lineDashOffset: -index,
};
index = (index + 1) % interval;
return cfg;
// 返回需要修改的参数集,这里修改了 lineDash,lineDashOffset
return res;
},
{
repeat: true, // 动画重复
@ -498,3 +459,23 @@ graph.on('node:mouseleave', ev => {
```
<span style="background-color: rgb(251, 233, 231); color: rgb(139, 53, 56)"> &nbsp;&nbsp;<strong>⚠️ 注意:</strong></span> `running``false` 时,要停止动画,同时把 `lineDash` 清空。
## animateCfg
| 配置项 | 类型 | 默认值 | 描述 |
| ---- | --------------- | -------- | ----------------------------------- |
| duration | Number | 500 | 一次动画的时长 |
| easing | boolean | 'linearEasing' | 动画函数,见 [easing 函数](#easing-函数) |
| delay | Number | 0 | 延迟一段时间后执行动画 |
| repeat | boolean | false | 是否重复执行动画 |
| callback | Function | undefined | 动画执行完时的回调函数 |
| pauseCallback | Function | undefined | 动画暂停时(`shape.pause()`)的回调函数 |
| resumeCallback | Function | undefined | 动画恢复时(`shape.resume()`)的回调函数 |
### easing 函数
easing 函数是指动画的函数。例如线性插值、先快后慢等。<br />G6 支持所有 d3.js 中的动画函数。因此,上面代码中 `animateCfg` 配置中的 String 类型的 `easing` 可以取值有:<br />`'easeLinear'` <br />`'easePolyIn'` `'easePolyOut'` `'easePolyInOut'` <br />`'easeQuad'` `'easeQuadIn'` `'easeQuadOut'`  `'easeQuadInOut'`
更多取值及所有取值含义参见:<a href='https://github.com/d3/d3/blob/master/API.md#easings-d3-ease' target='_blank'>d3 Easings</a>

View File

@ -34,23 +34,23 @@ const graph = new G6.Graph({
},
position: 'bottom',
},
// 节点上左右上下四个方向上的链接circle配置
// configurations for four linkpoints
linkPoints: {
top: true,
right: true,
bottom: true,
left: true,
// circle的大小
size: 5,
// the diameter of the linkPoint
size: 10,
lineWidth: 1,
fill: '#fff',
stroke: '#1890FF',
},
// 节点中icon配置
// icon configuration
icon: {
// 是否显示icon值为 false 则不渲染icon
// whether show the icon
show: true,
// icon的地址,字符串类型
// icon's img address, string type
img:
'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 60,
@ -61,11 +61,11 @@ const graph = new G6.Graph({
default: ['drag-canvas', 'drag-node'],
},
nodeStateStyles: {
// 鼠标hover状态下的配置
// node style of hover state
hover: {
fillOpacity: 0.8,
},
// 选中节点状态下的配置
// node style of selected state
selected: {
lineWidth: 5,
},

View File

@ -34,22 +34,23 @@ const graph = new G6.Graph({
},
position: 'bottom',
},
// 节点上左右上下四个方向上的链接circle配置
// configurations for four linkpoints
linkPoints: {
top: false,
bottom: false,
left: true,
right: true,
size: 5,
// the diameter of the linkPoint
size: 10,
fill: '#fff',
lineWidth: 1,
stroke: '#1890FF',
},
// 节点中icon配置
// icon configuration
icon: {
// 是否显示icon值为 false 则不渲染icon
// whether show the icon
show: true,
// icon的地址,字符串类型
// icon's img address, string type
img:
'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 60,
@ -60,11 +61,11 @@ const graph = new G6.Graph({
default: ['drag-canvas', 'drag-node'],
},
nodeStateStyles: {
// 鼠标hover状态下的配置
// node style of hover state
hover: {
fillOpacity: 0.8,
},
// 选中节点状态下的配置
// node style of selected state
selected: {
lineWidth: 5,
},

View File

@ -34,23 +34,23 @@ const graph = new G6.Graph({
},
position: 'bottom',
},
// 节点上左右上下四个方向上的链接circle配置
// configurations for four linkpoints
linkPoints: {
top: true,
right: false,
bottom: true,
left: false,
// circle的大小
size: 5,
// the diameter of the linkPoint
size: 10,
lineWidth: 1,
fill: '#fff',
stroke: '#1890FF',
},
// 节点中icon配置
// icon configuration
icon: {
// 是否显示icon值为 false 则不渲染icon
// whether show the icon
show: true,
// icon的地址,字符串类型
// icon's img address, string type
img:
'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 60,
@ -61,11 +61,11 @@ const graph = new G6.Graph({
default: ['drag-canvas', 'drag-node'],
},
nodeStateStyles: {
// 鼠标hover状态下的配置
// node style of hover state
hover: {
fillOpacity: 0.8,
},
// 选中节点状态下的配置
// node style of selected state
selected: {
lineWidth: 5,
},

View File

@ -52,7 +52,7 @@ const graph = new G6.Graph({
bottom: false,
left: false,
// the size of the linkpoints' circle
size: 3,
size: 10,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A',

View File

@ -36,7 +36,7 @@ const graph = new G6.Graph({
bottom: true,
left: true,
right: true,
size: 5,
size: 10,
fill: '#fff',
lineWidth: 1,
stroke: '#1890FF',

View File

@ -41,7 +41,7 @@ const graph = new G6.Graph({
leftBottom: true,
rightBottom: true,
// the size of the linkpoints' circle
size: 5,
size: 10,
lineWidth: 1,
fill: '#fff',
stroke: '#1890FF',

View File

@ -42,7 +42,7 @@ const graph = new G6.Graph({
bottom: true,
left: true,
// the size of the linkpoints' circle
size: 5,
size: 10,
lineWidth: 1,
fill: '#fff',
stroke: '#1890FF',

View File

@ -5,10 +5,10 @@ G6.registerEdge(
'line-dash',
{
afterDraw(cfg, group) {
// 获得该边的第一个图形,这里是边的 path
// get the first shape in the group, it is the edge's path here=
const shape = group.get('children')[0];
let index = 0;
// 边 path 图形的动画
// Define the animation
shape.animate(
() => {
index++;
@ -19,18 +19,18 @@ G6.registerEdge(
lineDash,
lineDashOffset: -index,
};
// 返回需要修改的参数集,这里修改了 lineDash,lineDashOffset
// returns the modified configurations here, lineDash and lineDashOffset here
return res;
},
{
repeat: true, // 动画重复
duration: 3000, // 一次动画的时长为 3000
repeat: true, // whether executes the animation repeatly
duration: 3000, // the duration for executing once
},
);
},
},
'cubic',
); // 该自定义边继承了内置三阶贝塞尔曲线边 cubic
'cubic', // extend the built-in edge 'cubic'
);
const data = {
nodes: [

View File

@ -8,23 +8,23 @@ G6.registerEdge(
const length = shape.getTotalLength();
shape.animate(
ratio => {
// 每一帧的操作,入参 ratio这一帧的比例值Number。返回值这一帧需要变化的参数集Object
// the operations in each frame. Ratio ranges from 0 to 1 indicating the prograss of the animation. Returns the modified configurations
const startLen = ratio * length;
// 计算线的lineDash
// Calculate the lineDash
const cfg = {
lineDash: [startLen, length - startLen],
};
return cfg;
},
{
repeat: true, // 动画重复
duration: 2000, // 一次动画的时长为 2000
repeat: true, // Whether executes the animation repeatly
duration: 2000, // the duration for executing once
},
);
},
},
'cubic',
); // 该自定义边继承了内置三阶贝塞尔曲线边 cubic
'cubic', // extend the built-in edge 'cubic'
);
const data = {
nodes: [

View File

@ -3,12 +3,12 @@ G6.registerEdge(
'circle-running',
{
afterDraw(cfg, group) {
// 获得当前边的第一个图形,这里是边本身的 path
// get the first shape in the group, it is the edge's path here=
const shape = group.get('children')[0];
// 边 path 的起点位置
// the start position of the edge's path
const startPoint = shape.getPoint(0);
// 添加红色 circle 图形
// add red circle shape
const circle = group.addShape('circle', {
attrs: {
x: startPoint.x,
@ -19,27 +19,27 @@ G6.registerEdge(
name: 'circle-shape',
});
// 对红色圆点添加动画
// animation for the red circle
circle.animate(
ratio => {
// 每一帧的操作,入参 ratio这一帧的比例值Number。返回值这一帧需要变化的参数集Object
// 根据比例值,获得在边 path 上对应比例的位置。
// the operations in each frame. Ratio ranges from 0 to 1 indicating the prograss of the animation. Returns the modified configurations
// get the position on the edge according to the ratio
const tmpPoint = shape.getPoint(ratio);
// 返回需要变化的参数集,这里返回了位置 x 和 y
// returns the modified configurations here, x and y here
return {
x: tmpPoint.x,
y: tmpPoint.y,
};
},
{
repeat: true, // 动画重复
duration: 3000, // 一次动画的时间长度
repeat: true, // Whether executes the animation repeatly
duration: 3000, // the duration for executing once
},
);
},
},
'cubic',
); // 该自定义边继承内置三阶贝塞尔曲线 cubic
'cubic', // extend the built-in edge 'cubic'
);
const data = {
nodes: [

View File

@ -44,10 +44,12 @@ export default {
const width = this.graph.get('width');
const height = this.graph.get('height');
const graphCanvasBBox = this.graph.get('canvas').getCanvasBBox();
if (graphCanvasBBox.minX + dx > width || graphCanvasBBox.maxX + dx < 0) {
if ((graphCanvasBBox.minX <= width && graphCanvasBBox.minX + dx > width)
|| (graphCanvasBBox.maxX >= 0 && graphCanvasBBox.maxX + dx < 0)) {
dx = 0;
}
if (graphCanvasBBox.minY + dy > height || graphCanvasBBox.maxY + dy < 0) {
if ((graphCanvasBBox.minY <= height && graphCanvasBBox.minY + dy > height)
|| (graphCanvasBBox.maxY >= 0 && graphCanvasBBox.maxY + dy < 0)) {
dy = 0;
}
this.graph.translate(dx, dy);

View File

@ -3,7 +3,7 @@ import { Point } from '@antv/g-base/lib/types';
import Group from '@antv/g-canvas/lib/group';
import isNumber from '@antv/util/lib/is-number';
import isString from '@antv/util/lib/is-string';
import { Item, Matrix, Padding } from '../../types';
import { Item, Matrix, Padding, GraphAnimateConfig } from '../../types';
import { formatPadding } from '../../util/base';
import { applyMatrix, invertMatrix } from '../../util/math';
import Graph from '../graph';
@ -82,15 +82,41 @@ export default class ViewController {
return formatPadding(padding);
}
public focusPoint(point: Point) {
public focusPoint(point: Point, animate?: boolean, animateCfg?: GraphAnimateConfig) {
const viewCenter = this.getViewCenter();
const modelCenter = this.getPointByCanvas(viewCenter.x, viewCenter.y);
let viewportMatrix: Matrix = this.graph.get('group').getMatrix();
if (!viewportMatrix) viewportMatrix = mat3.create();
this.graph.translate(
(modelCenter.x - point.x) * viewportMatrix[0],
(modelCenter.y - point.y) * viewportMatrix[4],
);
if (animate) {
const dx = (modelCenter.x - point.x) * viewportMatrix[0];
const dy = (modelCenter.y - point.y) * viewportMatrix[4];
let lastX = 0;
let lastY = 0;
let newX = 0;
let newY = 0;
const cfg = Object.assign({}, {
duration: 300,
easing: 'easeCubic'
}, animateCfg)
// 动画每次平移一点,直到目标位置
this.graph.get('canvas').animate(
ratio => {
newX = dx * ratio;
newY = dy * ratio;
this.graph.translate(newX - lastX, newY - lastY);
lastX = newX;
lastY = newY;
},
{
...cfg
},
);
} else {
this.graph.translate(
(modelCenter.x - point.x) * viewportMatrix[0],
(modelCenter.y - point.y) * viewportMatrix[4],
);
}
}
/**
@ -147,21 +173,23 @@ export default class ViewController {
/**
*
* @param item Item id
* @param {boolean} animate
* @param {GraphAnimateConfig} animateCfg
*/
public focus(item: string | Item) {
public focus(item: string | Item, animate?: boolean, animateCfg?: GraphAnimateConfig) {
if (isString(item)) {
item = this.graph.findById(item);
}
const group: Group = item.get('group');
let matrix: Matrix = group.getMatrix();
if (!matrix) matrix = mat3.create();
if (item) {
const group: Group = item.get('group');
let matrix: Matrix = group.getMatrix();
if (!matrix) matrix = mat3.create();
// 用实际位置而不是model中的x,y,防止由于拖拽等的交互导致model的x,y并不是当前的x,y
this.focusPoint({
x: matrix[6],
y: matrix[7],
});
}, animate, animateCfg);
}
}

View File

@ -675,10 +675,12 @@ export default class Graph extends EventEmitter implements IGraph {
/**
*
* @param {Item} item
* @param {boolean} animate
* @param {GraphAnimateConfig} animateCfg
*/
public focusItem(item: Item | string): void {
public focusItem(item: Item | string, animate?: boolean, animateCfg?: GraphAnimateConfig): void {
const viewController: ViewController = this.get('viewController');
viewController.focus(item);
viewController.focus(item, animate, animateCfg);
this.autoPaint();
}

View File

@ -15,7 +15,8 @@ import {
GraphOptions,
ModeOption,
ModeType,
ComboConfig
ComboConfig,
GraphAnimateConfig
} from '../types';
import { IEdge, INode, ICombo } from './item';
import PluginBase from '../plugins/base';
@ -97,8 +98,10 @@ export interface IGraph extends EventEmitter {
/**
*
* @param {Item} item
* @param {boolean} animate
* @param {GraphAnimateConfig} animateCfg
*/
focusItem(item: Item | string): void;
focusItem(item: Item | string, animate?: boolean, animateCfg?: GraphAnimateConfig): void;
/**
*

View File

@ -5,6 +5,7 @@ import { Item, NodeConfig, ShapeStyle, ModelConfig } from '../../types';
import Global from '../../global';
import Shape from '../shape';
import { ShapeOptions } from '../../interface/shape';
import { isNumber, isArray } from '@antv/util';
// 带有图标的圆,可用于拓扑图中
Shape.registerNode(
@ -32,7 +33,7 @@ Shape.registerNode(
bottom: false,
left: false,
// circle的大小
size: 3,
size: 10,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A',
@ -88,9 +89,9 @@ Shape.registerNode(
const { linkPoints: defaultLinkPoints } = this.options as ModelConfig;
const linkPoints = deepMix({}, defaultLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize, ...markStyle } = linkPoints;
const { top, left, right, bottom, size: markSize, r: markR, ...markStyle } = linkPoints;
const size = this.getSize!(cfg);
const r = size[0] / 2;
let r = size[0] / 2;
if (left) {
// left circle
group.addShape('circle', {
@ -98,7 +99,7 @@ Shape.registerNode(
...markStyle,
x: -r,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-left',
name: 'link-point-left',
@ -113,7 +114,7 @@ Shape.registerNode(
...markStyle,
x: r,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-right',
name: 'link-point-right',
@ -128,7 +129,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: -r,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-top',
name: 'link-point-top',
@ -143,7 +144,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: r,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-bottom',
name: 'link-point-bottom',

View File

@ -30,7 +30,7 @@ Shape.registerNode(
bottom: false,
left: false,
// circle的大小
size: 3,
size: 10,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A',
@ -88,7 +88,7 @@ Shape.registerNode(
const { linkPoints: defaultLinkPoints } = this.options as ModelConfig;
const linkPoints = mix({}, defaultLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize, ...markStyle } = linkPoints;
const { top, left, right, bottom, size: markSize, r: markR, ...markStyle } = linkPoints;
const size = this.getSize!(cfg);
const width = size[0];
const height = size[1];
@ -99,7 +99,7 @@ Shape.registerNode(
...markStyle,
x: -width / 2,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-left',
name: 'link-point-left',
@ -114,7 +114,7 @@ Shape.registerNode(
...markStyle,
x: width / 2,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-right',
name: 'link-point-right',
@ -129,7 +129,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: -height / 2,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-top',
name: 'link-point-top',
@ -144,7 +144,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: height / 2,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-bottom',
name: 'link-point-bottom',

View File

@ -35,7 +35,7 @@ Shape.registerNode(
bottom: false,
left: false,
// circle的大小
size: 3,
size: 10,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A',
@ -93,7 +93,7 @@ Shape.registerNode(
const { linkPoints: defaultLinkPoints } = this.options as ModelConfig;
const linkPoints = mix({}, defaultLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize, ...markStyle } = linkPoints;
const { top, left, right, bottom, size: markSize, r: markR, ...markStyle } = linkPoints;
const size = this.getSize!(cfg);
const rx = size[0] / 2;
const ry = size[1] / 2;
@ -105,7 +105,7 @@ Shape.registerNode(
...markStyle,
x: -rx,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-left',
name: 'link-point-left',
@ -120,7 +120,7 @@ Shape.registerNode(
...markStyle,
x: rx,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-right',
name: 'link-point-right',
@ -135,7 +135,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: -ry,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-top',
name: 'link-point-top',
@ -150,7 +150,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: ry,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-bottom',
name: 'link-point-bottom',

View File

@ -48,7 +48,7 @@ Shape.registerNode(
bottom: false,
left: false,
// circle的大小
size: 3,
size: 10,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A',
@ -189,7 +189,7 @@ Shape.registerNode(
const { linkPoints: defaultLinkPoints } = this.options as ModelConfig;
const linkPoints = mix({}, defaultLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize, ...markStyle } = linkPoints;
const { top, left, right, bottom, size: markSize, r: markR, ...markStyle } = linkPoints;
const size = (this as ShapeOptions).getSize!(cfg);
const width = size[0];
const height = size[1];
@ -201,7 +201,7 @@ Shape.registerNode(
...markStyle,
x: -width / 2,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-left',
name: 'link-point-left',
@ -216,7 +216,7 @@ Shape.registerNode(
...markStyle,
x: width / 2,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-right',
name: 'link-point-right',
@ -231,7 +231,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: -height / 2,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-top',
name: 'link-point-top',
@ -246,7 +246,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: height / 2,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-bottom',
name: 'link-point-bottom',

View File

@ -33,7 +33,7 @@ Shape.registerNode(
bottom: false,
left: false,
// circle的大小
size: 3,
size: 10,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A',
@ -69,7 +69,7 @@ Shape.registerNode(
const { linkPoints: defaultLinkPoints } = this.options as ModelConfig;
const linkPoints = mix({}, defaultLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize, ...markStyle } = linkPoints;
const { top, left, right, bottom, size: markSize, r: markR, ...markStyle } = linkPoints;
const size = (this as ShapeOptions).getSize!(cfg);
const width = size[0];
const height = size[1];
@ -81,7 +81,7 @@ Shape.registerNode(
...markStyle,
x: -width / 2,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-left',
name: 'link-point-left',
@ -96,7 +96,7 @@ Shape.registerNode(
...markStyle,
x: width / 2,
y: 0,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-right',
name: 'link-point-right',
@ -111,7 +111,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: -height / 2,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-top',
name: 'link-point-top',
@ -126,7 +126,7 @@ Shape.registerNode(
...markStyle,
x: 0,
y: height / 2,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-bottom',
name: 'link-point-bottom',

View File

@ -32,7 +32,7 @@ Shape.registerNode(
leftBottom: false,
rightBottom: false,
// circle的大小
size: 3,
size: 10,
lineWidth: 1,
fill: '#fff',
stroke: '#72CC4A',
@ -97,6 +97,7 @@ Shape.registerNode(
leftBottom,
rightBottom,
size: markSize,
r: markR,
...markStyle
} = linkPoints;
const size = (this as ShapeOptions).getSize!(cfg);
@ -113,7 +114,7 @@ Shape.registerNode(
...markStyle,
x: x1,
y: -y1,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-right',
name: 'link-point-right',
@ -131,7 +132,7 @@ Shape.registerNode(
...markStyle,
x: x1,
y: -y1,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-top',
name: 'link-point-top',
@ -149,7 +150,7 @@ Shape.registerNode(
...markStyle,
x: x1,
y: -y1,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-left',
name: 'link-point-left',
@ -167,7 +168,7 @@ Shape.registerNode(
...markStyle,
x: x1,
y: -y1,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-left-bottom',
name: 'link-point-left-bottom',
@ -185,7 +186,7 @@ Shape.registerNode(
...markStyle,
x: x1,
y: -y1,
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-right-bottom',
name: 'link-point-right-bottom',
@ -277,17 +278,17 @@ Shape.registerNode(
const linkPoints = mix({}, currentLinkPoints, cfg.linkPoints);
const { fill: markFill, stroke: markStroke, lineWidth: borderWidth } = linkPoints;
let markSize = linkPoints.size;
let markSize = linkPoints.size / 2;
if (!markSize) markSize = linkPoints.r;
const { left, right, top, leftBottom, rightBottom } = cfg.linkPoints
? cfg.linkPoints
: {
left: undefined,
right: undefined,
top: undefined,
leftBottom: undefined,
rightBottom: undefined,
};
left: undefined,
right: undefined,
top: undefined,
leftBottom: undefined,
rightBottom: undefined,
};
const size = (this as ShapeOptions).getSize!(cfg);
const outerR = size[0];

View File

@ -33,7 +33,7 @@ Shape.registerNode(
bottom: false,
left: false,
// circle的大小
size: 5,
size: 10,
lineWidth: 1,
fill: '#fff',
stroke: '#72CC4A',
@ -105,7 +105,7 @@ Shape.registerNode(
const direction = cfg.direction || defaultDirection;
const { top, left, right, bottom, size: markSize, ...markStyle } = linkPoints;
const { top, left, right, bottom, size: markSize, r: markR, ...markStyle } = linkPoints;
const size = (this as ShapeOptions).getSize!(cfg);
const len = size[0];
@ -130,7 +130,7 @@ Shape.registerNode(
...markStyle,
x: leftPos[0],
y: leftPos[1],
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-left',
name: 'link-point-left',
@ -158,7 +158,7 @@ Shape.registerNode(
...markStyle,
x: rightPos[0],
y: rightPos[1],
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-right',
name: 'link-point-right',
@ -186,7 +186,7 @@ Shape.registerNode(
...markStyle,
x: topPos[0],
y: topPos[1],
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-top',
name: 'link-point-top',
@ -214,7 +214,7 @@ Shape.registerNode(
...markStyle,
x: bottomPos[0],
y: bottomPos[1],
r: markSize,
r: markSize / 2 || markR || 5,
},
className: 'link-point-bottom',
name: 'link-point-bottom',
@ -320,7 +320,7 @@ Shape.registerNode(
const linkPoints = mix({}, currentLinkPoints, cfg.linkPoints);
const { fill: markFill, stroke: markStroke, lineWidth: borderWidth } = linkPoints;
let markSize = linkPoints.size;
let markSize = linkPoints.size / 2;
if (!markSize) markSize = linkPoints.r;
const { left, right, top, bottom } = cfg.linkPoints
? cfg.linkPoints

View File

@ -419,7 +419,7 @@ export const getLetterWidth = (letter, fontSize) => {
*/
export const getTextSize = (text, fontSize) => {
let width = 0;
const pattern = new RegExp("[\u4E00-\u9FA5]+");
const pattern = new RegExp("[\u{4E00}-\u{9FA5}]+");
text.split('').forEach(letter => {
if (pattern.test(letter)) {
// 中文字符

View File

@ -142,7 +142,7 @@ describe('circle test', () => {
bottom: true,
left: true,
fill: '#fff',
size: 5,
size: 10,
},
},
};

View File

@ -139,7 +139,7 @@ describe('diamond test', () => {
left: true,
right: true,
fill: '#fff',
size: 5,
size: 10,
},
},
};

View File

@ -180,7 +180,7 @@ describe('ellipse test', () => {
return g.get('className') === 'link-point-top';
});
expect(markTop).not.toBe(null);
expect(markTop.attr('r')).toEqual(5);
expect(markTop.attr('r')).toEqual(2.5);
expect(markTop.attr('fill')).toEqual('#fff');
const markBottom = group.find(g => {

View File

@ -459,7 +459,7 @@ describe('model rect test', () => {
return g.get('className') === 'link-point-top';
});
expect(markTop).not.toBe(null);
expect(markTop.attr('r')).toEqual(3);
expect(markTop.attr('r')).toEqual(5);
expect(markTop.attr('y')).toEqual(-35);
const markBottom = group.find(g => {
@ -506,7 +506,7 @@ describe('model rect test', () => {
return g.get('className') === 'link-point-left';
});
expect(markLeft).not.toBe(null);
expect(markLeft.attr('r')).toEqual(3);
expect(markLeft.attr('r')).toEqual(5);
expect(markLeft.attr('y')).toEqual(0);
const markTop = group.find(g => {

View File

@ -89,7 +89,7 @@ describe('rect test', () => {
left: true,
right: true,
fill: '#fff',
size: 5,
size: 10,
},
},
};

View File

@ -214,7 +214,7 @@ describe('star test', () => {
return g.get('className') === 'link-point-right';
});
expect(rightPoint).not.toBe(null);
expect(rightPoint.attr('r')).toBe(10);
expect(rightPoint.attr('r')).toBe(5);
expect(rightPoint.attr('fill')).toBe('#f00');
expect(rightPoint.attr('stroke')).toBe('#0f0');
expect(rightPoint.attr('lineWidth')).toBe(2);
@ -227,7 +227,7 @@ describe('star test', () => {
linkPoints: {
left: false,
top: true,
size: 10,
size: 20,
fill: '#f00',
stroke: '#0f0',
lineWidth: 2,

View File

@ -289,7 +289,7 @@ describe('triangle test', () => {
return g.get('className') === 'link-point-right';
});
expect(rightPoint).not.toBe(null);
expect(rightPoint.attr('r')).toBe(10);
expect(rightPoint.attr('r')).toBe(5);
expect(rightPoint.attr('fill')).toBe('#f00');
expect(rightPoint.attr('stroke')).toBe('#0f0');
expect(rightPoint.attr('lineWidth')).toBe(2);