React Overview for SubCharts with Range Selection

Demonstrates how to create multiple synchronized subcharts with an interactive overview in a React 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 – React

Overview

This example demonstrates how to implement a multi-subchart layout with a shared overview in a React application using SciChart.js. Multiple vertically stacked charts are rendered within a single SciChartSurface, each synchronized on the X-axis and controlled through an interactive overview panel.

Technical Implementation

The chart is initialized via the <SciChartReact /> component, which invokes a custom drawExample function. This function dynamically creates several SciChartSubSurface instances, each configured with its own axes, renderable series, and interaction modifiers. An AxisSynchroniser ensures all subcharts maintain the same visible X-range.

The overview functionality is encapsulated in a reusable SubChartsOverviewModifier, which listens for subchart lifecycle events and mirrors their renderable series into an overview subsurface. The OverviewRangeSelectionModifier allows users to control zoom and pan interactions across all subcharts from a single control surface.

Features and Capabilities

React-Friendly Lifecycle Management: Subcharts are safely created and destroyed without React reconciliation conflicts by leveraging SciChart’s internal update suspension mechanisms.

Centralized Zoom Control: Users can zoom and pan all subcharts simultaneously using either direct mouse interaction or the overview range selector.

Reusable Overview Modifier: The overview logic is encapsulated in a custom chart modifier, making it easy to reuse across different dashboards or chart configurations.

High-Performance Real-Time Charts: The example showcases how SciChart.js integrates seamlessly into React while maintaining WebAssembly-powered performance.

Integration and Best Practices

This approach follows best practices for integrating SciChart.js into React by isolating chart creation logic from React rendering. Developers can extend this pattern to support real-time streaming data, dynamic chart layouts, or advanced dashboard interactions. For more guidance, see Creating a SciChart React Component and the React Charts with SciChart.js guide.

react Chart Examples & Demos

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

React Chart with Multiple X Axes | React Charts | SciChart.js

React Chart with Multiple X Axes

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

React Chart with Secondary Y Axes | SciChart.js Demo

React Chart with Secondary Y Axes

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

Drag React Chart Axis to Scale or Pan | SciChart.js Demo

Drag React Chart Axis to Scale or Pan

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

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

Zoom and Pan a Realtime React Chart

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

Zoom and Pan with React Chart multiple Modifiers | SciChart

Zoom and Pan with React Chart multiple Modifiers

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

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

Zoom and Pan with Overview Chart

Demonstrates how to zoom and pan with an Overview Chart

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

Virtualized React Charts: Load Data on Zoom/Pan

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

React Polar Modifiers | Polar Interactivity Modifiers

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