Demonstrates how to create multiple synchronized subcharts with an interactive overview in an Angular application using SciChart.js
drawExample.ts
index.tsx
AxisSynchroniser.ts
theme.ts
SubChartsOverviewModifier.ts
1import {
2 SciChartSurface,
3 NumericAxis,
4 NumberRange,
5 FastLineRenderableSeries,
6 XyDataSeries,
7 ZoomExtentsModifier,
8 MouseWheelZoomModifier,
9 ZoomPanModifier,
10 ESubSurfacePositionCoordinateMode,
11 Rect,
12 EPerformanceMarkType,
13 SciChartSubSurface,
14 I2DSubSurfaceOptions,
15 EAutoRange,
16 RolloverModifier,
17 EXyDirection,
18} from "scichart";
19
20import { appTheme } from "../../../theme";
21import { SubChartsOverviewModifier } from "./SubChartsOverviewModifier";
22import { AxisSynchroniser } from "../../MultiChart/SyncMultiChart/AxisSynchroniser";
23
24const colorsArr = [
25 appTheme.MutedBlue,
26 appTheme.MutedOrange,
27 appTheme.MutedPink,
28 appTheme.MutedPurple,
29 appTheme.MutedRed,
30 appTheme.MutedSkyBlue,
31 appTheme.MutedTeal,
32];
33
34export type TMarkType = EPerformanceMarkType | string;
35
36export interface SubChartConfig {
37 id: string;
38 phase: number;
39 color: string;
40 title: string;
41}
42
43export interface SubChartManager {
44 updateSubCharts: (configs: SubChartConfig[]) => void;
45 addSubChart: (config: SubChartConfig) => void;
46 removeSubChart: (id: string) => void;
47 updateLayout: () => void;
48}
49
50// Helper to create some sample data
51const createLineData = (phase: number) => {
52 const xValues: number[] = [];
53 const yValues: number[] = [];
54 for (let i = 0; i < 500; i++) {
55 const x = i;
56 const y = Math.sin(i * 0.1 + phase);
57 xValues.push(x);
58 yValues.push(y);
59 }
60 return { xValues, yValues };
61};
62
63export const drawExample = async (
64 rootElement: string | HTMLDivElement,
65 initialConfigs: SubChartConfig[]
66): Promise<{ wasmContext: any; sciChartSurface: SciChartSurface; manager: SubChartManager }> => {
67 // Create a SciChartSurface
68 const { wasmContext, sciChartSurface } = await SciChartSurface.create(rootElement);
69
70 // Store references to subcharts for dynamic management
71 const subChartMap = new Map<string, SciChartSubSurface>();
72 const axisSynchroniser = new AxisSynchroniser(new NumberRange(0, 500));
73
74 // Add main axes to the surface for the overview to reference
75 const mainXAxis = new NumericAxis(wasmContext, {
76 id: "mainXAxis",
77 isVisible: false, // Hidden since subcharts have their own axes
78 autoRange: EAutoRange.Always,
79 });
80 const mainYAxis = new NumericAxis(wasmContext, {
81 id: "mainYAxis",
82 isVisible: false, // Hidden since subcharts have their own axes
83 autoRange: EAutoRange.Always,
84 });
85
86 sciChartSurface.xAxes.add(mainXAxis);
87 sciChartSurface.yAxes.add(mainYAxis);
88
89 // Create overview modifier
90 const overviewModifier = new SubChartsOverviewModifier({
91 overviewPosition: new Rect(0, 0.8, 1, 0.2),
92 isTransparent: true,
93 axisTitle: "Overview - All Charts",
94 labelStyle: {
95 color: "#ffffff80",
96 fontSize: 10,
97 },
98 majorTickLineStyle: {
99 color: "#ffffff80",
100 tickSize: 6,
101 strokeThickness: 1,
102 },
103 yAxisGrowBy: new NumberRange(0.1, 0.1),
104 });
105
106 sciChartSurface.chartModifiers.add(overviewModifier);
107
108 const updateLayout = () => {
109 // Simple layout update without recreating subcharts
110 // This is called after add/remove operations to ensure proper spacing
111 // The actual repositioning will be handled by the individual add/remove functions
112 };
113
114 const addSubChart = (config: SubChartConfig, index?: number) => {
115 // Don't add if already exists
116 if (subChartMap.has(config.id)) {
117 return;
118 }
119
120 // For now, just add at the bottom to avoid repositioning existing charts
121 const currentCount = subChartMap.size;
122 const chartIndex = currentCount; // Always add at the end
123
124 // Calculate position: this is a simple approach that may cause overlapping
125 // but avoids the MouseManager issues
126 const rect = new Rect(0, chartIndex * 0.2, 1, 0.2);
127
128 const subChartOptions: I2DSubSurfaceOptions = {
129 id: config.id,
130 position: rect,
131 coordinateMode: ESubSurfacePositionCoordinateMode.Relative,
132 };
133
134 const subChart = SciChartSubSurface.createSubSurface(sciChartSurface, subChartOptions);
135
136 // Create axes for the subchart
137 const subXAxis = new NumericAxis(wasmContext);
138 const subYAxis = new NumericAxis(wasmContext, {
139 growBy: new NumberRange(0.1, 0.1),
140 axisTitle: config.title,
141 axisTitleStyle: { fontSize: 14 },
142 drawMinorGridLines: false,
143 });
144
145 subChart.xAxes.add(subXAxis);
146 subChart.yAxes.add(subYAxis);
147
148 // Create data and series
149 const data = createLineData(config.phase);
150 const dataSeries = new XyDataSeries(wasmContext, {
151 xValues: data.xValues,
152 yValues: data.yValues,
153 });
154
155 const lineSeries = new FastLineRenderableSeries(wasmContext, {
156 dataSeries,
157 strokeThickness: 4,
158 stroke: config.color,
159 opacity: 0.6,
160 });
161
162 // Add to synchronizer and subchart
163 axisSynchroniser.addAxis(subXAxis);
164 subChart.renderableSeries.add(lineSeries);
165
166 // Add modifiers
167 subChart.chartModifiers.add(
168 new ZoomPanModifier(),
169 new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
170 new ZoomExtentsModifier(),
171 new RolloverModifier({ modifierGroup: "one" })
172 );
173
174 // Store reference
175 subChartMap.set(config.id, subChart);
176
177 subChart.zoomExtents();
178 };
179
180 const removeSubChart = (id: string) => {
181 const subChart = subChartMap.get(id);
182 if (subChart) {
183 // Remove from synchronizer
184 const xAxis = subChart.xAxes.get(0);
185 if (xAxis) {
186 axisSynchroniser.removeAxis(xAxis);
187 }
188
189 // Use removeSubChart on the parent surface to properly remove from subChartsProperty array
190 // This prevents MouseManager from iterating over deleted subcharts
191 sciChartSurface.removeSubChart(subChart);
192 subChartMap.delete(id);
193 }
194 };
195
196 const updateSubChart = (id: string, config: SubChartConfig) => {
197 const subChart = subChartMap.get(id);
198 if (subChart) {
199 // Update title
200 const yAxis = subChart.yAxes.get(0);
201 if (yAxis) {
202 yAxis.axisTitle = config.title;
203 }
204
205 // Update color
206 const series = subChart.renderableSeries.get(0) as FastLineRenderableSeries;
207 if (series) {
208 series.stroke = config.color;
209 }
210
211 // Update data if phase changed
212 const data = createLineData(config.phase);
213 if (series && series.dataSeries) {
214 (series.dataSeries as XyDataSeries).clear();
215 (series.dataSeries as XyDataSeries).appendRange(data.xValues, data.yValues);
216 }
217 }
218 };
219
220 const updateSubCharts = (configs: SubChartConfig[]) => {
221 // Use the safer recreate approach to avoid MouseManager issues
222 recreateSubChartsWithLayout(configs);
223 };
224
225 const recreateSubChartsWithLayout = (configs: SubChartConfig[]) => {
226 // Suspend updates to prevent MouseManager from iterating over partially deleted subcharts
227 sciChartSurface.suspendUpdates();
228
229 try {
230 // Clear existing subcharts - use the proper removeSubChart function to ensure cleanup
231 const currentIds = Array.from(subChartMap.keys());
232 currentIds.forEach((id) => {
233 removeSubChart(id);
234 });
235
236 // Recreate with proper layout
237 const count = configs.length;
238 if (count === 0) {
239 sciChartSurface.resumeUpdates();
240 return;
241 }
242
243 configs.forEach((config, index) => {
244 // Calculate position: each subchart takes 1/count of the available 80% height
245 // Y position starts at (index/count) * 0.8 and has height of (1/count) * 0.8
246 const yStart = (index / count) * 0.8;
247 const height = (1 / count) * 0.8;
248 const rect = new Rect(0, yStart, 1, height);
249
250 const subChartOptions: I2DSubSurfaceOptions = {
251 id: config.id,
252 position: rect,
253 coordinateMode: ESubSurfacePositionCoordinateMode.Relative,
254 };
255
256 const subChart = SciChartSubSurface.createSubSurface(sciChartSurface, subChartOptions);
257
258 // Create axes for the subchart
259 const subXAxis = new NumericAxis(wasmContext);
260 const subYAxis = new NumericAxis(wasmContext, {
261 growBy: new NumberRange(0.1, 0.1),
262 axisTitle: config.title,
263 axisTitleStyle: { fontSize: 14 },
264 drawMinorGridLines: false,
265 });
266
267 subChart.xAxes.add(subXAxis);
268 subChart.yAxes.add(subYAxis);
269
270 // Create data and series
271 const data = createLineData(config.phase);
272 const dataSeries = new XyDataSeries(wasmContext, {
273 xValues: data.xValues,
274 yValues: data.yValues,
275 });
276
277 const lineSeries = new FastLineRenderableSeries(wasmContext, {
278 dataSeries,
279 strokeThickness: 4,
280 stroke: config.color,
281 opacity: 0.6,
282 });
283
284 // Add to synchronizer and subchart
285 axisSynchroniser.addAxis(subXAxis);
286 subChart.renderableSeries.add(lineSeries);
287
288 // Add modifiers
289 subChart.chartModifiers.add(
290 new ZoomPanModifier(),
291 new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
292 new ZoomExtentsModifier(),
293 new RolloverModifier({ modifierGroup: "one" })
294 );
295
296 // Store reference
297 subChartMap.set(config.id, subChart);
298
299 subChart.zoomExtents();
300 });
301 } finally {
302 sciChartSurface.resumeUpdates();
303 }
304 };
305
306 // Initialize with provided configs
307 initialConfigs.forEach((config) => addSubChart(config));
308
309 sciChartSurface.zoomExtents();
310
311 const manager: SubChartManager = {
312 updateSubCharts,
313 addSubChart,
314 removeSubChart,
315 updateLayout,
316 };
317
318 return { wasmContext, sciChartSurface, manager };
319};
320This example demonstrates how to create a multi-panel chart layout with synchronized subcharts and an overview range selector using SciChart.js within an Angular application. Each subchart displays its own data while remaining fully synchronized through shared zoom and pan interactions.
The chart is hosted inside a standalone Angular component using the scichart-angular integration. A custom initialization function dynamically creates multiple SciChartSubSurface instances, each with its own NumericAxis, FastLineRenderableSeries, and interaction modifiers such as ZoomPanModifier and MouseWheelZoomModifier.
An overview panel is added using a custom SubChartsOverviewModifier, which creates an additional subsurface at the bottom of the chart. This overview aggregates series from all subcharts and applies an OverviewRangeSelectionModifier to synchronize the visible range across every chart.
Dynamic Multi-Chart Layout: Subcharts are automatically positioned and resized based on the number of active charts.
Unified Range Selection: The overview panel provides a single control point for zooming and panning all subcharts simultaneously.
Robust Lifecycle Handling: The example carefully manages subchart creation and deletion to avoid interaction issues, ensuring stable behavior during dynamic updates.
Enterprise-Grade Performance: Leveraging SciChart.js WebAssembly rendering ensures smooth interactivity even with multiple charts and dense datasets.
This example follows recommended patterns for integrating SciChart.js into Angular, including isolating chart initialization logic and using safe update suspension during layout changes. Developers building analytical dashboards or monitoring tools can use this approach as a foundation. See the SciChart Angular Documentation and SubCharts API for further details.

Demonstrates Multiple X & Y Axis on a Angular Chart using SciChart.js. SciChart supports unlimited left, right, top, bottom X, Y axis with configurable alignment and individual zooming, panning

Demonstrates Secondary Y Axis on a Angular Chart using SciChart.js. SciChart supports unlimited, multiple left, right, top, bottom X, Y axis with configurable alignment and individual zooming, panning

Demonstrates how to Zoom, Scale or Pan individual Axis on a Angular Chart with SciChart.js AxisDragModifiers

Demonstrates how to zoom and pan a realtime Angular Chart while it is updating, with SciChart.js ZoomState API

Demonstrates how to use multiple Zoom and Pan Modifiers on a Angular Chart with SciChart.js

Demonstrates how to zoom and pan with an Overview Chart

shows how to load data on zoom/pan and how to create an overview chart for this case.

Explore SciChart's Polar Interactivity Modifiers including zooming, panning, and cursor tracking. Try the demo to trial the Polar Chart Behavior Modifiers.

Demonstrates 64-bit precision Date Axis in SciChart.js handling Nanoseconds to Billions of Years