Realtime Percentage Change using Filter

Demonstrates how to use a ScaleOffsetFilter to convert data to a Percentage Change with realtime updates, using SciChart.js, High Performance JavaScript Charts

Fullscreen

Edit

 Edit

Docs

drawExample.ts

index.tsx

RandomWalkGenerator.ts

theme.ts

Copy to clipboard
Minimise
Fullscreen
1import { appTheme } from "../../../theme";
2import { RandomWalkGenerator } from "../../../ExampleData/RandomWalkGenerator";
3import {
4    SciChartSurface,
5    NumericAxis,
6    NumberRange,
7    EAutoRange,
8    XyDataSeries,
9    XyScaleOffsetFilter,
10    FastLineRenderableSeries,
11    HitTestInfo,
12    XySeriesInfo,
13    SeriesInfo,
14    ZoomPanModifier,
15    ZoomExtentsModifier,
16    RolloverModifier,
17    TextAnnotation,
18    EHorizontalAnchorPoint,
19    EVerticalAnchorPoint,
20    ECoordinateMode,
21    EAnnotationLayer,
22    ENumericFormat,
23} from "scichart";
24
25// Custom formatNumber function to avoid conflicts
26const customFormatNumber = (value: number, format: ENumericFormat, precision: number) => {
27    return value.toFixed(precision);
28};
29
30const getScaleValue = (dataSeries: XyDataSeries, zeroXValue: number) => {
31    const dataLength = dataSeries.count();
32    let zeroIndex = -1;
33    for (let i = 0; i < dataLength; i++) {
34        const xValue = dataSeries.getNativeXValues().get(i);
35        if (xValue >= zeroXValue) {
36            zeroIndex = i;
37            break;
38        }
39    }
40    if (zeroIndex === -1) {
41        return 1;
42    }
43    return 100 / dataSeries.getNativeYValues().get(zeroIndex);
44};
45
46class TransformedSeries extends FastLineRenderableSeries {
47    public originalSeries: XyDataSeries;
48
49    public override getSeriesInfo(hitTestInfo: HitTestInfo): SeriesInfo {
50        const info = new XySeriesInfo(this, hitTestInfo);
51        if (this.originalSeries && info.dataSeriesIndex !== undefined) {
52            info.yValue = this.originalSeries.getNativeYValues().get(info.dataSeriesIndex);
53        }
54        return info;
55    }
56}
57
58export const drawExample = async (rootElement: string | HTMLDivElement, usePercentage: boolean) => {
59    const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
60        theme: appTheme.SciChartJsTheme,
61    });
62
63    const xAxis = new NumericAxis(wasmContext);
64    sciChartSurface.xAxes.add(xAxis);
65
66    const yAxis = new NumericAxis(wasmContext, {
67        autoRange: EAutoRange.Always,
68        labelPostfix: usePercentage ? "%" : "",
69        labelPrecision: usePercentage ? 0 : 1,
70        growBy: new NumberRange(0.1, 0.1),
71    });
72
73    yAxis.labelProvider.formatCursorLabel = (value: number) => customFormatNumber(value, ENumericFormat.Decimal, 1);
74    sciChartSurface.yAxes.add(yAxis);
75
76    const lineSeries = new TransformedSeries(wasmContext, {
77        strokeThickness: 3,
78        stroke: appTheme.VividSkyBlue,
79    });
80    sciChartSurface.renderableSeries.add(lineSeries);
81
82    const data0 = new RandomWalkGenerator().Seed(1337).getRandomWalkSeries(100);
83    const dataSeries1 = new XyDataSeries(wasmContext, { xValues: data0.xValues, yValues: data0.yValues });
84
85    const transform1 = new XyScaleOffsetFilter(dataSeries1, { offset: -100 });
86
87    xAxis.visibleRangeChanged.subscribe(
88        (args) => (transform1.scale = getScaleValue(dataSeries1, args.visibleRange.min))
89    );
90
91    if (usePercentage) {
92        lineSeries.dataSeries = transform1;
93        lineSeries.originalSeries = dataSeries1;
94    } else {
95        lineSeries.dataSeries = dataSeries1;
96    }
97
98    const lineSeries2 = new TransformedSeries(wasmContext, {
99        strokeThickness: 3,
100        stroke: appTheme.VividOrange,
101    });
102    sciChartSurface.renderableSeries.add(lineSeries2);
103
104    const data1 = new RandomWalkGenerator().Seed(0).getRandomWalkSeries(100);
105    const dataSeries2 = new XyDataSeries(wasmContext, { xValues: data1.xValues, yValues: data1.yValues });
106
107    const transform2 = new XyScaleOffsetFilter(dataSeries2, { offset: -100 });
108    xAxis.visibleRangeChanged.subscribe(
109        (args) => (transform2.scale = getScaleValue(dataSeries2, args.visibleRange.min))
110    );
111
112    if (usePercentage) {
113        lineSeries2.dataSeries = transform2;
114        lineSeries2.originalSeries = dataSeries2;
115    } else {
116        lineSeries2.dataSeries = dataSeries2;
117    }
118
119    sciChartSurface.chartModifiers.add(new ZoomPanModifier({ enableZoom: true }));
120    sciChartSurface.chartModifiers.add(new ZoomExtentsModifier());
121    sciChartSurface.chartModifiers.add(new RolloverModifier({ rolloverLineStroke: appTheme.VividTeal }));
122
123    sciChartSurface.annotations.add(
124        new TextAnnotation({
125            text: "Toggle between original data & Percentage Changed on chart",
126            fontSize: 16,
127            textColor: appTheme.ForegroundColor,
128            x1: 0.5,
129            y1: 0,
130            opacity: 0.77,
131            horizontalAnchorPoint: EHorizontalAnchorPoint.Center,
132            xCoordinateMode: ECoordinateMode.Relative,
133            yCoordinateMode: ECoordinateMode.Relative,
134        })
135    );
136
137    const watermarkText = usePercentage ? "Percentage Changed" : "Original Data";
138    sciChartSurface.annotations.add(
139        new TextAnnotation({
140            text: watermarkText,
141            fontSize: 32,
142            textColor: appTheme.ForegroundColor,
143            x1: 0.5,
144            y1: 0.5,
145            opacity: 0.23,
146            horizontalAnchorPoint: EHorizontalAnchorPoint.Center,
147            verticalAnchorPoint: EVerticalAnchorPoint.Center,
148            xCoordinateMode: ECoordinateMode.Relative,
149            yCoordinateMode: ECoordinateMode.Relative,
150            annotationLayer: EAnnotationLayer.BelowChart,
151        })
152    );
153
154    return { sciChartSurface, wasmContext };
155};
156

Percentage Change Example in JavaScript

Overview

This example demonstrates how to transform raw data into a real-time percentage change chart using SciChart.js with JavaScript. It converts random walk data into percentage-based visualizations and offers dynamic interactivity with high performance.

Technical Implementation

The implementation generates two random walk data series that are transformed using the XyScaleOffsetFilter to calculate percentage changes. By subscribing to the x-axis visible range changes, the chart dynamically adjusts the filter scale in real time, as detailed in the Adding Realtime Updates documentation. Furthermore, a custom series extending the fast line renderable series is implemented to override tooltip behavior, providing precise display of original values—techniques similar to those covered in Tutorial 07 - Adding Tooltips and Legends.

Features and Capabilities

The example incorporates real-time data transformation and interactive features such as zooming and panning using modifiers like the ZoomPanModifier. It also provides a toggle mechanism to switch between the original data and its percentage change view, ensuring an engaging and informative user experience. Data simulation is achieved via the RandomWalkGenerator, echoing strategies used in the Realtime JavaScript Chart Performance Demo.

Integration and Best Practices

Focusing on JavaScript, the example emphasizes clean integration with SciChart.js by adhering to event-driven update patterns and performance optimization techniques. By limiting updates to essential parameters and leveraging efficient WebGL rendering, the implementation serves as a robust template for developers aiming to build interactive, high-performance charts with SciChart.js.

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