import { deepMix, get, isFunction } from '@antv/util'; import { FIELD_ORIGIN } from '../constant'; import { Coordinate, IGroup, IShape } from '../dependents'; import { AnimateCfg, Data, Datum, GAnimateCfg, Point } from '../interface'; import { AnimateExtraCfg } from './interface'; import { getAnimation } from './animation'; // 默认的动画参数配置 export const DEFAULT_ANIMATE_CFG = { appear: { duration: 450, easing: 'easeQuadOut', }, // 初始入场动画配置 update: { duration: 400, easing: 'easeQuadInOut', }, // 更新时发生变更的动画配置 enter: { duration: 400, easing: 'easeQuadInOut', }, // 更新时新增元素的入场动画配置 leave: { duration: 350, easing: 'easeQuadIn', }, // 更新时销毁动画配置 }; // 各个 Geometry 默认的动画执行函数 const GEOMETRY_ANIMATE_CFG = { interval: (coordinate: Coordinate) => { return { enter: { animation: coordinate.isRect ? (coordinate.isTransposed ? 'scale-in-x' : 'scale-in-y') : 'fade-in', }, update: { animation: coordinate.isPolar && coordinate.isTransposed ? 'sector-path-update' : null, }, leave: { animation: 'fade-out', }, }; }, line: { enter: { animation: 'fade-in', }, leave: { animation: 'fade-out', }, }, path: { enter: { animation: 'fade-in', }, leave: { animation: 'fade-out', }, }, point: { appear: { animation: 'zoom-in', }, enter: { animation: 'zoom-in', }, leave: { animation: 'zoom-out', }, }, area: { enter: { animation: 'fade-in', }, leave: { animation: 'fade-out', }, }, polygon: { enter: { animation: 'fade-in', }, leave: { animation: 'fade-out', }, }, schema: { enter: { animation: 'fade-in', }, leave: { animation: 'fade-out', }, }, edge: { enter: { animation: 'fade-in', }, leave: { animation: 'fade-out', }, }, label: { appear: { animation: 'fade-in', delay: 450, }, enter: { animation: 'fade-in', }, update: { animation: 'position-update', }, leave: { animation: 'fade-out', }, }, }; // 各个 Geometry 默认的群组出场动画 const GEOMETRY_GROUP_APPEAR_ANIMATION = { line: () => { return { animation: 'wave-in', }; }, area: () => { return { animation: 'wave-in', }; }, path: () => { return { animation: 'fade-in', }; }, interval(coordinate: Coordinate) { let animation; if (coordinate.isRect) { animation = coordinate.isTransposed ? 'grow-in-x' : 'grow-in-y'; } else { animation = 'grow-in-xy'; if (coordinate.isPolar && coordinate.isTransposed) { // pie chart animation = 'wave-in'; } } return { animation, }; }, schema: (coordinate) => { let animation; if (coordinate.isRect) { animation = coordinate.isTransposed ? 'grow-in-x' : 'grow-in-y'; } else { animation = 'grow-in-xy'; } return { animation, }; }, polygon: () => { return { animation: 'fade-in', duration: 500, }; }, edge: () => { return { animation: 'fade-in', }; }, }; // 解析用户的动画配置 function parseAnimateConfig(animateCfg: AnimateCfg, data: Data | Datum): GAnimateCfg { return { delay: isFunction(animateCfg.delay) ? animateCfg.delay(data) : animateCfg.delay, easing: isFunction(animateCfg.easing) ? animateCfg.easing(data) : animateCfg.easing, duration: isFunction(animateCfg.duration) ? animateCfg.duration(data) : animateCfg.duration, callback: animateCfg.callback, repeat: animateCfg.repeat, }; } /** * @ignore * 获取 elementName 对应的动画配置,当声明了 `animateType`,则返回 `animateType` 对应的动画配置 * @param elementName 元素名称 * @param coordinate 做表弟类型 * @param animateType 可选,动画类型 */ export function getDefaultAnimateCfg(elementName: string, coordinate: Coordinate, animateType?: string) { let animateCfg = GEOMETRY_ANIMATE_CFG[elementName]; if (animateCfg) { if (isFunction(animateCfg)) { animateCfg = animateCfg(coordinate); } animateCfg = deepMix({}, DEFAULT_ANIMATE_CFG, animateCfg); if (animateType) { return animateCfg[animateType]; } } return animateCfg; } /** * @ignore * 工具函数 * 根据用户传入的配置为 shape 执行动画 * @param shape 执行动画的图形元素 * @param animateCfg 动画配置 * @param cfg 额外的信息 */ export function doAnimate(shape: IGroup | IShape, animateCfg: AnimateCfg, cfg: AnimateExtraCfg) { const data = get(shape.get('origin'), 'data', FIELD_ORIGIN); const animation = animateCfg.animation; // 获取动画执行函数 const parsedAnimateCfg = parseAnimateConfig(animateCfg, data); if (animation) { // 用户声明了动画执行函数 const animateFunction = getAnimation(animation); if (animateFunction) { animateFunction(shape, parsedAnimateCfg, cfg); } } else { // 没有声明,则根据 toAttrs 做差值动画 shape.animate(cfg.toAttrs, parsedAnimateCfg); } } /** * @ignore * 执行 Geometry 群组入场动画 * @param container 执行群组动画的图形元素 * @param animateCfg 动画配置 * @param geometryType geometry 类型 * @param coordinate 坐标系对象 * @param minYPoint y 轴最小值对应的画布坐标点 */ export function doGroupAppearAnimate( container: IGroup, animateCfg: AnimateCfg, geometryType: string, coordinate: Coordinate, minYPoint: Point ) { if (GEOMETRY_GROUP_APPEAR_ANIMATION[geometryType]) { const defaultCfg = GEOMETRY_GROUP_APPEAR_ANIMATION[geometryType](coordinate); const animation = getAnimation(get(defaultCfg, 'animation', '')); if (animation) { const cfg = { ...DEFAULT_ANIMATE_CFG.appear, ...defaultCfg, ...animateCfg, }; container.stopAnimate(); // 先结束当前 container 动画 animation(container, cfg, { coordinate, minYPoint, toAttrs: null, }); } } }