Angular Overview for SubCharts with Range Selection

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

4

Fullscreen

Edit

 Edit

Docs

drawExample.ts

index.tsx

AxisSynchroniser.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    EXyDirection,
17    CursorModifier,
18    ENumericFormat,
19    SeriesInfo,
20    CursorTooltipSvgAnnotation,
21} from "scichart";
22import { appTheme } from "../../../theme";
23import { SubChartsOverviewModifier } from "./SubChartsOverviewModifier";
24import { AxisSynchroniser } from "../../MultiChart/SyncMultiChart/AxisSynchroniser";
25
26const STREAM_INTERVAL_MS = 1000;
27const POINTS_ON_CHART = 180;
28const OVERVIEW_HEIGHT = 0.2;
29const SUBCHARTS_HEIGHT = 1 - OVERVIEW_HEIGHT;
30
31export type TMarkType = EPerformanceMarkType | string;
32
33export interface SubChartConfig {
34    id: string;
35    phase: number;
36    color: string;
37    title: string;
38}
39
40export interface SubChartManager {
41    updateSubCharts: (configs: SubChartConfig[]) => void;
42    addSubChart: (config: SubChartConfig) => void;
43    removeSubChart: (id: string) => void;
44    updateLayout: () => void;
45}
46
47interface SubChartRuntime {
48    subChart: SciChartSubSurface;
49    dataSeries: XyDataSeries;
50    phase: number;
51    color: string;
52    title: string;
53}
54
55const getYValue = (x: number, phase: number): number =>
56    Math.sin(x * 0.08 + phase) * 0.75 + Math.cos(x * 0.015 + phase * 0.5) * 0.25;
57
58const createLineData = (phase: number, startX: number, count: number) => {
59    const xValues: number[] = [];
60    const yValues: number[] = [];
61    for (let i = 0; i < count; i++) {
62        const x = startX + i;
63        xValues.push(x);
64        yValues.push(getYValue(x, phase));
65    }
66    return { xValues, yValues };
67};
68
69const getTooltipLegendTemplate = (seriesInfos: SeriesInfo[], svgAnnotation: CursorTooltipSvgAnnotation) => {
70    const legendItems = seriesInfos.filter((si) => si.isWithinDataBounds);
71    if (!legendItems.length) {
72        return "<svg width='100%' height='100%'></svg>";
73    }
74
75    const lineHeight = 18;
76    const padding = 8;
77    const titleY = padding + 12;
78    const legendHeight = padding * 2 + 12 + lineHeight * legendItems.length;
79    const currentTime = legendItems[0].formattedXValue;
80
81    let rows = `<text x="${padding}" y="${titleY}" font-size="12" font-family="Verdana" fill="#d4d7dd">Time: ${currentTime}</text>`;
82    legendItems.forEach((seriesInfo, index) => {
83        const y = titleY + 8 + (index + 1) * lineHeight;
84        const seriesColor = seriesInfo.stroke || "#E8C667";
85        rows += `<text x="${padding}" y="${y}" font-size="12" font-family="Verdana" fill="${seriesColor}">${seriesInfo.seriesName}: ${seriesInfo.formattedYValue}</text>`;
86    });
87
88    return `<svg width="100%" height="${legendHeight}">
89        <rect x="1" y="1" width="98%" height="${legendHeight - 2}" rx="4" ry="4" fill="#0D1523CC" stroke="#E8C667AA" stroke-width="1"/>
90        ${rows}
91    </svg>`;
92};
93
94export const drawExample = async (
95    rootElement: string | HTMLDivElement,
96    initialConfigs: SubChartConfig[]
97): Promise<{ wasmContext: any; sciChartSurface: SciChartSurface; manager: SubChartManager }> => {
98    const { wasmContext, sciChartSurface } = await SciChartSurface.create(rootElement);
99
100    const subChartMap = new Map<string, SciChartSubSurface>();
101    const subChartRuntimeMap = new Map<string, SubChartRuntime>();
102
103    let nextStreamX = Math.floor(Date.now() / 1000) + 1;
104    const initialVisibleRange = new NumberRange(nextStreamX - POINTS_ON_CHART, nextStreamX - 1);
105    const axisSynchroniser = new AxisSynchroniser(initialVisibleRange);
106
107    const mainXAxis = new NumericAxis(wasmContext, {
108        id: "mainXAxis",
109        isVisible: false,
110        autoRange: EAutoRange.Always,
111    });
112    const mainYAxis = new NumericAxis(wasmContext, {
113        id: "mainYAxis",
114        isVisible: false,
115        autoRange: EAutoRange.Always,
116    });
117
118    sciChartSurface.xAxes.add(mainXAxis);
119    sciChartSurface.yAxes.add(mainYAxis);
120
121    const overviewModifier = new SubChartsOverviewModifier({
122        overviewPosition: new Rect(0, SUBCHARTS_HEIGHT, 1, OVERVIEW_HEIGHT),
123        isTransparent: true,
124        axisTitle: "Overview - All Charts",
125        labelStyle: {
126            color: "#ffffff80",
127            fontSize: 10,
128        },
129        majorTickLineStyle: {
130            color: "#ffffff80",
131            tickSize: 6,
132            strokeThickness: 1,
133        },
134        yAxisGrowBy: new NumberRange(0.1, 0.1),
135    });
136
137    sciChartSurface.chartModifiers.add(overviewModifier);
138
139    const getCurrentConfigs = (): SubChartConfig[] =>
140        Array.from(subChartRuntimeMap.entries()).map(([id, runtime]) => ({
141            id,
142            phase: runtime.phase,
143            color: runtime.color,
144            title: runtime.title,
145        }));
146
147    const createCursorModifier = () =>
148        new CursorModifier({
149            modifierGroup: "subcharts-cursor",
150            showAxisLabels: true,
151            showTooltip: true,
152            isSvgOnly: false,
153            crosshairStroke: appTheme.ForegroundColor,
154            crosshairStrokeThickness: 1,
155            axisLabelFill: appTheme.ForegroundColor,
156            axisLabelStroke: "#0D1523",
157            tooltipContainerBackground: "#0D1523",
158            tooltipTextStroke: appTheme.ForegroundColor,
159            tooltipLegendTemplate: getTooltipLegendTemplate,
160        });
161
162    const createSubChart = (config: SubChartConfig, rect: Rect) => {
163        const subChartOptions: I2DSubSurfaceOptions = {
164            id: config.id,
165            position: rect,
166            coordinateMode: ESubSurfacePositionCoordinateMode.Relative,
167        };
168
169        const subChart = SciChartSubSurface.createSubSurface(sciChartSurface, subChartOptions);
170
171        const subXAxis = new NumericAxis(wasmContext, {
172            autoRange: EAutoRange.Always,
173            labelFormat: ENumericFormat.Date_HHMMSS,
174            cursorLabelFormat: ENumericFormat.Date_HHMMSS,
175            drawMinorGridLines: false,
176            maxAutoTicks: 6,
177        });
178
179        const subYAxis = new NumericAxis(wasmContext, {
180            autoRange: EAutoRange.Always,
181            growBy: new NumberRange(0.1, 0.1),
182            axisTitle: config.title,
183            axisTitleStyle: { fontSize: 14 },
184            drawMinorGridLines: false,
185        });
186
187        subChart.xAxes.add(subXAxis);
188        subChart.yAxes.add(subYAxis);
189
190        const data = createLineData(config.phase, nextStreamX - POINTS_ON_CHART, POINTS_ON_CHART);
191        const dataSeries = new XyDataSeries(wasmContext, {
192            xValues: data.xValues,
193            yValues: data.yValues,
194            dataSeriesName: config.title,
195        });
196
197        const lineSeries = new FastLineRenderableSeries(wasmContext, {
198            dataSeries,
199            strokeThickness: 3,
200            stroke: config.color,
201            opacity: 0.7,
202        });
203
204        axisSynchroniser.addAxis(subXAxis);
205        subChart.renderableSeries.add(lineSeries);
206
207        subChart.chartModifiers.add(
208            new ZoomPanModifier(),
209            new MouseWheelZoomModifier({ xyDirection: EXyDirection.XDirection }),
210            new ZoomExtentsModifier(),
211            createCursorModifier()
212        );
213
214        subChartMap.set(config.id, subChart);
215        subChartRuntimeMap.set(config.id, {
216            subChart,
217            dataSeries,
218            phase: config.phase,
219            color: config.color,
220            title: config.title,
221        });
222
223        subChart.zoomExtents();
224    };
225
226    const clearAllSubCharts = () => {
227        const currentSubCharts = Array.from(subChartMap.values());
228        currentSubCharts.forEach((subChart) => {
229            const xAxis = subChart.xAxes.get(0);
230            if (xAxis) {
231                axisSynchroniser.removeAxis(xAxis);
232            }
233            sciChartSurface.removeSubChart(subChart);
234        });
235        subChartMap.clear();
236        subChartRuntimeMap.clear();
237    };
238
239    const recreateSubChartsWithLayout = (configs: SubChartConfig[]) => {
240        sciChartSurface.suspendUpdates();
241        try {
242            clearAllSubCharts();
243
244            if (configs.length === 0) {
245                return;
246            }
247
248            configs.forEach((config, index) => {
249                const yStart = (index / configs.length) * SUBCHARTS_HEIGHT;
250                const height = (1 / configs.length) * SUBCHARTS_HEIGHT;
251                createSubChart(config, new Rect(0, yStart, 1, height));
252            });
253        } finally {
254            sciChartSurface.resumeUpdates();
255        }
256    };
257
258    const updateSubCharts = (configs: SubChartConfig[]) => {
259        recreateSubChartsWithLayout(configs);
260    };
261
262    const addSubChart = (config: SubChartConfig) => {
263        if (subChartMap.has(config.id)) {
264            return;
265        }
266        recreateSubChartsWithLayout([...getCurrentConfigs(), config]);
267    };
268
269    const removeSubChart = (id: string) => {
270        if (!subChartMap.has(id)) {
271            return;
272        }
273        recreateSubChartsWithLayout(getCurrentConfigs().filter((cfg) => cfg.id !== id));
274    };
275
276    const updateLayout = () => {
277        recreateSubChartsWithLayout(getCurrentConfigs());
278    };
279
280    recreateSubChartsWithLayout(initialConfigs);
281    sciChartSurface.zoomExtents();
282
283    const streamInterval = window.setInterval(() => {
284        subChartRuntimeMap.forEach((runtime) => {
285            runtime.dataSeries.append(nextStreamX, getYValue(nextStreamX, runtime.phase));
286            const excess = runtime.dataSeries.count() - POINTS_ON_CHART;
287            if (excess > 0) {
288                runtime.dataSeries.removeRange(0, excess);
289            }
290        });
291
292        nextStreamX += 1;
293    }, STREAM_INTERVAL_MS);
294
295    sciChartSurface.addDeletable({
296        delete: () => window.clearInterval(streamInterval),
297    });
298
299    const manager: SubChartManager = {
300        updateSubCharts,
301        addSubChart,
302        removeSubChart,
303        updateLayout,
304    };
305
306    return { wasmContext, sciChartSurface, manager };
307};
308

Overview for SubCharts – Angular

Overview

This 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.

Technical Implementation

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.

Features and Capabilities

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.

Integration and Best Practices

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.

angular Chart Examples & Demos

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

Angular Chart with Multiple X Axes | SciChart.js Demo

Angular Chart with Multiple X Axes

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

Angular Chart with Secondary Y Axes | SciChart.js Demo

Angular Chart with Secondary Y Axes

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

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

Drag Angular Chart Axis to Scale or Pan

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

Zoom and Pan a Realtime Angular Chart | SciChart.js Demo

Zoom and Pan a Realtime Angular Chart

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

Zoom and Pan with Angular Chart multiple Modifiers | SciChart

Zoom and Pan with Angular Chart multiple Modifiers

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

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

Zoom and Pan with Overview Chart

Demonstrates how to zoom and pan with an Overview Chart

Virtualized Angular Charts: Load Data on Zoom/Pan | SciChart

Virtualized Angular Charts: Load Data on Zoom/Pan

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

Angular Polar Modifiers | Polar Interactivity Modifiers

Angular 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 | Angular 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.