How to Customize Animations and Transitions in ECharts: A Complete Developer Guide

You can customize animations in ECharts through three configuration layers: global defaults set during echarts.init(), series-level options with callback functions for dynamic timing, and low-level ZRender utilities like createWrap() and Universal Transition for shape morphing between chart types.

The Apache ECharts library builds its animation system on top of ZRender, providing granular control over how visual elements enter, update, and transition. Whether you need staggered entrance effects, data-driven duration calculations, or seamless morphing between a bar chart and a pie chart, the source code in apache/echarts exposes a three-tier configuration API. This guide explains how to customize animations and transitions in ECharts using the actual implementation details found in src/model/globalDefault.ts, src/util/types.ts, and the animation utility modules.

Global Animation Defaults

Default animation behavior is defined in src/model/globalDefault.ts and automatically merged into every chart instance unless overridden. These settings control the baseline timing and easing for all series.

// src/model/globalDefault.ts (lines 108-114)
export default {
    animation: 'auto',
    animationDuration: 1000,
    animationDurationUpdate: 500,
    animationEasing: 'cubicInOut',
    animationEasingUpdate: 'cubicInOut',
    animationThreshold: 2000,
    // …
};

Override these values globally by passing an options object as the third argument to echarts.init():

const chart = echarts.init(dom, null, {
  animationDuration: 800,
  animationEasing: 'elasticOut'
});

Series-Level Animation Options

Each series can independently enable or disable animation, set data-size thresholds, and supply callback functions for dynamic durations or delays. The AnimationOptionMixin interface in src/util/types.ts (lines 1018-1056) defines the available properties:

export interface AnimationOptionMixin {
    animation?: boolean;
    animationThreshold?: number;
    animationDuration?: number | AnimationDurationCallback;
    animationEasing?: AnimationEasing;
    animationDelay?: number | AnimationDelayCallback;
    animationDurationUpdate?: number | AnimationDurationCallback;
    animationEasingUpdate?: AnimationEasing;
    animationDelayUpdate?: number | AnimationDelayCallback;
}

The Series model in src/model/Series.ts (lines 525-536) implements the threshold logic that disables animation when data counts exceed the limit:

let animationEnabled = this.getShallow('animation');
if (animationEnabled && this.getData().count() > this.getShallow('animationThreshold')) {
  animationEnabled = false;
}
return !!animationEnabled;

Practical usage combines boolean flags with callback functions to create staggered or data-driven effects:

option = {
  series: [{
    type: 'bar',
    data: [12, 19, 3, 5, 2, 3],
    animation: true,
    animationThreshold: 1000,
    animationDuration: idx => idx * 100 + 500,  // Staggered duration
    animationDelay: idx => Math.random() * 200, // Random wave effect
    animationEasingUpdate: 'bounceOut'
  }]
};

Element-Level Animation Control

For direct manipulation of ZRender graphic elements, ECharts provides the createWrap() utility in src/util/animation.ts (lines 20-55). This helper batches multiple element animations with a single completion callback.

import { createWrap } from 'echarts/lib/util/animation';

const wrap = createWrap();
wrap.add(el1, { x: 100, y: 0 }, 800, 0, 'cubicOut')
    .add(el2, { x: 0, y: 150 }, 600, 200, 'elasticIn')
    .finished(() => console.log('All animations complete'))
    .start();

This low-level approach bypasses the declarative series configuration and targets the underlying Displayable or Path objects directly.

Universal Transition for Cross-Series Morphing

ECharts 5 introduced Universal Transition, enabling smooth morphing between any two series types (e.g., line to pie) while preserving element identity. The core algorithm lives in src/animation/universalTransition.ts.

The system performs three key operations:

  1. Data diffing via flattenDataDiffItems (lines 18-44) matches old and new data items using groupId or childGroupId
  2. Morph animation via applyMorphAnimation (line 25) creates path interpolation between shapes
  3. Performance guarding disables the feature when data exceeds 10,000 items (DATA_COUNT_THRESHOLD at lines 25-31)

Enable Universal Transition by maintaining consistent series IDs across setOption calls:

option = {
  series: [
    {
      type: 'bar',
      id: 'mySeries',
      data: [12, 19, 3, 5]
    }
  ],
  universalTransition: {
    enabled: true,
    divideShape: 'clone'
  }
};

// Later update to pie chart triggers morphing
chart.setOption({
  series: [{
    type: 'pie',
    id: 'mySeries',  // Same ID enables transition
    data: [20, 40, 30]
  }]
});

Complete Working Example

This example demonstrates all three animation layers: global defaults, series callbacks, and Universal Transition:

// 1. Initialize with custom global animation settings
const chart = echarts.init(document.getElementById('main'), null, {
  animationDuration: 1200,
  animationEasing: 'quadraticOut'
});

// 2. Define series with per-datum animation callbacks
chart.setOption({
  series: [{
    type: 'bar',
    id: 'sales',
    data: [120, 200, 150, 80, 70, 110, 130],
    animationDuration: idx => 400 + idx * 80,
    animationDelay: idx => idx * 100,
    animationEasingUpdate: 'elasticOut'
  }]
});

// 3. Trigger Universal Transition to line chart after 3 seconds
setTimeout(() => {
  chart.setOption({
    series: [{
      type: 'line',
      id: 'sales',
      data: [100, 180, 130, 90, 60, 120, 140],
      animationDurationUpdate: 800,
      animationEasingUpdate: 'cubicInOut'
    }],
    universalTransition: { enabled: true }
  });
}, 3000);

The initial render animates each bar with a staggered delay based on data index. After three seconds, the chart morphs the bar rectangles into line points while preserving the visual continuity through shape interpolation.

Summary

  • Global defaults in src/model/globalDefault.ts set baseline animation timing via echarts.init() options
  • Series-level control through AnimationOptionMixin allows dynamic durations, delays, and thresholds to disable animation for large datasets
  • Element-level utilities like createWrap() in src/util/animation.ts provide imperative control over ZRender graphic objects
  • Universal Transition in src/animation/universalTransition.ts enables cross-series morphing by matching data items via series ID and morphing paths through applyMorphAnimation

Frequently Asked Questions

How do I completely disable animations in ECharts?

Set the animation property to false either globally during initialization or per-series. In src/model/Series.ts, the framework checks this.getShallow('animation') and returns false to disable all tweening for that series. For global disablement, pass { animation: false } as the third argument to echarts.init().

What is the purpose of the animationThreshold option?

The animationThreshold guards performance by automatically disabling animation when the data count exceeds the specified number. According to src/model/Series.ts (lines 525-536), when getData().count() exceeds the threshold, the animationEnabled flag is forced to false. The default threshold is 2000 for standard animations and 10000 for Universal Transition.

How does Universal Transition match elements between different chart types?

Universal Transition uses a data diffing algorithm in src/animation/universalTransition.ts (lines 18-44) that flattens old and new series data into comparable items. It matches elements using groupId or childGroupId properties, then applies morphing via applyMorphAnimation to interpolate between the source shape (e.g., a bar rectangle) and target shape (e.g., a pie sector).

Can I animate individual graphic elements independently of series data?

Yes. Import createWrap from src/util/animation.ts to batch animations for individual ZRender elements such as Path or Displayable objects. This utility accepts target properties, duration, delay, and easing parameters, allowing you to animate graphic elements added via the graphic component or custom renderers outside of standard series updates.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →