Creates a React Vector Field using our FastLineSegmentRenderableSeries paired with a custom PaletteProvider and optional arrow heads made with FastTriangleRenderableSeries
import {
NumberRange,
SciChartSurface,
NumericAxis,
CentralAxesLayoutManager,
FastLineSegmentRenderableSeries,
IStrokePaletteProvider,
EStrokePaletteMode,
parseColorToUIntArgb,
IRenderableSeries,
TPaletteProviderDefinition,
EPaletteProviderType,
XyDataSeries,
ZoomPanModifier,
ZoomExtentsModifier,
MouseWheelZoomModifier,
FastTriangleRenderableSeries,
ETriangleSeriesDrawMode,
CursorModifier,
TCursorTooltipDataTemplate,
SeriesInfo,
XySeriesInfo,
} from "scichart";
import { appTheme } from "./appTheme";
const lineEndColor = appTheme.VividOrange;
class LineSegmentPaletteProvider implements IStrokePaletteProvider {
public readonly strokePaletteMode = EStrokePaletteMode.GRADIENT;
private readonly palettedStart = parseColorToUIntArgb(appTheme.VividBlue);
private readonly palettedEnd = parseColorToUIntArgb(lineEndColor);
public onAttached(parentSeries: IRenderableSeries): void {}
public onDetached(): void {}
public overrideStrokeArgb(xValue: number, yValue: number, index: number): number {
return index % 2 === 0 ? this.palettedStart : this.palettedEnd;
}
public toJSON(): TPaletteProviderDefinition {
return { type: EPaletteProviderType.Custom, customType: "MyPaletteProvider" };
}
}
/**
* Format function
*/
const fFn = (v: number) => v.toFixed(1);
// Tooltip Data Template
const tooltipDataTemplate: TCursorTooltipDataTemplate = (seriesInfos: SeriesInfo[]) => {
const valuesWithLabels: string[] = [];
seriesInfos.forEach((si) => {
const xySI = si as XySeriesInfo;
if (xySI.isWithinDataBounds) {
if (!isNaN(xySI.yValue) && xySI.isHit) {
valuesWithLabels.push(
`start (${fFn(xySI.xValue)},${fFn(xySI.yValue)}) end (${fFn(xySI.point2xValue)},${fFn(xySI.point2yValue)})`
);
}
}
});
return valuesWithLabels;
};
function addArrowheads(xValues: number[], yValues: number[], arrowLength = 0.36, arrowAngle = Math.PI / 12) {
const arrows = [];
for (let i = 0; i < xValues.length; i += 2) {
// Line start and end
const x1 = xValues[i];
const y1 = yValues[i];
const x2 = xValues[i + 1];
const y2 = yValues[i + 1];
// Direction of the line
const dx = x2 - x1;
const dy = y2 - y1;
const lineAngle = Math.atan2(dy, dx);
// Arrowhead points
const angle1 = lineAngle + Math.PI - arrowAngle;
const angle2 = lineAngle + Math.PI + arrowAngle;
const arrowPoint1 = [x2 + arrowLength * Math.cos(angle1), y2 + arrowLength * Math.sin(angle1)];
const arrowPoint2 = [x2 + arrowLength * Math.cos(angle2), y2 + arrowLength * Math.sin(angle2)];
// Arrowhead: [tip, base1, base2]
arrows.push([
[x2, y2], // Tip of the arrow
arrowPoint1, // First base point
arrowPoint2, // Second base point
]);
}
return arrows;
}
export const drawExample = async (rootElement: string | HTMLDivElement) => {
// Create a SciChartSurface
const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
theme: appTheme.SciChartJsTheme,
});
// configure central axes
const layoutManager = new CentralAxesLayoutManager();
sciChartSurface.layoutManager = layoutManager;
const xMin = -15;
const xMax = 15;
const yMin = -10;
const yMax = 10;
// Slightly crop the sparse outer arrows while leaving more of the field visible.
const growBy = new NumberRange(-0.05, -0.05);
const xAxis = new NumericAxis(wasmContext, {
axisBorder: { color: "white", borderBottom: 1 },
drawMajorBands: false,
drawMajorGridLines: false,
drawMinorGridLines: false,
growBy,
});
sciChartSurface.xAxes.add(xAxis);
const yAxis = new NumericAxis(wasmContext, {
axisBorder: { color: "white", borderRight: 1 },
drawMajorBands: false,
drawMajorGridLines: false,
drawMinorGridLines: false,
growBy,
});
sciChartSurface.yAxes.add(yAxis);
const multiplier = 0.01;
const linesXValues: number[] = [];
const linesYValues: number[] = [];
for (let x = xMin; x <= xMax; x++) {
for (let y = yMin; y <= yMax; y++) {
// start point
linesXValues.push(x);
linesYValues.push(y);
// end point
const xEnd = x + (x * x - y * y - 4) * multiplier;
const yEnd = y + 2 * x * y * multiplier;
linesXValues.push(xEnd);
linesYValues.push(yEnd);
}
}
const lineSegmentSeries = new FastLineSegmentRenderableSeries(wasmContext, {
dataSeries: new XyDataSeries(wasmContext, {
xValues: linesXValues,
yValues: linesYValues,
}),
strokeThickness: 3,
paletteProvider: new LineSegmentPaletteProvider(),
});
const arrowheads = addArrowheads(linesXValues, linesYValues);
const arrowheadsXvalues = arrowheads.flat().map((d) => d[0]);
const arrowheadsYvalues = arrowheads.flat().map((d) => d[1]);
const polygonSeries = new FastTriangleRenderableSeries(wasmContext, {
dataSeries: new XyDataSeries(wasmContext, {
xValues: arrowheadsXvalues,
yValues: arrowheadsYvalues,
}),
isDigitalLine: false,
opacity: 1,
fill: lineEndColor,
drawMode: ETriangleSeriesDrawMode.List, // Polygon / List / Strip
});
sciChartSurface.renderableSeries.add(lineSegmentSeries);
sciChartSurface.renderableSeries.add(polygonSeries);
sciChartSurface.chartModifiers.add(new ZoomPanModifier());
sciChartSurface.chartModifiers.add(new ZoomExtentsModifier());
sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier());
sciChartSurface.chartModifiers.add(new CursorModifier({ showTooltip: true, tooltipDataTemplate }));
return { sciChartSurface, wasmContext };
};
This React implementation of a Vector Field Chart wraps the core SciChart.js visualization in a reusable SciChartReact component. It demonstrates efficient integration of mathematical vector field visualization in a React application.
The chart configuration from drawExample.ts is passed via the initChart prop. React manages the component lifecycle while SciChart handles WebGL rendering. The solution uses XyDataSeries for vector data and implements a custom tooltip template through CursorModifier.
The component maintains all JavaScript version features including dynamic vector generation, gradient-colored segments, and interactive zoom/pan. The React wrapper adds responsive resizing and clean unmounting through SciChartReact's built-in lifecycle management.
This pattern follows React best practices by separating visualization logic (drawExample) from presentation (ChartComponent). For advanced use cases, consider using React context for theme propagation or memoizing calculation-heavy operations.








































