JavaScript data visualization library for creating interactive charts, graphs, and scientific visualizations
81
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.
Animates the plot through a sequence of frames or to a specific frame state with customizable animation options.
/**
* Animates the plot through frames or to a target state
* @param graphDiv - DOM element ID (string) or element reference
* @param frames - Frame objects, frame names, or frame group names to animate to
* @param animationOpts - Animation configuration options
* @returns Promise that resolves when animation completes
*/
function animate(
graphDiv: string | HTMLElement,
frames: Frame[] | string | string[],
animationOpts?: AnimationOptions
): Promise<void>;Usage Examples:
import Plotly from 'plotly.js-dist';
// Basic animation between two states
const frames = [
{
name: 'frame1',
data: [{
x: [1, 2, 3],
y: [1, 4, 9],
type: 'scatter'
}]
},
{
name: 'frame2',
data: [{
x: [1, 2, 3],
y: [2, 8, 18],
type: 'scatter'
}]
}
];
// Add frames first
await Plotly.addFrames('chart', frames);
// Animate to specific frame
await Plotly.animate('chart', 'frame2', {
transition: { duration: 1000, easing: 'cubic-in-out' },
frame: { duration: 500, redraw: false }
});
// Animate through multiple frames
await Plotly.animate('chart', ['frame1', 'frame2'], {
transition: { duration: 800 },
frame: { duration: 1000 }
});
// Advanced animation with custom easing
await Plotly.animate('chart', frames, {
transition: {
duration: 2000,
easing: 'elastic-out'
},
frame: {
duration: 1500,
redraw: true
},
mode: 'afterall'
});Adds animation frames to a plot at specified positions. Frames define different states of the plot for animation.
/**
* Adds animation frames to a plot
* @param graphDiv - DOM element ID (string) or element reference
* @param frameList - Array of frame objects defining animation states
* @param indices - Position(s) to insert frames (optional, defaults to end)
* @returns Promise that resolves to the graph div element
*/
function addFrames(
graphDiv: string | HTMLElement,
frameList: Frame[],
indices?: number | number[]
): Promise<HTMLElement>;Usage Examples:
// Basic frame creation
const frames = [
{
name: 'year2020',
data: [{
x: countries,
y: gdp2020,
type: 'bar'
}],
layout: {
title: 'GDP by Country - 2020'
}
},
{
name: 'year2021',
data: [{
x: countries,
y: gdp2021,
type: 'bar'
}],
layout: {
title: 'GDP by Country - 2021'
}
}
];
await Plotly.addFrames('chart', frames);
// Frames with custom traces and layout changes
const evolutionFrames = [
{
name: 'step1',
data: [{
x: [1, 2, 3],
y: [1, 4, 2],
mode: 'markers',
marker: { size: 10, color: 'red' }
}],
traces: [0], // Apply to first trace only
layout: {
xaxis: { range: [0, 5] },
annotations: [{
text: 'Step 1',
x: 2.5,
y: 4
}]
}
},
{
name: 'step2',
data: [{
x: [1, 2, 3, 4],
y: [1, 4, 2, 6],
mode: 'lines+markers',
marker: { size: 8, color: 'blue' }
}],
traces: [0],
layout: {
annotations: [{
text: 'Step 2',
x: 2.5,
y: 6
}]
}
}
];
await Plotly.addFrames('evolution-chart', evolutionFrames);
// Add frames at specific positions
await Plotly.addFrames('chart', newFrames, [0, 2]); // Insert at positions 0 and 2Removes animation frames from a plot by their names or indices.
/**
* Removes animation frames from a plot
* @param graphDiv - DOM element ID (string) or element reference
* @param frameList - Array of frame names or indices to remove
* @returns Promise that resolves to the graph div element
*/
function deleteFrames(
graphDiv: string | HTMLElement,
frameList: string[] | number[]
): Promise<HTMLElement>;Usage Examples:
// Remove frames by name
await Plotly.deleteFrames('chart', ['frame1', 'frame3']);
// Remove frames by index
await Plotly.deleteFrames('chart', [0, 2, 4]);
// Remove all frames
const chartDiv = document.getElementById('chart');
const frameNames = chartDiv._transitionData._frames.map(f => f.name);
await Plotly.deleteFrames('chart', frameNames);// Animate through time series data
async function createTimeSeriesAnimation(data, timeField, valueField) {
const timePoints = [...new Set(data.map(d => d[timeField]))].sort();
const frames = timePoints.map(time => ({
name: time.toString(),
data: [{
x: data.filter(d => d[timeField] <= time).map(d => d.x),
y: data.filter(d => d[timeField] <= time).map(d => d[valueField]),
type: 'scatter',
mode: 'lines+markers'
}],
layout: {
title: `Data as of ${time}`,
xaxis: { range: [minX, maxX] },
yaxis: { range: [minY, maxY] }
}
}));
await Plotly.addFrames('timeseries-chart', frames);
// Auto-play animation
await Plotly.animate('timeseries-chart', frames.map(f => f.name), {
transition: { duration: 300 },
frame: { duration: 500 }
});
}// Animate scatter plot data evolution
const scatterFrames = years.map(year => ({
name: `year-${year}`,
data: [{
x: countries.map(country => getGDP(country, year)),
y: countries.map(country => getLifeExpectancy(country, year)),
text: countries,
mode: 'markers',
marker: {
size: countries.map(country => getPopulation(country, year) / 1000000),
color: countries.map(country => getRegionColor(country)),
sizemode: 'diameter',
sizeref: 0.1
},
type: 'scatter'
}],
layout: {
title: `World Development Indicators - ${year}`,
xaxis: { title: 'GDP per Capita' },
yaxis: { title: 'Life Expectancy' }
}
}));
await Plotly.addFrames('world-chart', scatterFrames);// Create animated bar chart race
function createBarRace(data, categories, timePoints) {
const frames = timePoints.map(time => {
const timeData = data.filter(d => d.time === time)
.sort((a, b) => b.value - a.value)
.slice(0, 10); // Top 10
return {
name: time.toString(),
data: [{
x: timeData.map(d => d.value),
y: timeData.map(d => d.category),
type: 'bar',
orientation: 'h',
marker: {
color: timeData.map(d => getCategoryColor(d.category))
}
}],
layout: {
title: `Rankings - ${time}`,
xaxis: { range: [0, Math.max(...timeData.map(d => d.value)) * 1.1] },
yaxis: {
categoryorder: 'array',
categoryarray: timeData.map(d => d.category).reverse()
}
}
};
});
return frames;
}// Animate between different shape configurations
const shapeFrames = [
{
name: 'circle',
data: [{
x: circleX,
y: circleY,
mode: 'markers',
marker: { size: 20, color: 'blue' }
}]
},
{
name: 'square',
data: [{
x: squareX,
y: squareY,
mode: 'markers',
marker: { size: 20, color: 'red' }
}]
},
{
name: 'triangle',
data: [{
x: triangleX,
y: triangleY,
mode: 'markers',
marker: { size: 20, color: 'green' }
}]
}
];
await Plotly.addFrames('morph-chart', shapeFrames);
// Smooth morphing animation
await Plotly.animate('morph-chart', ['circle', 'square', 'triangle'], {
transition: {
duration: 2000,
easing: 'cubic-in-out'
},
frame: {
duration: 500,
redraw: false
}
});// Create animation with slider control
const layout = {
title: 'Animated Plot with Slider',
sliders: [{
active: 0,
steps: frames.map((frame, i) => ({
label: frame.name,
method: 'animate',
args: [[frame.name], {
mode: 'immediate',
transition: { duration: 300 },
frame: { duration: 300, redraw: false }
}]
})),
x: 0.1,
len: 0.9,
xanchor: 'left',
y: 0,
yanchor: 'top',
pad: { t: 50, b: 10 },
currentvalue: {
visible: true,
prefix: 'Year:',
xanchor: 'right',
font: { size: 20, color: '#666' }
}
}]
};// Add play/pause buttons
const layout = {
updatemenus: [{
type: 'buttons',
direction: 'left',
buttons: [{
label: 'Play',
method: 'animate',
args: [null, {
mode: 'immediate',
fromcurrent: true,
transition: { duration: 300 },
frame: { duration: 500, redraw: false }
}]
}, {
label: 'Pause',
method: 'animate',
args: [[null], {
mode: 'immediate',
transition: { duration: 0 },
frame: { duration: 0, redraw: false }
}]
}],
pad: { r: 10, t: 87 },
showactive: false,
x: 0.011,
xanchor: 'right',
y: 0,
yanchor: 'top'
}]
};interface AnimationEvents {
'plotly_animating': (eventData: { frame: Frame }) => void;
'plotly_animationinterrupted': (eventData: { frame: Frame }) => void;
'plotly_transitioned': () => void;
'plotly_transitioninterrupted': () => void;
}Usage Examples:
const chartDiv = document.getElementById('animated-chart');
chartDiv.on('plotly_animating', (eventData) => {
console.log('Animating to frame:', eventData.frame.name);
});
chartDiv.on('plotly_animationinterrupted', () => {
console.log('Animation was interrupted');
});
chartDiv.on('plotly_transitioned', () => {
console.log('Transition completed');
});// Pre-calculate all frame data for smooth animation
function precomputeFrames(rawData, timePoints) {
return timePoints.map(time => {
const frameData = processDataForTime(rawData, time);
return {
name: time.toString(),
data: frameData,
layout: { title: `Time: ${time}` }
};
});
}
// Use redraw: false for better performance
const animationOpts = {
transition: { duration: 300 },
frame: {
duration: 200,
redraw: false // Skip full redraw between frames
}
};// Clean up frames when animation is complete
async function runAnimationSequence(chartId, frames) {
await Plotly.addFrames(chartId, frames);
try {
await Plotly.animate(chartId, frames.map(f => f.name));
} finally {
// Clean up frames to free memory
await Plotly.deleteFrames(chartId, frames.map(f => f.name));
}
}interface Frame {
name?: string;
group?: string;
data?: Partial<PlotlyTrace>[];
layout?: Partial<Layout>;
traces?: number[];
baseframe?: string;
}
interface AnimationOptions {
mode?: 'immediate' | 'next' | 'afterall';
direction?: 'forward' | 'reverse';
fromcurrent?: boolean;
transition?: TransitionOptions;
frame?: FrameOptions;
}
interface TransitionOptions {
duration?: number;
easing?: 'linear' | 'quad' | 'cubic' | 'sin' | 'exp' | 'circle' | 'elastic' | 'back' | 'bounce' | string;
ordering?: 'layout first' | 'traces first';
}
interface FrameOptions {
duration?: number;
redraw?: boolean;
}
interface SliderStep {
label?: string;
method?: 'animate' | 'relayout' | 'restyle' | 'update';
args?: any[];
value?: string;
visible?: boolean;
execute?: boolean;
}
interface SliderConfig {
active?: number;
bgcolor?: string;
bordercolor?: string;
borderwidth?: number;
currentvalue?: {
font?: FontConfig;
offset?: number;
prefix?: string;
suffix?: string;
visible?: boolean;
xanchor?: 'left' | 'center' | 'right';
};
font?: FontConfig;
len?: number;
lenmode?: 'fraction' | 'pixels';
steps?: SliderStep[];
tickcolor?: string;
ticklen?: number;
tickwidth?: number;
transition?: TransitionOptions;
visible?: boolean;
x?: number;
xanchor?: 'auto' | 'left' | 'center' | 'right';
y?: number;
yanchor?: 'auto' | 'top' | 'middle' | 'bottom';
}Install with Tessl CLI
npx tessl i tessl/npm-plotly-js-distdocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10