JavaScript Overview for SubCharts with Range Selection

Demonstrates how to create multiple synchronized subcharts with an interactive overview using SciChart.js

4

Fullscreen

Edit

 Edit

Docs

drawExample.ts

index.html

AxisSynchroniser.ts

vanilla.ts

theme.ts

SubChartsOverviewModifier.ts

Copy to clipboard
Minimise
Fullscreen
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};
320

Overview for SubCharts – JavaScript

Overview

This example demonstrates how to build a multi-panel chart layout in JavaScript using SciChart.js, where several subcharts are vertically stacked and synchronized through a shared interactive overview. The overview chart displays aggregated data from all subcharts and allows users to zoom and pan all charts simultaneously using a draggable range selector.

Technical Implementation

The chart is initialized by creating a single SciChartSurface, then dynamically adding multiple SciChartSubSurface instances. Each subchart owns its own NumericAxis pair and FastLineRenderableSeries, while an AxisSynchroniser keeps all X-axes aligned. Data is generated using XyDataSeries, simulating phase-shifted sine waves.

An interactive overview is implemented using a custom SubChartsOverviewModifier. This modifier creates an additional subsurface occupying the bottom 20% of the chart, clones renderable series from each subchart, and attaches an OverviewRangeSelectionModifier to control the visible range across all subcharts.

Features and Capabilities

Dynamic SubChart Management: Subcharts can be added, removed, or fully recreated at runtime without triggering interaction issues, using safe lifecycle management with suspendUpdates().

Synchronized Zooming and Panning: Mouse wheel zoom, drag panning, and programmatic zooming are automatically synchronized across all subcharts via a shared X-axis range.

Interactive Overview Panel: The overview aggregates all visible data and provides a draggable selection window that controls the visible range of every subchart simultaneously.

High-Performance Rendering: All charts leverage SciChart.js WebAssembly rendering, ensuring smooth performance even with multiple charts and large datasets.

Integration and Best Practices

This example demonstrates best practices for managing multiple subcharts using SciChartSubSurface, including safe cleanup, axis synchronization, and modifier coordination. Developers can extend this pattern to build advanced dashboards, signal analysis tools, or monitoring applications. For more information, see the SubSurfaces API and Performance Tips & Tricks.

javascript Chart Examples & Demos

See Also: Zoom and Pan a Chart (9 Demos)

JavaScript Chart with Multiple X Axes | SciChart.js Demo

JavaScript Chart with Multiple X Axes

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

JavaScript Chart with Secondary Y Axes | SciChart.js Demo

JavaScript Chart with Secondary Y Axes

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

Drag JavaScript Chart Axis to Scale or Pan | SciChart.js

Drag JavaScript Chart Axis to Scale or Pan

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

Zoom and Pan a Realtime JavaScript Chart | SciChart.js

Zoom and Pan a Realtime JavaScript Chart

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

Zoom and Pan with JavaScript Chart multiple Modifiers

Zoom and Pan with JavaScript Chart multiple Modifiers

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

Zoom and Pan with Overview Chart | Javascript Charts | SciChart.js

Zoom and Pan with Overview Chart

Demonstrates how to zoom and pan with an Overview Chart

Virtualized JavaScript Charts: Load Data on Zoom/Pan

Virtualized JavaScript Charts: Load Data on Zoom/Pan

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

JavaScript Polar Modifiers | Polar Interactivity Modifiers

JavaScript Polar Modifiers | Polar Interactivity Modifiers Demo

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

NEW!
High Precision Date Axis | Javascript Charts | SciChart.js Demo

High Precision Date Axis

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

SciChart Ltd, 16 Beaufort Court, Admirals Way, Docklands, London, E14 9XL.