<commit_before>---
title: 基础动画
order: 5
---

G6 中的动画分为两个层次：

- 全局动画：全局性的动画，图整体变化时的动画过渡；
- 元素（边和节点）动画：节点或边上的独立动画。

<br />

## 全局动画

G6 的全局动画指通过图实例进行某些全局操作时，产生的动画效果。例如：

- `graph.updateLayout(cfg)` 布局的变化
- `graph.changeData()` 数据的变化

通过实例化图时配置 `animate: true`，可以达到每次进行上述操作时，动画效果变化的目的。配合 `animateCfg` 配置动画参数，`animateCfg` 具体配置参见 [animateCfg](#animateCfg)<br />

```javascript
const graph = new G6.Graph({
  // ...                   // 图的其他配置项
  animate: true, // Boolean，切换布局时是否使用动画过度，默认为 false
  animateCfg: {
    duration: 500, // Number，一次动画的时长
    easing: 'linearEasing', // String，动画函数
  },
});
```

## 元素动画

由于 G6 的内置节点和边是没有动画的，需要实现节点和边上的动画需要通过[自定义节点](/zh/docs/manual/middle/elements/nodes/custom-node)、[自定义边](/zh/docs/manual/middle/elements/edges/custom-edge)时复写  `afterDraw`  实现。

### 节点动画

节点上的动画，即每一帧发生变化的是节点上的某一个图形。关于节点动画，以下面三个动画示例进行讲解：

- 节点上图形的动画（如下图左）；
- 增加带有动画的背景图形（如下图中）；
- 节点上部分图形的旋转动画（如下图右）。

<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*aAjWQ4n_OOEAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*FxDJQ5eY-5oAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*uFQsQqxIa_QAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
<br />

以上三个动画节点的 demo 代码见： <a href='/zh/examples/scatter/node' target='_blank'>节点动画</a>。

#### 节点上图形的动画

<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]；第二个参数是动画的参数，动画参数的具体配置参见 [animateCfg](#animateCfg)）。

```javascript
// 放大、变小动画
G6.registerNode(
  'circle-animate',
  {
    afterDraw(cfg, group) {
      // 获取该节点上的第一个图形
      const shape = group.get('children')[0];
      // 该图形的动画
      shape.animate(
        (ratio) => {
          // 每一帧的操作，入参 ratio：这一帧的比例值（Number）。返回值：这一帧需要变化的参数集（Object）。
          // 先变大、再变小
          const diff = ratio <= 0.5 ? ratio * 10 : (1 - ratio) * 10;
          let radius = cfg.size;
          if (isNaN(radius)) radius = radius[0];
          // 返回这一帧需要变化的参数集，这里只包含了半径
          return {
            r: radius / 2 + diff,
          };
        },
        {
          // 动画重复
          repeat: true,
          duration: 3000,
          easing: 'easeCubic',
        },
      ); // 一次动画持续的时长为 3000，动画效果为 'easeCubic'
    },
  },
  'circle',
); // 该自定义节点继承了内置节点 'circle'，除了被复写的 afterDraw 方法外，其他按照 'circle' 里的函数执行。
```

#### 增加带有动画的背景图形

在 `afterDraw` 方法中为已有节点添加额外的 shape ，并为这些新增的图形设置动画。<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'/>

```javascript
G6.registerNode('background-animate', {
  afterDraw(cfg, group) {
    let r = cfg.size / 2;
      if (isNaN(r)) {
        r = cfg.size[0] / 2;
      };
    // 第一个背景圆
    const back1 = group.addShape('circle',{
      zIndex: -3,
      attrs: {
        x: 0,
        y: 0,
        r,
        fill: cfg.color,
        opacity: 0.6
      },
      // must be assigned in G6 3.3 and later versions. it can be any value you want
      name: 'circle-shape1'
    });
    // 第二个背景圆
    const back2 = group.addShape('circle',{
      zIndex: -2,
      attrs: {
        x: 0,
        y: 0,
        r,
        fill: 'blue', // 为了显示清晰，随意设置了颜色
        opacity: 0.6
      },
      // must be assigned in G6 3.3 and later versions. it can be any value you want
      name: 'circle-shape2'
    });
    // 第三个背景圆
    const back3 = group.addShape('circle',{
      zIndex: -1,
      attrs: {
        x: 0,
        y: 0,
        r,
        fill: 'green',
        opacity: 0.6
      },
      // must be assigned in G6 3.3 and later versions. it can be any value you want
      name: 'circle-shape3'
    });
    group.sort(); // 排序，根据 zIndex 排序

    // 第一个背景圆逐渐放大，并消失
    back1.animate({
      r: r + 10,
      opacity: 0.1
    }, {
      repeat: true // 循环
      duration: 3000,
      easing: 'easeCubic',
      delay: 0 // 无延迟
    })

    // 第二个背景圆逐渐放大，并消失
    back2.animate({
      r: r + 10,
      opacity: 0.1
    }, {
      repeat: true // 循环
      duration: 3000,
      easing: 'easeCubic',
      delay: 1000 // 1 秒延迟
    }) // 1 秒延迟

    // 第三个背景圆逐渐放大，并消失
    back3.animate({
      r: r + 10,
      opacity: 0.1
    }, {
      repeat: true // 循环<commit_msg>Update animation.zh.md<commit_after>---
title: 基础动画
order: 5
---

G6 中的动画分为两个层次：

- 全局动画：全局性的动画，图整体变化时的动画过渡；
- 元素（边和节点）动画：节点或边上的独立动画。

<br />

## 全局动画

G6 的全局动画指通过图实例进行某些全局操作时，产生的动画效果。例如：

- `graph.updateLayout(cfg)` 布局的变化
- `graph.changeData()` 数据的变化

通过实例化图时配置 `animate: true`，可以达到每次进行上述操作时，动画效果变化的目的。配合 `animateCfg` 配置动画参数，`animateCfg` 具体配置参见 [animateCfg](#animateCfg)<br />

```javascript
const graph = new G6.Graph({
  // ...                   // 图的其他配置项
  animate: true, // Boolean，切换布局时是否使用动画过度，默认为 false
  animateCfg: {
    duration: 500, // Number，一次动画的时长
    easing: 'linearEasing', // String，动画函数
  },
});
```

## 元素动画

由于 G6 的内置节点和边是没有动画的，需要实现节点和边上的动画需要通过[自定义节点](/zh/docs/manual/middle/elements/nodes/custom-node)、[自定义边](/zh/docs/manual/middle/elements/edges/custom-edge)时复写  `afterDraw`  实现。

### 节点动画

节点上的动画，即每一帧发生变化的是节点上的某一个图形。关于节点动画，以下面三个动画示例进行讲解：

- 节点上图形的动画（如下图左）；
- 增加带有动画的背景图形（如下图中）；
- 节点上部分图形的旋转动画（如下图右）。

<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*aAjWQ4n_OOEAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*FxDJQ5eY-5oAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
<img src='https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*uFQsQqxIa_QAAAAAAAAAAABkARQnAQ' alt='download' width='150'/>
<br />

以上三个动画节点的 demo 代码见： <a href='/zh/examples/scatter/node' target='_blank'>节点动画</a>。

#### 节点上图形的动画

<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]；第二个参数是动画的参数，动画参数的具体配置参见 [animateCfg](#animateCfg)）。

```javascript
// 放大、变小动画
G6.registerNode(
  'circle-animate',
  {
    afterDraw(cfg, group) {
      // 获取该节点上的第一个图形
      const shape = group.get('children')[0];
      // 该图形的动画
      shape.animate(
        (ratio) => {
          // 每一帧的操作，入参 ratio：这一帧的比例值（Number）。返回值：这一帧需要变化的参数集（Object）。
          // 先变大、再变小
          const diff = ratio <= 0.5 ? ratio * 10 : (1 - ratio) * 10;
          let radius = cfg.size;
          if (isNaN(radius)) radius = radius[0];
          // 返回这一帧需要变化的参数集，这里只包含了半径
          return {
            r: radius / 2 + diff,
          };
        },
        {
          // 动画重复
          repeat: true,
          duration: 3000,
          easing: 'easeCubic',
        },
      ); // 一次动画持续的时长为 3000，动画效果为 'easeCubic'
    },
  },
  'circle',
); // 该自定义节点继承了内置节点 'circle'，除了被复写的 afterDraw 方法外，其他按照 'circle' 里的函数执行。
```

#### 增加带有动画的背景图形

在 `afterDraw` 方法中为已有节点添加额外的 shape ，并为这些新增的图形设置动画。<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'/>

```javascript
G6.registerNode('background-animate', {
  afterDraw(cfg, group) {
    let r = cfg.size / 2;
      if (isNaN(r)) {
        r = cfg.size[0] / 2;
      };
    // 第一个背景圆
    const back1 = group.addShape('circle',{
      zIndex: -3,
      attrs: {
        x: 0,
        y: 0,
        r,
        fill: cfg.color,
        opacity: 0.6
      },
      // must be assigned in G6 3.3 and later versions. it can be any value you want
      name: 'circle-shape1'
    });
    // 第二个背景圆
    const back2 = group.addShape('circle',{
      zIndex: -2,
      attrs: {
        x: 0,
        y: 0,
        r,
        fill: 'blue', // 为了显示清晰，随意设置了颜色
        opacity: 0.6
      },
      // must be assigned in G6 3.3 and later versions. it can be any value you want
      name: 'circle-shape2'
    });
    // 第三个背景圆
    const back3 = group.addShape('circle',{
      zIndex: -1,
      attrs: {
        x: 0,
        y: 0,
        r,
        fill: 'green',
        opacity: 0.6
      },
      // must be assigned in G6 3.3 and later versions. it can be any value you want
      name: 'circle-shape3'
    });
    group.sort(); // 排序，根据 zIndex 排序

    // 第一个背景圆逐渐放大，并消失
    back1.animate({
      r: r + 10,
      opacity: 0.1
    }, {
      repeat: true, // 循环
      duration: 3000,
      easing: 'easeCubic',
      delay: 0 // 无延迟
    })

    // 第二个背景圆逐渐放大，并消失
    back2.animate({
      r: r + 10,
      opacity: 0.1
    }, {
      repeat: true, // 循环
      duration: 3000,
      easing: 'easeCubic',
      delay: 1000 // 1 秒延迟
    }) // 1 秒延迟

    // 第三个背景圆逐渐放大，并消失
    back3.animate({
      r: r + 10,
      opacity: 0.1
    }, {
      repeat: true, // 循环
{'fraction_non_alphanumeric': 0.15485873287671234, 'fraction_numerical': 0.02889554794520548, 'mean_word_length': 2.492152466367713, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 10, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 10, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5880524', 'n_tokens_mistral': 5013, 'n_tokens_neox': 5164, 'n_words': 672}