Demonstrates how to run Generic Animation using SciChart.js, High Performance JavaScript Charts
drawExample.ts
index.html
vanilla.ts
ExampleDataProvider.ts
theme.ts
1import {
2 SciChartSurface,
3 NumericAxis,
4 NumberRange,
5 FastBubbleRenderableSeries,
6 XyzDataSeries,
7 EllipsePointMarker,
8 NativeTextAnnotation,
9 EVerticalAnchorPoint,
10 LineAnnotation,
11 GenericAnimation,
12} from "scichart";
13import { appTheme } from "../../../theme";
14import { fetchPopulationDataData } from "../../../ExampleData/ExampleDataProvider";
15
16const initializeChart = async (rootElement: string | HTMLDivElement) => {
17 // Create a SciChartSurface with bubble chart
18 const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
19 theme: appTheme.SciChartJsTheme,
20 });
21
22 sciChartSurface.title = "In SciChart.js you can animate anything";
23 sciChartSurface.titleStyle = {
24 placeWithinChart: true,
25 fontSize: 24,
26 color: appTheme.ForegroundColor + "C4",
27 };
28
29 sciChartSurface.xAxes.add(
30 new NumericAxis(wasmContext, {
31 axisTitle: "Year",
32 labelPrecision: 0,
33 })
34 );
35 sciChartSurface.yAxes.add(
36 new NumericAxis(wasmContext, {
37 axisTitle: "Life Expectancy (years)",
38 labelPrecision: 0,
39 growBy: new NumberRange(0, 0.2),
40 })
41 );
42
43 return { sciChartSurface, wasmContext };
44};
45
46export const drawExample = async (rootElement: string | HTMLDivElement) => {
47 const [chart, data] = await Promise.all([initializeChart(rootElement), fetchPopulationDataData()]);
48
49 const { sciChartSurface, wasmContext } = chart;
50
51 // TODO link to data source file
52 const { year, lifeExpectancy, gdpPerCapita, population } = data;
53
54 const bubbleSeries0 = new FastBubbleRenderableSeries(wasmContext, {
55 dataSeries: new XyzDataSeries(wasmContext, { xValues: year, yValues: lifeExpectancy, zValues: gdpPerCapita }),
56 opacity: 0.3,
57 // Set the default pointmarker size
58 pointMarker: new EllipsePointMarker(wasmContext, {
59 fill: appTheme.VividSkyBlue,
60 opacity: 0.3,
61 width: 64,
62 height: 64,
63 strokeThickness: 0,
64 }),
65 // z sizes are pixels so normalize these until the largest value in gdpPerCapita = 100px
66 zMultiplier: 100 / Math.max(...gdpPerCapita),
67 });
68 sciChartSurface.renderableSeries.add(bubbleSeries0);
69
70 // add a label & line
71 const labelAnnotation1 = new NativeTextAnnotation({
72 x1: 1955,
73 y1: 82,
74 text: "In this dataset life expectancy increases with time (years).\n Bubble size is GDP/capita",
75 fontSize: 18,
76 opacity: 0, // initially hidden
77 textColor: appTheme.PaleSkyBlue,
78 verticalAnchorPoint: EVerticalAnchorPoint.Bottom,
79 });
80 sciChartSurface.annotations.add(labelAnnotation1);
81 const lineAnnotation = new LineAnnotation({
82 x1: 1960,
83 y1: 81.5,
84 x2: 1966,
85 y2: 76,
86 opacity: 0, // initially hidden
87 stroke: appTheme.PaleSkyBlue,
88 strokeThickness: 2,
89 });
90 sciChartSurface.annotations.add(lineAnnotation);
91
92 // Add some animations using genericAnimation
93 //
94
95 // From 0..2 seconds typewrite the title
96 sciChartSurface.addAnimation(addTypewriterEffect(2000, 0, sciChartSurface));
97
98 // From 2..4 seconds animate the label on the data
99 sciChartSurface.addAnimation(
100 new GenericAnimation({
101 from: 0,
102 to: 1,
103 onAnimate: (from: number, to: number, progress: number) => {
104 labelAnnotation1.opacity = to * progress;
105 lineAnnotation.opacity = to * progress;
106 },
107 duration: 2000,
108 delay: 2000,
109 })
110 );
111
112 // From 5..8s change the data and relabel
113 //
114 const bubbleSeries1 = new FastBubbleRenderableSeries(wasmContext, {
115 dataSeries: new XyzDataSeries(wasmContext, {
116 xValues: gdpPerCapita,
117 yValues: lifeExpectancy,
118 zValues: population,
119 }),
120 opacity: 0.3,
121 // Set the default pointmarker size
122 pointMarker: new EllipsePointMarker(wasmContext, {
123 fill: appTheme.VividSkyBlue,
124 opacity: 0.3,
125 width: 64,
126 height: 64,
127 strokeThickness: 0,
128 }),
129 // z sizes are pixels so normalize these until the largest value in population = 100px
130 zMultiplier: 100 / Math.max(...population),
131 // initially hidden
132 isVisible: false,
133 });
134 sciChartSurface.renderableSeries.add(bubbleSeries1);
135
136 // Animate the new data
137 sciChartSurface.addAnimation(
138 new GenericAnimation({
139 from: 0,
140 to: 0.3,
141 onAnimate: (from: number, to: number, progress: number) => {
142 bubbleSeries1.isVisible = true;
143 bubbleSeries1.pointMarker.opacity = to * progress;
144 bubbleSeries0.pointMarker.opacity = 0.3 * (1 - progress);
145 labelAnnotation1.opacity = 1 - progress;
146 lineAnnotation.opacity = 1 - progress;
147 },
148 onCompleted: () => {
149 bubbleSeries0.isVisible = false;
150 // When the data has changed, now zoom to fit new data
151 sciChartSurface.xAxes.get(0).animateVisibleRange(new NumberRange(0, 50000), 1000);
152 sciChartSurface.xAxes.get(0).axisTitle = "GDP per capita";
153 },
154 duration: 3000,
155 delay: 5000,
156 })
157 );
158
159 // add a second label & line from 7..9s
160 const labelAnnotation2 = new NativeTextAnnotation({
161 x1: 10000,
162 y1: 50,
163 text: "Let's swap the axis to GDP vs. Life Expectancy using GenericAnimation.\n Bubble size is Population",
164 fontSize: 18,
165 opacity: 0, // initially hidden
166 textColor: appTheme.PaleSkyBlue,
167 verticalAnchorPoint: EVerticalAnchorPoint.Top,
168 });
169 sciChartSurface.annotations.add(labelAnnotation2);
170 const lineAnnotation2 = new LineAnnotation({
171 x1: 10000,
172 y1: 60,
173 x2: 20000,
174 y2: 50,
175 opacity: 0, // initially hidden
176 stroke: appTheme.PaleSkyBlue,
177 strokeThickness: 2,
178 });
179 sciChartSurface.annotations.add(lineAnnotation2);
180
181 // Animate the 2nd label and line
182 sciChartSurface.addAnimation(
183 new GenericAnimation({
184 from: 0,
185 to: 1,
186 onAnimate: (from: number, to: number, progress: number) => {
187 labelAnnotation2.opacity = to * progress;
188 lineAnnotation2.opacity = to * progress;
189 },
190 duration: 2000,
191 delay: 7000,
192 })
193 );
194
195 return { sciChartSurface };
196};
197
198const addTypewriterEffect = (duration: number, delay: number, sciChartSurface: SciChartSurface) => {
199 return new GenericAnimation<string>({
200 from: "",
201 to: sciChartSurface.title as string,
202 onAnimate: (from: string, to: string, progress: number) => {
203 const length = Math.floor(to.length * progress);
204 sciChartSurface.title = to.substring(0, length);
205 },
206 duration,
207 delay,
208 setInitialValueImmediately: true,
209 });
210};
211This example demonstrates how to leverage SciChart.js with JavaScript to create dynamic and interactive 2D charts enhanced with smooth animations. The focus is on using the GenericAnimation class to create effects such as a typewriter reveal for the chart title, seamless annotation fade-ins, and fluid transitions between different bubble series.
The implementation begins with asynchronous initialization of the SciChartSurface as described in Getting Started with SciChart JS. The chart is constructed by setting up numeric axes, bubble series with XyzDataSeries and FastBubbleRenderableSeries, and integrating annotations directly into the visualization. Custom animations are created using the GenericAnimation class, which offers granular control over the animation progress. For example, a custom typewriter effect is implemented to reveal the chart title gradually, a technique that aligns with approaches covered in Javascript Beginner Tutorial - Typewriter Effect with Vanilla JS. The example also employs axis animations via the animateVisibleRange method to smoothly adjust axis ranges, as detailed in Axis Ranging - Set Range and Zoom to Fit - SciChart.
Key features include real-time update animations that manage opacity transitions between annotations and bubble series, dynamic changes to axis titles, and coordinated delays that sequence various animations over time. These capabilities allow for visually engaging transitions and offer developers a template for implementing sophisticated interactive charts.
Built purely with JavaScript, this example avoids reliance on frameworks and demonstrates clean, efficient code management. It emphasizes asynchronous chart initialization, proper resource cleanup using sciChartSurface.delete(), and detailed control over animation timing. Additionally, performance optimization techniques are applied, ensuring responsiveness even during complex animation sequences. Developers can further explore performance strategies in Performance Optimisation of JavaScript Applications & Charts.

Demonstrates how to run Dataset Animations with JavaScript.

Demonstrates how to run Style Transition Animations with JavaScript.

Demonstrates how to run Startup Animations with JavaScript.

Bring annual comparison data to life with the JavaScript Animated Bar Chart example from SciChart. This demo showcases top 10 tennis players from 1990 to 2024.