Tooltips in SciChart.js 3D are performed by the TooltipModifier3D. This is a ChartModifierBase3D derived type which executes on touch over the data point and shows tooltips for the data-points under the mouse.
Declaring a TooltipModifier3D
Declaring a TooltipModifier3D is as simple as adding one to the SciChart3DSurface.chartModifiers property. This can be done as a single modifier, or as part of a group.
This results in the following behaviour added to the chart.
<div class="wrapper"> <div id="scichart-root" ></div> <div class="titleWrapper"> <p class="title">SciChart.js 3D Chart Example</p> <p class="subTitle">Hover the points to see tooltips</p> </div> </div>
body { margin: 0; font-family: Arial; } .wrapper { width: 100%; height: 100vh; position: relative; } #scichart-root { width: 100%; height: 100%; position: relative; } .titleWrapper { position: absolute; width: 100%; top: 35%; text-align: center; pointer-events: none; color: #ffffff77 } .title { font-size: 20px; } .subTitle { font-size: 16px; }
async function tooltips3D(divElementId) { const { SciChart3DSurface, NumericAxis3D, Vector3, SciChartJsNavyTheme, EAutoRange, NumberRange, SpherePointMarker3D, ScatterRenderableSeries3D, XyzDataSeries3D, SurfaceMeshRenderableSeries3D, UniformGridDataSeries3D, GradientColorPalette, PointLineRenderableSeries3D, EDrawMeshAs, TooltipModifier3D, EMeshPaletteMode } = SciChart; // or, for npm, import { SciChart3DSurface, ... } from "scichart" // Create a SciChart3DSurface in the host <div id=".." /> const { wasmContext, sciChart3DSurface } = await SciChart3DSurface.create(divElementId, { theme: new SciChartJsNavyTheme(), worldDimensions: new Vector3(300, 200, 300), cameraOptions: { position: new Vector3(-300, 300, 300), target: new Vector3(0, 50, 0), } }); sciChart3DSurface.xAxis = new NumericAxis3D(wasmContext, { axisTitle: "X Axis", visibleRange: new NumberRange(0, 10), autoRange: EAutoRange.Never }); sciChart3DSurface.yAxis = new NumericAxis3D(wasmContext, { axisTitle: "Y Axis", visibleRange: new NumberRange(0, 10), autoRange: EAutoRange.Never }); sciChart3DSurface.zAxis = new NumericAxis3D(wasmContext, { axisTitle: "Z Axis", visibleRange: new NumberRange(0, 10), autoRange: EAutoRange.Never }); sciChart3DSurface.renderableSeries.add( new ScatterRenderableSeries3D(wasmContext, { pointMarker: new SpherePointMarker3D(wasmContext, { size: 10, fill: "#FF6600" }), dataSeries: new XyzDataSeries3D(wasmContext, { xValues: [4, 4.1, 4.3], yValues: [4, 4.1, 4.3], zValues: [4, 4.1, 4.3], dataSeriesName: "Orange" }) }) ); sciChart3DSurface.renderableSeries.add( new ScatterRenderableSeries3D(wasmContext, { pointMarker: new SpherePointMarker3D(wasmContext, { size: 10, fill: "#33AAFF" }), dataSeries: new XyzDataSeries3D(wasmContext, { xValues: [5, 5.1, 5.3], yValues: [5, 5.1, 5.3], zValues: [5, 5.1, 5.3], dataSeriesName: "Blue" }) }) ); sciChart3DSurface.renderableSeries.add( new ScatterRenderableSeries3D(wasmContext, { pointMarker: new SpherePointMarker3D(wasmContext, { size: 10, fill: "#00FF00" }), dataSeries: new XyzDataSeries3D(wasmContext, { xValues: [6, 6.1, 6.3], yValues: [6, 6.1, 6.3], zValues: [6, 6.1, 6.3], dataSeriesName: "Green" }) }) ); sciChart3DSurface.renderableSeries.add( new SurfaceMeshRenderableSeries3D(wasmContext, { dataSeries: new UniformGridDataSeries3D(wasmContext, { xStart: 5, zStart: 5, yValues: [ [0.1, 0.4, 0.4, 0.2, 0.8], [0.6, 0.4, 0.6, 0.1, 0.7], [0.2, 0.4, 0.3, 0.4, 0.0], [0.6, 0.4, 0.6, 0.1, 0.7], [0.1, 0.4, 0.4, 0.2, 0.8], ], dataSeriesName: "Surface mesh" }), meshColorPalette: new GradientColorPalette(wasmContext, { gradientStops: [ {offset: 1, color: "#EC0F6C"}, // yValues >= maximum mapped to this color {offset: 0.55, color: "#F48420"}, {offset: 0.3, color: "#67BDAF"}, {offset: 0.2, color: "#50C7E0"}, {offset: 0.1, color: "#264B93"}, {offset: 0, color: "#14233C"} // yValues <= minimum mapped to this color ] }), minimum: 0, maximum: 1, opacity: 0.77, drawSkirt: false, stroke: "White", strokeThickness: 1.5, lightingFactor: 0.2, meshPaletteMode: EMeshPaletteMode.HEIGHT_MAP_SOLID_CELLS, drawMeshAs: EDrawMeshAs.SOLID_WIREFRAME }) ); sciChart3DSurface.renderableSeries.add( new PointLineRenderableSeries3D(wasmContext, { stroke: "#E4F5FC", dataSeries: new XyzDataSeries3D(wasmContext, { dataSeriesName: "PointLine 3D", xValues: [0, 0, 0, 0, 0], yValues: [6, 6.1, 6.3, 5.5, 6.0], zValues: [2, 4, 6, 8, 10] }), pointMarker: new SpherePointMarker3D(wasmContext, { size: 10, fill: "#00FF00" }) }) ); // #region ExampleA // Declare a tooltip and add to the chart like this. // Optional parameters help define tooltip operation const tooltipModifier = new TooltipModifier3D({ isCrosshairVisible: true, isTooltipVisible: true, crosshairStroke: "#83D2F5", crosshairStrokeThickness: 3, tooltipContainerBackground: "#537ABD", tooltipTextStroke: "White", tooltipLegendOffsetX: 10, tooltipLegendOffsetY: 10 }); sciChart3DSurface.chartModifiers.add(tooltipModifier); // #endregion }; tooltips3D("scichart-root");
Styling the Tooltip Output
Properties which affect Tooltip style
Some simple properties which affect the tooltip style are:
Property | Description |
isCrosshairVisible | When true (default), a crosshair is drawn from the hovered datapoint to the far axis wall |
crosshairStroke | The stroke color as a Hex code of the crosshair line |
crosshairStrokeThickness | The stroke thickness of the crosshair line |
tooltipContainerBackground | The background color of the tooltip container as a Hex code |
tooltipLegendOffsetX / Y | Offset in pixels of the tooltip from the hovered datapoint |
tooltipTextStroke | The text color on the tooltip |
For further customisation of the tooltip content & container, read on.
Tooltip Text Formatting
Modifying the Tooltip Content
You can modify the content output by tooltip via the TooltipModifier3D.tooltipDataTemplate property. This accepts a function with SeriesInfo3D and TooltipSvgAnnotation3D arguments where you can access data about the series that was hit.
Here's an example:
This results in the following output.
<div class="wrapper"> <div id="scichart-root" ></div> <div class="titleWrapper"> <p class="title">SciChart.js 3D Chart Example</p> <p class="subTitle">Hover the points to see tooltips</p> </div> </div>
body { margin: 0; font-family: Arial; } .wrapper { width: 100%; height: 100vh; position: relative; } #scichart-root { width: 100%; height: 100%; position: relative; } .titleWrapper { position: absolute; width: 100%; top: 35%; text-align: center; pointer-events: none; color: #ffffff77 } .title { font-size: 20px; } .subTitle { font-size: 16px; }
const generateData = (index) => { const gaussianRandom = (mean, stdev) => { const u = 1 - Math.random(); // Converting [0,1) to (0,1] const v = Math.random(); const z = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v ); // Transform to the desired mean and standard deviation: return z * stdev + mean; }; const xValues = []; const yValues = []; const zValues = []; for (let i = 0; i < 30; i++) { xValues.push(i*0.1); yValues.push(gaussianRandom(0, 1)); zValues.push(index); } return { xValues, yValues, zValues }; } async function tooltips3DCustomisation(divElementId) { const { SciChart3DSurface, NumericAxis3D, Vector3, SciChartJsNavyTheme, NumberRange, XyzDataSeries3D, PointLineRenderableSeries3D, TooltipModifier3D, EllipsePointMarker3D, uintArgbColorLerp, parseColorToUIntArgb, parseArgbToHtmlColor } = SciChart; // or, for npm, import { SciChart3DSurface, ... } from "scichart" // Create a SciChart3DSurface in the host <div id=".." /> const { wasmContext, sciChart3DSurface } = await SciChart3DSurface.create(divElementId, { theme: new SciChartJsNavyTheme(), worldDimensions: new Vector3(300, 200, 300), cameraOptions: { position: new Vector3(-300, 300, 300), target: new Vector3(0, 50, 0), } }); const growBy = new NumberRange(0.2, 0.2); sciChart3DSurface.xAxis = new NumericAxis3D(wasmContext, { axisTitle: "Frequency (Hz)", growBy }); sciChart3DSurface.yAxis = new NumericAxis3D(wasmContext, { axisTitle: "Power (dB)" }); sciChart3DSurface.zAxis = new NumericAxis3D(wasmContext, { axisTitle: "Time (s)", growBy }); // returns data in arrays of numbers e.g. xValues = [0,1,2,3,4], yValues = [0,1,2,3,4], zValues = [0,1,2,3,4] const { xValues, yValues, zValues } = generateData(1); // Add a PointLineRenderableSeries3D sciChart3DSurface.renderableSeries.add(new PointLineRenderableSeries3D(wasmContext, { dataSeries: new XyzDataSeries3D(wasmContext, { xValues, yValues, zValues, dataSeriesName: "Series A" }), opacity: 0.9, stroke: "#EC0F6C", strokeThickness: 3, pointMarker: new EllipsePointMarker3D(wasmContext, { size: 3 }) })); // Repeat 2x const dataset1 = generateData(2); const colorHigh = parseColorToUIntArgb("#EC0F6C"); const colorLow = parseColorToUIntArgb("#30BC9A"); const yMin = Math.min(...yValues); const yMax = Math.max(...yValues); const metadata = dataset1.yValues.map((y, i) => { // interpolate y between colorLow and colorHigh using the helper function uintArgbColorLerp const t = (y - yMin) / (yMax - yMin); const color = uintArgbColorLerp(colorLow, colorHigh, t); return { vertexColor: color, customString: `Custom string ${i}` }; }); sciChart3DSurface.renderableSeries.add(new PointLineRenderableSeries3D(wasmContext, { dataSeries: new XyzDataSeries3D(wasmContext, { xValues: dataset1.xValues, yValues: dataset1.yValues, zValues: dataset1.zValues, metadata, dataSeriesName: "Series B" }), opacity: 0.9, stroke: "#50C7E0", strokeThickness: 3, pointMarker: new EllipsePointMarker3D(wasmContext, { size: 3 }) })); const dataset2 = generateData(3); sciChart3DSurface.renderableSeries.add(new PointLineRenderableSeries3D(wasmContext, { dataSeries: new XyzDataSeries3D(wasmContext, { xValues: dataset2.xValues, yValues: dataset2.yValues, zValues: dataset2.zValues, dataSeriesName: "Series C" }), opacity: 0.9, stroke: "#F48420", strokeThickness: 3, pointMarker: new EllipsePointMarker3D(wasmContext, { size: 3 }) })); // #region ExampleA // Declare a tooltip and add to the chart const tooltipModifier = new TooltipModifier3D({ crosshairStroke: "#83D2F5", crosshairStrokeThickness: 3, tooltipContainerBackground: "#537ABD", tooltipTextStroke: "White", tooltipLegendOffsetX: 10, tooltipLegendOffsetY: 10 }); sciChart3DSurface.chartModifiers.add(tooltipModifier); // Customize the tooltip content tooltipModifier.tooltipDataTemplate = (seriesInfo, svgAnnotation) => { // Create an array to hold strings (lines) to show in the tooltip const valuesWithLabels = []; if (seriesInfo && seriesInfo.isHit) { // You can access the renderableSeries which was hit via the seriesInfo const renderableSeries = seriesInfo.renderableSeries; // And the parent Chart from that const parentSurface = renderableSeries.parentSurface; // Push lines to the array to display in the tooltip valuesWithLabels.push(`dataSeriesName: "${seriesInfo.dataSeriesName}"`); valuesWithLabels.push(` ${parentSurface.xAxis.axisTitle}: ${seriesInfo.xValue.toFixed(2)}`); valuesWithLabels.push(` ${parentSurface.yAxis.axisTitle}: ${seriesInfo.yValue.toFixed(2)}`); valuesWithLabels.push(` ${parentSurface.zAxis.axisTitle}: ${seriesInfo.zValue.toFixed(2)}`); // access the metadata (if exists)". Any JS object on the data-points can be accessed // in tooltips const md = seriesInfo.pointMetadata; if (md) { valuesWithLabels.push(` Metadata: "${md.customString}"`); } } return valuesWithLabels; } // #endregion }; tooltips3DCustomisation("scichart-root");
Modifying the Tooltip Container
The container of the tooltip can be modified as well. Extending the example above further, we override TooltipModifier3D.tooltipSvgTemplate to customize the background/foreground color before rendering the tooltip.
This results in the following output:
<div class="wrapper"> <div id="scichart-root" ></div> <div class="titleWrapper"> <p class="title">SciChart.js 3D Chart Example</p> <p class="subTitle">Hover the points to see tooltips</p> </div> </div>
body { margin: 0; font-family: Arial; } .wrapper { width: 100%; height: 100vh; position: relative; } #scichart-root { width: 100%; height: 100%; position: relative; } .titleWrapper { position: absolute; width: 100%; top: 35%; text-align: center; pointer-events: none; color: #ffffff77 } .title { font-size: 20px; } .subTitle { font-size: 16px; }
const generateData = (index) => { const gaussianRandom = (mean, stdev) => { const u = 1 - Math.random(); // Converting [0,1) to (0,1] const v = Math.random(); const z = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v ); // Transform to the desired mean and standard deviation: return z * stdev + mean; }; const xValues = []; const yValues = []; const zValues = []; for (let i = 0; i < 30; i++) { xValues.push(i*0.1); yValues.push(gaussianRandom(0, 1)); zValues.push(index); } return { xValues, yValues, zValues }; } async function tooltips3DCustomisation(divElementId) { const { SciChart3DSurface, NumericAxis3D, Vector3, SciChartJsNavyTheme, NumberRange, XyzDataSeries3D, PointLineRenderableSeries3D, TooltipModifier3D, EllipsePointMarker3D, uintArgbColorLerp, parseColorToUIntArgb, parseArgbToHtmlColor } = SciChart; // or, for npm, import { SciChart3DSurface, ... } from "scichart" // Create a SciChart3DSurface in the host <div id=".." /> const { wasmContext, sciChart3DSurface } = await SciChart3DSurface.create(divElementId, { theme: new SciChartJsNavyTheme(), worldDimensions: new Vector3(300, 200, 300), cameraOptions: { position: new Vector3(-300, 300, 300), target: new Vector3(0, 50, 0), } }); const growBy = new NumberRange(0.2, 0.2); sciChart3DSurface.xAxis = new NumericAxis3D(wasmContext, { axisTitle: "Frequency (Hz)", growBy }); sciChart3DSurface.yAxis = new NumericAxis3D(wasmContext, { axisTitle: "Power (dB)" }); sciChart3DSurface.zAxis = new NumericAxis3D(wasmContext, { axisTitle: "Time (s)", growBy }); // returns data in arrays of numbers e.g. xValues = [0,1,2,3,4], yValues = [0,1,2,3,4], zValues = [0,1,2,3,4] const { xValues, yValues, zValues } = generateData(1); // Add a PointLineRenderableSeries3D sciChart3DSurface.renderableSeries.add(new PointLineRenderableSeries3D(wasmContext, { dataSeries: new XyzDataSeries3D(wasmContext, { xValues, yValues, zValues, dataSeriesName: "Series A" }), opacity: 0.9, stroke: "#EC0F6C", strokeThickness: 3, pointMarker: new EllipsePointMarker3D(wasmContext, { size: 3 }) })); // Repeat 2x const dataset1 = generateData(2); const colorHigh = parseColorToUIntArgb("#EC0F6C"); const colorLow = parseColorToUIntArgb("#30BC9A"); const yMin = Math.min(...yValues); const yMax = Math.max(...yValues); const metadata = dataset1.yValues.map((y, i) => { // interpolate y between colorLow and colorHigh using the helper function uintArgbColorLerp const t = (y - yMin) / (yMax - yMin); const color = uintArgbColorLerp(colorLow, colorHigh, t); return { vertexColor: color, customString: `Custom string ${i}` }; }); sciChart3DSurface.renderableSeries.add(new PointLineRenderableSeries3D(wasmContext, { dataSeries: new XyzDataSeries3D(wasmContext, { xValues: dataset1.xValues, yValues: dataset1.yValues, zValues: dataset1.zValues, metadata, dataSeriesName: "Series B" }), opacity: 0.9, stroke: "#50C7E0", strokeThickness: 3, pointMarker: new EllipsePointMarker3D(wasmContext, { size: 3 }) })); const dataset2 = generateData(3); sciChart3DSurface.renderableSeries.add(new PointLineRenderableSeries3D(wasmContext, { dataSeries: new XyzDataSeries3D(wasmContext, { xValues: dataset2.xValues, yValues: dataset2.yValues, zValues: dataset2.zValues, dataSeriesName: "Series C" }), opacity: 0.9, stroke: "#F48420", strokeThickness: 3, pointMarker: new EllipsePointMarker3D(wasmContext, { size: 3 }) })); // #region ExampleA // Declare a tooltip and add to the chart const tooltipModifier = new TooltipModifier3D({ crosshairStroke: "#83D2F5", crosshairStrokeThickness: 3, tooltipContainerBackground: "#537ABD", tooltipTextStroke: "White", tooltipLegendOffsetX: 10, tooltipLegendOffsetY: 10 }); // Customize the tooltip container like this const defaultTemplate = tooltipModifier.tooltipSvgTemplate; tooltipModifier.tooltipSvgTemplate = (seriesInfo, svgAnnotation) => { if (seriesInfo) { const md = seriesInfo.pointMetadata; const backgroundColor = md ? parseArgbToHtmlColor(md.vertexColor) : seriesInfo.renderableSeries.stroke; svgAnnotation.containerBackground = backgroundColor; svgAnnotation.textStroke = "white"; } return defaultTemplate(seriesInfo, svgAnnotation); }; sciChart3DSurface.chartModifiers.add(tooltipModifier); // #endregion // Customize the tooltip content tooltipModifier.tooltipDataTemplate = (seriesInfo, svgAnnotation) => { // Create an array to hold strings (lines) to show in the tooltip const valuesWithLabels = []; if (seriesInfo && seriesInfo.isHit) { // You can access the renderableSeries which was hit via the seriesInfo const renderableSeries = seriesInfo.renderableSeries; // And the parent Chart from that const parentSurface = renderableSeries.parentSurface; // Push lines to the array to display in the tooltip valuesWithLabels.push(`dataSeriesName: "${seriesInfo.dataSeriesName}"`); valuesWithLabels.push(` ${parentSurface.xAxis.axisTitle}: ${seriesInfo.xValue.toFixed(2)}`); valuesWithLabels.push(` ${parentSurface.yAxis.axisTitle}: ${seriesInfo.yValue.toFixed(2)}`); valuesWithLabels.push(` ${parentSurface.zAxis.axisTitle}: ${seriesInfo.zValue.toFixed(2)}`); // access the metadata (if exists)". Any JS object on the data-points can be accessed // in tooltips const md = seriesInfo.pointMetadata; if (md) { valuesWithLabels.push(` Metadata: "${md.customString}"`); } } return valuesWithLabels; }; }; tooltips3DCustomisation("scichart-root");
Placing the Tooltip as a Separate Legend
The tooltip can be placed as a legend in the corner of the chart by using the TooltipModifier3D.placementDivId property. This simply changes the location in the HTML Dom where tooltips are placed.
Here's a quick example:
This results in the following output:
<div class="wrapper"> <div id="scichart-root" ></div> <div class="titleWrapper"> <p class="title">SciChart.js 3D Chart Example</p> <p class="subTitle">Hover the points to see tooltips</p> </div> <div id="tooltipContainerDivId"> <!-- Tooltips are placed here --> </div> </div>
body { margin: 0; font-family: Arial; } .wrapper { width: 100%; height: 100vh; position: relative; } #scichart-root { width: 100%; height: 100%; position: relative; } .titleWrapper { position: absolute; width: 100%; top: 35%; text-align: center; pointer-events: none; color: #ffffff77 } .title { font-size: 20px; } .subTitle { font-size: 16px; } #tooltipContainerDivId { position: absolute; top: 10px; left: 10px; pointer-events: none; }
async function tooltips3D(divElementId) { const { SciChart3DSurface, NumericAxis3D, Vector3, SciChartJsNavyTheme, EAutoRange, NumberRange, SpherePointMarker3D, ScatterRenderableSeries3D, XyzDataSeries3D, SurfaceMeshRenderableSeries3D, UniformGridDataSeries3D, GradientColorPalette, PointLineRenderableSeries3D, EDrawMeshAs, TooltipModifier3D, EMeshPaletteMode } = SciChart; // or, for npm, import { SciChart3DSurface, ... } from "scichart" // Create a SciChart3DSurface in the host <div id=".." /> const { wasmContext, sciChart3DSurface } = await SciChart3DSurface.create(divElementId, { theme: new SciChartJsNavyTheme(), worldDimensions: new Vector3(300, 200, 300), cameraOptions: { position: new Vector3(-300, 300, 300), target: new Vector3(0, 50, 0), } }); sciChart3DSurface.xAxis = new NumericAxis3D(wasmContext, { axisTitle: "X Axis", }); sciChart3DSurface.yAxis = new NumericAxis3D(wasmContext, { axisTitle: "Y Axis", visibleRange: new NumberRange(0, 3) }); sciChart3DSurface.zAxis = new NumericAxis3D(wasmContext, { axisTitle: "Z Axis", }); sciChart3DSurface.renderableSeries.add( new SurfaceMeshRenderableSeries3D(wasmContext, { dataSeries: new UniformGridDataSeries3D(wasmContext, { yValues: [ [0.1, 0.4, 0.4, 0.2, 0.8], [0.6, 0.4, 0.6, 0.1, 0.7], [0.2, 0.4, 0.3, 0.4, 0.0], [0.6, 0.4, 0.6, 0.1, 0.7], [0.1, 0.4, 0.4, 0.2, 0.8], ], dataSeriesName: "Surface mesh" }), meshColorPalette: new GradientColorPalette(wasmContext, { gradientStops: [ {offset: 1, color: "#EC0F6C"}, // yValues >= maximum mapped to this color {offset: 0.55, color: "#F48420"}, {offset: 0.3, color: "#67BDAF"}, {offset: 0.2, color: "#50C7E0"}, {offset: 0.1, color: "#264B93"}, {offset: 0, color: "#14233C"} // yValues <= minimum mapped to this color ] }), minimum: 0, maximum: 1, opacity: 0.77, drawSkirt: false, stroke: "White", strokeThickness: 2, lightingFactor: 0.2, meshPaletteMode: EMeshPaletteMode.HEIGHT_MAP_SOLID_CELLS, drawMeshAs: EDrawMeshAs.SOLID_WIREFRAME }) ); // #region ExampleA // Declare a tooltip and add to the chart like this. const tooltipModifier = new TooltipModifier3D({ crosshairStroke: "#83D2F5", crosshairStrokeThickness: 3, tooltipContainerBackground: "#537ABD", tooltipTextStroke: "White", tooltipLegendOffsetX: 10, tooltipLegendOffsetY: 10, // Allows placement of tooltip in a custom div anywhere in your app placementDivId: "tooltipContainerDivId" }); sciChart3DSurface.chartModifiers.add(tooltipModifier); // #endregion }; tooltips3D("scichart-root");