!!!###!!!title=自定义动画——VisActor/VChart 教程文档!!!###!!!!!!###!!!description=之前我们介绍了 VChart 中如何配置基础动画和动画编排。除此之外,如果你有更特别的需求,或是更棒的动画创意,可以通过自定义动画的能力来实现。自定义动画的方式不仅仅在 VChart 中可以使用,在整个 VisActor 系列的可视化解决方案中,你都能找到它的身影。本章将会介绍如何在 VChart 中自定义动画,包括自定义动画属性插值函数和自定义动画类两种方法。在开始之前,请确保您已经熟悉了并能够使用 VChart 中的基本图表动画能力。在上一节([复杂动画的配置与编排](../Animation/Configuration_and_Choreography_of_Complex_Animations))中,我们介绍了时间片(TimeSlice)的概念,每个时间片都可以定义多个动画效果(`effect`),接下来,我们将围绕 `effect.custom` 来介绍自定义动画的方法。!!!###!!!

自定义动画

之前我们介绍了 VChart 中如何配置基础动画和动画编排。除此之外,如果你有更特别的需求,或是更棒的动画创意,可以通过自定义动画的能力来实现。自定义动画的方式不仅仅在 VChart 中可以使用,在整个 VisActor 系列的可视化解决方案中,你都能找到它的身影。

本章将会介绍如何在 VChart 中自定义动画,包括自定义动画属性插值函数和自定义动画类两种方法。在开始之前,请确保您已经熟悉了并能够使用 VChart 中的基本图表动画能力。在上一节(复杂动画的配置与编排)中,我们介绍了时间片(TimeSlice)的概念,每个时间片都可以定义多个动画效果(effect),接下来,我们将围绕 effect.custom 来介绍自定义动画的方法。

自定义动画属性插值函数

自定义动画属性插值函数主要是提供设定动画执行前后的视觉通道,来实现一些动画效果。effect.custom 可以配置为一个自定义插值函数:

export type IAnimationChannelInterpolator = (
  ratio: number,
  from: any,
  to: any,
  nextAttributes: any,
  datum: any,
  element: IElement,
  parameters: IAnimationParameters
) => boolean | void;

您需要创建一个实现 IAnimationChannelInterpolator 类型的动画函数,函数参数如下:

  • ratio:动画进行的进度比例,值为 0 到 1 之间的小数。
  • from:动画开始时图元的视觉通道属性。
  • to:动画开始时图元的视觉通道属性。
  • nextAttributes:下一帧的属性值,可以在函数里进行计算和修改。
  • datum:与当前图形元素相关的原始数据。
  • element:当前图形元素。
  • parameters:自定义动画参数,可以在创建动画时传递。

以下是一个自定义动画的示例:

自定义动画类

在 VChart 中,自定义动画是通过实现 ICustomAnimate 接口来完成的。以下是 ICustomAnimate 接口的定义:

export interface ICustomAnimate {
  duration: number;
  easing EasingType;
  step?: IStep;
  mode?: AnimateMode;

  bind: (target IAnimateTarget, subAni: ISubAnimate => void;
  onBind: () => void;
  onRun: () => void;
  onStart: () => void;
  onEnd: () => void;
  onUpdate: (end: boolean, ratio: number, out: Record<string, any>) => void;
  update (end: boolean, ratio: number, out: Record<string, any>) => void;
  getEndProps: () => Record<string, any> | void;
  getFromProps: () => Record<string, any> | void;
  MergedEndProps: () => Record<string, any> | void;
}

接口中定义了一些动的基本属性和生命周期函数,可以用于自定义动画的创建、更新和销毁等。详细文档可以参考VRender 自定义动画

下面的例子使用了 VRender 内置 StreamLight 的自定义动画类,来实现的柱状图流光特效:

编写自己的自定义动画类

在 VChart 中,可以继承ACustomAnimate类来实现编写自己的自定义动画类,例如下面代码,编写了一个柱子从某个点 translate 的动画

import { ACustomAnimate, EasingType } from '@visactor/vrender-core';
export class BarFromPoint extends ACustomAnimate<{ y?: number; y1?: number; x?: number; x1?: number }> {
  constructor(
    from: { y?: number; y1?: number; x?: number; x1?: number },
    to: { y?: number; y1?: number; x?: number; x1?: number },
    duration: number,
    easing: EasingType,
    params: any
  ) {
    const f = {
      y: from.y1,
      y1: from.y1,
      x: from.x1,
      x1: from.x1
    };
    super(f, { y: from.y, y1: from.y1, x: from.x, x1: from.x1 }, duration, easing, params);
  }

  getEndProps(): Record<string, any> {
    return this.to;
  }

  getFromProps(): void | Record<string, any> {
    return this.from;
  }

  onBind(): void {
    this.target && this.target.setAttributes(this.from);
  }

  onUpdate(end: boolean, ratio: number, out: Record<string, any>): void {
    const { x: fromX, y: fromY } = this.params.from;
    const { x: toX, y: toY, y1: toY1 } = this.to;
    const height = toY1! - toY!;
    out.x = fromX + (toX! - fromX) * ratio;
    out.y = fromY + (toY! - fromY) * ratio;
    out.y1 = out.y + height;
  }
}

然后将这个类传入 spec 中自定义函数的 custom 字段即可

{
  type: 'bar',
  data: [
    {
      id: 'barData',
      values: [
        { month: 'Monday', sales: 22 },
        { month: 'Tuesday', sales: 13 },
        { month: 'Wednesday', sales: 25 },
        { month: 'Thursday', sales: 29 },
        { month: 'Friday', sales: 38 }
      ]
    }
  ],
  animationAppear: {
    bar: {
      channel: ['x', 'y', 'x1', 'y1', 'width', 'height', 'cornerRadius'],
      custom: BarFromPoint,
      duration: 1000,
      easing: 'linear',
      customParameters: {
        from: {x: 0, y: 0},
      }
    },
  },
  xField: 'month',
  yField: 'sales'
}