Demonstrates how to use multiple styles on a single series on React Charts using SciChart.js, High Performance JavaScript Charts. This uses a RenderDataTransform to split the data so that we can draw the selected points using additional customised drawingProviders. This means that modifiers still see a single series with the original data.
drawExample.ts
index.tsx
theme.ts
1import {
2 BaseRenderDataTransform,
3 XyyPointSeriesResampled,
4 NumberRange,
5 RenderPassData,
6 IPointSeries,
7 XyDataSeries,
8 SciChartSurface,
9 NumericAxis,
10 TrianglePointMarker,
11 XyScatterRenderableSeries,
12 EllipsePointMarker,
13 PointMarkerDrawingProvider,
14 IXyyPointSeries,
15 IPointMarker,
16 FastColumnRenderableSeries,
17 GradientParams,
18 Point,
19 ColumnSeriesDrawingProvider,
20 ZoomExtentsModifier,
21 MouseWheelZoomModifier,
22 DataPointSelectionModifier,
23 LineSeriesDrawingProvider,
24 FastLineRenderableSeries,
25 ILineSeriesDrawingProviderProperties,
26 ELineDrawMode,
27 OhlcPointSeriesResampled,
28 IOhlcPointSeries,
29 RolloverModifier,
30 IPointMetadata,
31 SeriesInfo,
32 NativeTextAnnotation,
33 ECoordinateMode,
34 EHorizontalAnchorPoint,
35 EVerticalAnchorPoint,
36 IStrokePaletteProvider,
37 parseColorToUIntArgb,
38 EStrokePaletteMode,
39 IRenderableSeries,
40 vectorToArrayViewF64,
41} from "scichart";
42import { appTheme } from "../../../theme";
43
44/**
45 * This transform turns xy data into ohlc. Unselected points are in y (close).
46 * Selected points in low for pointmarkers, and selected plus points either side in high for lines.
47 * If you only need this for points or columns, you could transform to Xyy instead
48 */
49class SplitRenderDataTransform extends BaseRenderDataTransform<OhlcPointSeriesResampled> {
50 protected createPointSeries(): OhlcPointSeriesResampled {
51 return new OhlcPointSeriesResampled(this.wasmContext, new NumberRange(0, 0));
52 }
53 protected runTransformInternal(renderPassData: RenderPassData): IPointSeries {
54 const { xValues: oldX, yValues: oldY, indexes: oldI, resampled } = renderPassData.pointSeries;
55 // this.pointSeries is the target. Clear the existing values
56 const { xValues, yValues, highValues, lowValues, indexes } = this.pointSeries;
57 // This shows how to properly handled resampled data, though this is not necessary here.
58 const iStart = resampled ? 0 : renderPassData.indexRange.min;
59 const iEnd = resampled ? oldX.size() - 1 : renderPassData.indexRange?.max;
60 const length = iEnd - iStart + 1;
61 // Since this produces a known number of points we can just fast resize the target pointSeries to the desired length. All this will be overritten
62 xValues.resizeFast(length);
63 yValues.resizeFast(length);
64 highValues.resizeFast(length);
65 lowValues.resizeFast(length);
66 indexes.resizeFast(length);
67 // Create views over the source and target vectors for fast access. These views are only valid as long as there is no memory allocation
68 const oldXView = vectorToArrayViewF64(oldX, this.wasmContext);
69 const oldYView = vectorToArrayViewF64(oldY, this.wasmContext);
70 const oldIndexView = vectorToArrayViewF64(oldI, this.wasmContext);
71 const xView = vectorToArrayViewF64(xValues, this.wasmContext);
72 const yView = vectorToArrayViewF64(yValues, this.wasmContext);
73 const highView = vectorToArrayViewF64(highValues, this.wasmContext);
74 const lowView = vectorToArrayViewF64(lowValues, this.wasmContext);
75 const indexView = vectorToArrayViewF64(indexes, this.wasmContext);
76
77 const ds = this.parentSeries.dataSeries as XyDataSeries;
78 let prevSelected = false;
79 let iOut = 0;
80 for (let i = iStart; i <= iEnd; i++) {
81 const index = resampled ? oldIndexView[i] : i;
82 const md = ds.getMetadataAt(index);
83 xView[iOut] = oldXView[i];
84 indexView[iOut] = oldIndexView[i];
85 let nextSelected = false;
86 if (i < iEnd) {
87 const nextmd = ds.getMetadataAt(index + 1);
88 nextSelected = nextmd.isSelected;
89 }
90 yView[iOut] = md.isSelected ? NaN : oldYView[i];
91 // For pointmarkers we just need the point itself
92 lowView[iOut] = md.isSelected ? oldYView[i] : NaN;
93 // need points either side of the selected value for the line to draw.
94 highView[iOut] = prevSelected || md.isSelected || nextSelected ? oldYView[i] : NaN;
95 prevSelected = md.isSelected;
96 iOut++;
97 }
98 return this.pointSeries;
99 }
100}
101
102export const drawExample = async (rootElement: string | HTMLDivElement) => {
103 // Create a SciChartSurface
104 const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
105 theme: appTheme.SciChartJsTheme,
106 });
107
108 // Create X,Y Axis
109 sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { growBy: new NumberRange(0.05, 0.05) }));
110 sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { growBy: new NumberRange(0, 0.05) }));
111
112 // Column series with different gradient fill for selected columns
113 const xValues = Array.from({ length: 20 }, (x, i) => i);
114 const colyValues = xValues.map((x) => 10 + Math.random() * 40);
115 const colmetadata = xValues.map((x) => ({
116 isSelected: Math.random() < 0.3,
117 }));
118 const columnSeries = new FastColumnRenderableSeries(wasmContext, {
119 fillLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [
120 { color: appTheme.MutedRed, offset: 0 },
121 { color: appTheme.MutedTeal, offset: 1 },
122 ]),
123 dataSeries: new XyDataSeries(wasmContext, {
124 xValues: xValues,
125 yValues: colyValues,
126 metadata: colmetadata,
127 containsNaN: true,
128 }),
129 stroke: "transparent",
130 });
131 // We cannot use a paletteProvider to change a gradient fill, so we have to use a second drawingProvider
132 const selectedColDP = new ColumnSeriesDrawingProvider(
133 wasmContext,
134 columnSeries,
135 // configure this to draw using the selected points
136 (ps) => (ps as IOhlcPointSeries).lowValues
137 );
138 selectedColDP.getProperties = (parentSeries) => {
139 const { stroke, strokeThickness, fill } = parentSeries;
140 return {
141 opacity: 1,
142 // Opacity setting does not currently apply to the gradient colors, so we have to apply it individually
143 fillLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [
144 { color: appTheme.MutedRed + "88", offset: 0 },
145 { color: appTheme.MutedSkyBlue + "88", offset: 1 },
146 ]),
147 stroke,
148 strokeThickness,
149 fill,
150 };
151 };
152 columnSeries.drawingProviders.push(selectedColDP);
153 columnSeries.renderDataTransform = new SplitRenderDataTransform(
154 columnSeries,
155 wasmContext,
156 columnSeries.drawingProviders
157 );
158 sciChartSurface.renderableSeries.add(columnSeries);
159
160 const lineyValues = xValues.map((x) => 30 + x + x * Math.random());
161 const linemetadata = xValues.map((x) => ({
162 isSelected: Math.random() < 0.3,
163 }));
164 // Line series with different pointmarker and dashed line for selected sections
165 const lineSeries = new FastLineRenderableSeries(wasmContext, {
166 dataSeries: new XyDataSeries(wasmContext, {
167 xValues: xValues,
168 yValues: lineyValues,
169 metadata: linemetadata,
170 containsNaN: true,
171 }),
172 pointMarker: new EllipsePointMarker(wasmContext, {
173 width: 14,
174 height: 14,
175 strokeThickness: 0,
176 fill: appTheme.VividSkyBlue,
177 }),
178 stroke: appTheme.VividTeal,
179 strokeThickness: 3,
180 drawNaNAs: ELineDrawMode.DiscontinuousLine,
181 });
182
183 const trianglePM = new TrianglePointMarker(wasmContext, {
184 width: 15,
185 height: 15,
186 strokeThickness: 0,
187 fill: appTheme.VividOrange,
188 });
189 // Additional line drawing for selected segments
190 const selectedLineDP = new LineSeriesDrawingProvider(
191 wasmContext,
192 lineSeries,
193 (ps) => (ps as IOhlcPointSeries).highValues
194 );
195 // Make this drawingProvider used dashed lines
196 selectedLineDP.getProperties = (parentSeries) => {
197 const { stroke, strokeThickness, opacity, isDigitalLine, lineType, drawNaNAs } = parentSeries;
198 return {
199 stroke,
200 strokeThickness,
201 strokeDashArray: [3, 4],
202 isDigitalLine,
203 lineType,
204 drawNaNAs,
205 containsNaN: true,
206 } as ILineSeriesDrawingProviderProperties;
207 };
208 // Add this as the first drawingProviders so it draws behind all pointmarkers
209 lineSeries.drawingProviders.unshift(selectedLineDP);
210
211 // Additional point drawing for selecetd points
212 const triangleDP = new PointMarkerDrawingProvider(
213 wasmContext,
214 lineSeries,
215 (ps) => (ps as IOhlcPointSeries).lowValues
216 );
217 triangleDP.getProperties = (series) => {
218 return { pointMarker: trianglePM as IPointMarker };
219 };
220 lineSeries.drawingProviders.push(triangleDP);
221
222 // Apply the transform to all the drawingProviders
223 lineSeries.renderDataTransform = new SplitRenderDataTransform(lineSeries, wasmContext, lineSeries.drawingProviders);
224 sciChartSurface.renderableSeries.add(lineSeries);
225
226 sciChartSurface.annotations.add(
227 new NativeTextAnnotation({
228 xCoordinateMode: ECoordinateMode.Pixel,
229 yCoordinateMode: ECoordinateMode.Pixel,
230 x1: 20,
231 y1: 20,
232 horizontalAnchorPoint: EHorizontalAnchorPoint.Left,
233 verticalAnchorPoint: EVerticalAnchorPoint.Top,
234 text: "Selected points are styled differently. Click and drag to change the selection",
235 textColor: appTheme.ForegroundColor,
236 fontSize: 16,
237 opacity: 0.77,
238 })
239 );
240
241 // Optional: Add Interactivity Modifiers
242 sciChartSurface.chartModifiers.add(
243 new DataPointSelectionModifier({
244 allowClickSelect: true,
245 onSelectionChanged: (args) => {
246 lineSeries.renderDataTransform.requiresTransform = true;
247 columnSeries.renderDataTransform.requiresTransform = true;
248 },
249 })
250 );
251 // sciChartSurface.chartModifiers.add(new RolloverModifier({
252 // tooltipDataTemplate: (seriesInfo: SeriesInfo) => {
253 // const vals: string[] = [];
254 // vals.push(`X ${seriesInfo.formattedXValue}`);
255 // vals.push(`Y ${seriesInfo.formattedYValue}`);
256 // vals.push(`selected ${(seriesInfo.pointMetadata as IPointMetadata).isSelected}`);
257 // return vals;
258 // }
259 // }));
260
261 sciChartSurface.zoomExtents();
262 return { sciChartSurface, wasmContext };
263};
264This example demonstrates how to create a multi-style series in SciChart.js using React. It shows how to display charts where selected data points are styled differently from the rest, by leveraging a custom RenderDataTransform (see SplitRenderDataTransform) and multiple drawing providers. The example uses high-performance series such as FastLineRenderableSeries and FastColumnRenderableSeries, and integrates interactive modifiers like DataPointSelectionModifier to enable dynamic selection.
The core technical concept is the use of a custom RenderDataTransform to split the original data into segments that can be rendered with multiple visual styles. This is achieved by processing each data point and, based on custom metadata (i.e. whether metadata.isSelected is true), assigning different values for drawing providers. The transform is applied to both the line and column series to determine which segments should display different styles. For more details, see the RenderDataTransforms API Documentation.
The example highlights several advanced features:
GradientParams API to differentiate between selected and unselected columns. Learn more from the GradientParams TypeDoc API.DataPointSelectionModifier allows users to click and drag to change the selection on the chart, which then triggers a re-transform of the render data. This interactivity is further detailed in the Data Point Selection documentation example.This example is fully integrated with React through the <SciChartReact/> component. The implementation follows best practices for React integration by encapsulating the chart initialization within a dedicated React component and leveraging the WebAssembly wasmContext for optimal performance. Developers can explore more about efficient React integration in the React Charts with SciChart.js: Introducing “SciChart React”. Additionally, performance is optimized by using fast-rendering series and handling data updates efficiently with the render data transform.
This comprehensive approach combining real-time data transformation, custom drawing customization, and interactive components provides a robust template for developers looking to create dynamic and performant charts using SciChart.js in React.

Demonstrates how to create a React Chart with background image using transparency in SciChart.js

Demonstrates how to style a React Chart entirely in code with SciChart.js themeing API

Demonstrates our Light and Dark Themes for React Charts with SciChart.js ThemeManager API

Demonstrates how to create a Custom Theme for a SciChart.js React Chart using our Theming API

Demonstrates per-point coloring in JavaScript chart types with SciChart.js PaletteProvider API

Demonstrates the different point-marker types for React Scatter charts (Square, Circle, Triangle and Custom image point-marker)

Demonstrates dashed line series in React Charts with SciChart.js

Show data labels on React Chart. Get your free demo now.

Demonstrates how to use a RenderDataTransform to split lines into multiple segments so they can be individually colored according to thresholds

Demonstrates chart title with different position and alignment options

The React Order of Rendering example gives you full control of the draw order of series and annotations for charts. Try SciChart's advanced customizations.