0
# Animation
1
2
Functions for creating smooth animated transitions between plot states and managing animation frames. Plotly.js provides a powerful animation system for creating engaging data visualizations.
3
4
## Capabilities
5
6
### animate
7
8
Animates the plot through a sequence of frames or to a specific frame state with customizable animation options.
9
10
```javascript { .api }
11
/**
12
* Animates the plot through frames or to a target state
13
* @param graphDiv - DOM element ID (string) or element reference
14
* @param frames - Frame objects, frame names, or frame group names to animate to
15
* @param animationOpts - Animation configuration options
16
* @returns Promise that resolves when animation completes
17
*/
18
function animate(
19
graphDiv: string | HTMLElement,
20
frames: Frame[] | string | string[],
21
animationOpts?: AnimationOptions
22
): Promise<void>;
23
```
24
25
**Usage Examples:**
26
27
```javascript
28
import Plotly from 'plotly.js-dist';
29
30
// Basic animation between two states
31
const frames = [
32
{
33
name: 'frame1',
34
data: [{
35
x: [1, 2, 3],
36
y: [1, 4, 9],
37
type: 'scatter'
38
}]
39
},
40
{
41
name: 'frame2',
42
data: [{
43
x: [1, 2, 3],
44
y: [2, 8, 18],
45
type: 'scatter'
46
}]
47
}
48
];
49
50
// Add frames first
51
await Plotly.addFrames('chart', frames);
52
53
// Animate to specific frame
54
await Plotly.animate('chart', 'frame2', {
55
transition: { duration: 1000, easing: 'cubic-in-out' },
56
frame: { duration: 500, redraw: false }
57
});
58
59
// Animate through multiple frames
60
await Plotly.animate('chart', ['frame1', 'frame2'], {
61
transition: { duration: 800 },
62
frame: { duration: 1000 }
63
});
64
65
// Advanced animation with custom easing
66
await Plotly.animate('chart', frames, {
67
transition: {
68
duration: 2000,
69
easing: 'elastic-out'
70
},
71
frame: {
72
duration: 1500,
73
redraw: true
74
},
75
mode: 'afterall'
76
});
77
```
78
79
### addFrames
80
81
Adds animation frames to a plot at specified positions. Frames define different states of the plot for animation.
82
83
```javascript { .api }
84
/**
85
* Adds animation frames to a plot
86
* @param graphDiv - DOM element ID (string) or element reference
87
* @param frameList - Array of frame objects defining animation states
88
* @param indices - Position(s) to insert frames (optional, defaults to end)
89
* @returns Promise that resolves to the graph div element
90
*/
91
function addFrames(
92
graphDiv: string | HTMLElement,
93
frameList: Frame[],
94
indices?: number | number[]
95
): Promise<HTMLElement>;
96
```
97
98
**Usage Examples:**
99
100
```javascript
101
// Basic frame creation
102
const frames = [
103
{
104
name: 'year2020',
105
data: [{
106
x: countries,
107
y: gdp2020,
108
type: 'bar'
109
}],
110
layout: {
111
title: 'GDP by Country - 2020'
112
}
113
},
114
{
115
name: 'year2021',
116
data: [{
117
x: countries,
118
y: gdp2021,
119
type: 'bar'
120
}],
121
layout: {
122
title: 'GDP by Country - 2021'
123
}
124
}
125
];
126
127
await Plotly.addFrames('chart', frames);
128
129
// Frames with custom traces and layout changes
130
const evolutionFrames = [
131
{
132
name: 'step1',
133
data: [{
134
x: [1, 2, 3],
135
y: [1, 4, 2],
136
mode: 'markers',
137
marker: { size: 10, color: 'red' }
138
}],
139
traces: [0], // Apply to first trace only
140
layout: {
141
xaxis: { range: [0, 5] },
142
annotations: [{
143
text: 'Step 1',
144
x: 2.5,
145
y: 4
146
}]
147
}
148
},
149
{
150
name: 'step2',
151
data: [{
152
x: [1, 2, 3, 4],
153
y: [1, 4, 2, 6],
154
mode: 'lines+markers',
155
marker: { size: 8, color: 'blue' }
156
}],
157
traces: [0],
158
layout: {
159
annotations: [{
160
text: 'Step 2',
161
x: 2.5,
162
y: 6
163
}]
164
}
165
}
166
];
167
168
await Plotly.addFrames('evolution-chart', evolutionFrames);
169
170
// Add frames at specific positions
171
await Plotly.addFrames('chart', newFrames, [0, 2]); // Insert at positions 0 and 2
172
```
173
174
### deleteFrames
175
176
Removes animation frames from a plot by their names or indices.
177
178
```javascript { .api }
179
/**
180
* Removes animation frames from a plot
181
* @param graphDiv - DOM element ID (string) or element reference
182
* @param frameList - Array of frame names or indices to remove
183
* @returns Promise that resolves to the graph div element
184
*/
185
function deleteFrames(
186
graphDiv: string | HTMLElement,
187
frameList: string[] | number[]
188
): Promise<HTMLElement>;
189
```
190
191
**Usage Examples:**
192
193
```javascript
194
// Remove frames by name
195
await Plotly.deleteFrames('chart', ['frame1', 'frame3']);
196
197
// Remove frames by index
198
await Plotly.deleteFrames('chart', [0, 2, 4]);
199
200
// Remove all frames
201
const chartDiv = document.getElementById('chart');
202
const frameNames = chartDiv._transitionData._frames.map(f => f.name);
203
await Plotly.deleteFrames('chart', frameNames);
204
```
205
206
## Animation Patterns
207
208
### Time Series Animation
209
210
```javascript
211
// Animate through time series data
212
async function createTimeSeriesAnimation(data, timeField, valueField) {
213
const timePoints = [...new Set(data.map(d => d[timeField]))].sort();
214
215
const frames = timePoints.map(time => ({
216
name: time.toString(),
217
data: [{
218
x: data.filter(d => d[timeField] <= time).map(d => d.x),
219
y: data.filter(d => d[timeField] <= time).map(d => d[valueField]),
220
type: 'scatter',
221
mode: 'lines+markers'
222
}],
223
layout: {
224
title: `Data as of ${time}`,
225
xaxis: { range: [minX, maxX] },
226
yaxis: { range: [minY, maxY] }
227
}
228
}));
229
230
await Plotly.addFrames('timeseries-chart', frames);
231
232
// Auto-play animation
233
await Plotly.animate('timeseries-chart', frames.map(f => f.name), {
234
transition: { duration: 300 },
235
frame: { duration: 500 }
236
});
237
}
238
```
239
240
### Scatter Plot Evolution
241
242
```javascript
243
// Animate scatter plot data evolution
244
const scatterFrames = years.map(year => ({
245
name: `year-${year}`,
246
data: [{
247
x: countries.map(country => getGDP(country, year)),
248
y: countries.map(country => getLifeExpectancy(country, year)),
249
text: countries,
250
mode: 'markers',
251
marker: {
252
size: countries.map(country => getPopulation(country, year) / 1000000),
253
color: countries.map(country => getRegionColor(country)),
254
sizemode: 'diameter',
255
sizeref: 0.1
256
},
257
type: 'scatter'
258
}],
259
layout: {
260
title: `World Development Indicators - ${year}`,
261
xaxis: { title: 'GDP per Capita' },
262
yaxis: { title: 'Life Expectancy' }
263
}
264
}));
265
266
await Plotly.addFrames('world-chart', scatterFrames);
267
```
268
269
### Bar Chart Race Animation
270
271
```javascript
272
// Create animated bar chart race
273
function createBarRace(data, categories, timePoints) {
274
const frames = timePoints.map(time => {
275
const timeData = data.filter(d => d.time === time)
276
.sort((a, b) => b.value - a.value)
277
.slice(0, 10); // Top 10
278
279
return {
280
name: time.toString(),
281
data: [{
282
x: timeData.map(d => d.value),
283
y: timeData.map(d => d.category),
284
type: 'bar',
285
orientation: 'h',
286
marker: {
287
color: timeData.map(d => getCategoryColor(d.category))
288
}
289
}],
290
layout: {
291
title: `Rankings - ${time}`,
292
xaxis: { range: [0, Math.max(...timeData.map(d => d.value)) * 1.1] },
293
yaxis: {
294
categoryorder: 'array',
295
categoryarray: timeData.map(d => d.category).reverse()
296
}
297
}
298
};
299
});
300
301
return frames;
302
}
303
```
304
305
### Morphing Shapes Animation
306
307
```javascript
308
// Animate between different shape configurations
309
const shapeFrames = [
310
{
311
name: 'circle',
312
data: [{
313
x: circleX,
314
y: circleY,
315
mode: 'markers',
316
marker: { size: 20, color: 'blue' }
317
}]
318
},
319
{
320
name: 'square',
321
data: [{
322
x: squareX,
323
y: squareY,
324
mode: 'markers',
325
marker: { size: 20, color: 'red' }
326
}]
327
},
328
{
329
name: 'triangle',
330
data: [{
331
x: triangleX,
332
y: triangleY,
333
mode: 'markers',
334
marker: { size: 20, color: 'green' }
335
}]
336
}
337
];
338
339
await Plotly.addFrames('morph-chart', shapeFrames);
340
341
// Smooth morphing animation
342
await Plotly.animate('morph-chart', ['circle', 'square', 'triangle'], {
343
transition: {
344
duration: 2000,
345
easing: 'cubic-in-out'
346
},
347
frame: {
348
duration: 500,
349
redraw: false
350
}
351
});
352
```
353
354
## Interactive Animation Controls
355
356
### Animation Slider
357
358
```javascript
359
// Create animation with slider control
360
const layout = {
361
title: 'Animated Plot with Slider',
362
sliders: [{
363
active: 0,
364
steps: frames.map((frame, i) => ({
365
label: frame.name,
366
method: 'animate',
367
args: [[frame.name], {
368
mode: 'immediate',
369
transition: { duration: 300 },
370
frame: { duration: 300, redraw: false }
371
}]
372
})),
373
x: 0.1,
374
len: 0.9,
375
xanchor: 'left',
376
y: 0,
377
yanchor: 'top',
378
pad: { t: 50, b: 10 },
379
currentvalue: {
380
visible: true,
381
prefix: 'Year:',
382
xanchor: 'right',
383
font: { size: 20, color: '#666' }
384
}
385
}]
386
};
387
```
388
389
### Play/Pause Controls
390
391
```javascript
392
// Add play/pause buttons
393
const layout = {
394
updatemenus: [{
395
type: 'buttons',
396
direction: 'left',
397
buttons: [{
398
label: 'Play',
399
method: 'animate',
400
args: [null, {
401
mode: 'immediate',
402
fromcurrent: true,
403
transition: { duration: 300 },
404
frame: { duration: 500, redraw: false }
405
}]
406
}, {
407
label: 'Pause',
408
method: 'animate',
409
args: [[null], {
410
mode: 'immediate',
411
transition: { duration: 0 },
412
frame: { duration: 0, redraw: false }
413
}]
414
}],
415
pad: { r: 10, t: 87 },
416
showactive: false,
417
x: 0.011,
418
xanchor: 'right',
419
y: 0,
420
yanchor: 'top'
421
}]
422
};
423
```
424
425
## Animation Events
426
427
```javascript { .api }
428
interface AnimationEvents {
429
'plotly_animating': (eventData: { frame: Frame }) => void;
430
'plotly_animationinterrupted': (eventData: { frame: Frame }) => void;
431
'plotly_transitioned': () => void;
432
'plotly_transitioninterrupted': () => void;
433
}
434
```
435
436
**Usage Examples:**
437
438
```javascript
439
const chartDiv = document.getElementById('animated-chart');
440
441
chartDiv.on('plotly_animating', (eventData) => {
442
console.log('Animating to frame:', eventData.frame.name);
443
});
444
445
chartDiv.on('plotly_animationinterrupted', () => {
446
console.log('Animation was interrupted');
447
});
448
449
chartDiv.on('plotly_transitioned', () => {
450
console.log('Transition completed');
451
});
452
```
453
454
## Performance Optimization
455
456
### Efficient Frame Management
457
458
```javascript
459
// Pre-calculate all frame data for smooth animation
460
function precomputeFrames(rawData, timePoints) {
461
return timePoints.map(time => {
462
const frameData = processDataForTime(rawData, time);
463
return {
464
name: time.toString(),
465
data: frameData,
466
layout: { title: `Time: ${time}` }
467
};
468
});
469
}
470
471
// Use redraw: false for better performance
472
const animationOpts = {
473
transition: { duration: 300 },
474
frame: {
475
duration: 200,
476
redraw: false // Skip full redraw between frames
477
}
478
};
479
```
480
481
### Memory Management
482
483
```javascript
484
// Clean up frames when animation is complete
485
async function runAnimationSequence(chartId, frames) {
486
await Plotly.addFrames(chartId, frames);
487
488
try {
489
await Plotly.animate(chartId, frames.map(f => f.name));
490
} finally {
491
// Clean up frames to free memory
492
await Plotly.deleteFrames(chartId, frames.map(f => f.name));
493
}
494
}
495
```
496
497
## Types
498
499
```javascript { .api }
500
interface Frame {
501
name?: string;
502
group?: string;
503
data?: Partial<PlotlyTrace>[];
504
layout?: Partial<Layout>;
505
traces?: number[];
506
baseframe?: string;
507
}
508
509
interface AnimationOptions {
510
mode?: 'immediate' | 'next' | 'afterall';
511
direction?: 'forward' | 'reverse';
512
fromcurrent?: boolean;
513
transition?: TransitionOptions;
514
frame?: FrameOptions;
515
}
516
517
interface TransitionOptions {
518
duration?: number;
519
easing?: 'linear' | 'quad' | 'cubic' | 'sin' | 'exp' | 'circle' | 'elastic' | 'back' | 'bounce' | string;
520
ordering?: 'layout first' | 'traces first';
521
}
522
523
interface FrameOptions {
524
duration?: number;
525
redraw?: boolean;
526
}
527
528
interface SliderStep {
529
label?: string;
530
method?: 'animate' | 'relayout' | 'restyle' | 'update';
531
args?: any[];
532
value?: string;
533
visible?: boolean;
534
execute?: boolean;
535
}
536
537
interface SliderConfig {
538
active?: number;
539
bgcolor?: string;
540
bordercolor?: string;
541
borderwidth?: number;
542
currentvalue?: {
543
font?: FontConfig;
544
offset?: number;
545
prefix?: string;
546
suffix?: string;
547
visible?: boolean;
548
xanchor?: 'left' | 'center' | 'right';
549
};
550
font?: FontConfig;
551
len?: number;
552
lenmode?: 'fraction' | 'pixels';
553
steps?: SliderStep[];
554
tickcolor?: string;
555
ticklen?: number;
556
tickwidth?: number;
557
transition?: TransitionOptions;
558
visible?: boolean;
559
x?: number;
560
xanchor?: 'auto' | 'left' | 'center' | 'right';
561
y?: number;
562
yanchor?: 'auto' | 'top' | 'middle' | 'bottom';
563
}
564
```