In SciChart.js, all positioning and layout of axis are done with Layout Strategies. A list of default built-in strategies can be found below.
Layout Strategy | Use on | Apply to | Behaviour |
BottomAlignedOuterAxisLayoutStrategy | X Axis | Bottom side | Default behaviour |
BottomAlignedOuterHorizontallyStackedAxisLayoutStrategy | X Axis | Bottom side | Horizontal stacking behaviour |
LeftAlignedOuterAxisLayoutStrategy | Y Axis | Left side | Default behaviour |
RightAlignedOuterAxisLayoutStrategy | Y Axis | Right side | Default behaviour |
TopAlignedOuterAxisLayoutStrategy | X Axis | Top side | Default behaviour |
LeftAlignedOuterVerticallyStackedAxisLayoutStrategy | Y Axis | Left side | Vertical stacking behaviour |
RightAlignedOuterVerticallyStackedAxisLayoutStrategy | Y Axis | Right side | Vertical stacking behaviour |
TopAlignedOuterHorizontallyStackedAxisLayoutStrategy | X Axis | Top side | Horizontal stacking behaviour |
By applying these strategies to the SciChartSurface you can achieve various layouts, such as Central Axis Layout or Vertically Stacked Axis Layout.
However, you can also create your own layout managers for custom or complex axis layouts.
Here's a worked example:
Example: Custom Stacked and Normal Axis Layout
A user on the SciChart Forums asked us how to combine Horizontally Stacked Axis behaviour with default behaviour, to achieve a layout like this:
This is possible by creating a custom layout strategy.
Step 1: create a class which inherits one of our default layout strategies. For this, we chose BottomAlignedOuterHorizontallyStackedAxisLayoutStrategy as the base class.
Breaking this code down:
- The BottomAlignedOuterHorizontallyStackedAxisLayoutStrategy is designed to layout axis on the bottom of the chart (x-Axis) horizontally stacked.
- We override measureAxes and use the default super.measureAxes() function to measure the last three axis. We use a BottomAlignedOuterAxisLayoutStrategy member variable to measure the final axis.
- We override layoutAxes and do the same, combining the result of two strategies.
Applying the strategy like this to a chart:
You can now get the following output.
<div id="scichart-root" ></div>
body { margin: 0; } #scichart-root { width: 100%; height: 100vh; }
// #region ExampleA // or for npm ... import { BottomAlignedOuterHorizontallyStackedAxisLayoutStrategy } from "scichart"; const { BottomAlignedOuterHorizontallyStackedAxisLayoutStrategy, BottomAlignedOuterAxisLayoutStrategy, getHorizontalAxisRequiredSize } = SciChart; // Example of creating a custom layout manager. First requested here https://www.scichart.com/questions/js/is-it-possible-to-create-two-xaxis-where-one-is-normal-and-the-other-one-is-horizontally-stacked-axis-layout // // Axis rendering happens in 2 phases: measure & layout. // Axis size and positioning is calculated by an axis layout strategy accordingly to the axisAlignment and isInner properties // This custom Layout Strategy applies normal layout strategy to the first axis and the stacked strategy to the rest of bottom-aligned outer axes class CustomAxisLayoutStrategy extends BottomAlignedOuterHorizontallyStackedAxisLayoutStrategy { constructor() { super(); /** The strategy used for normal (non-stacked) layout */ this.defaultBottomOuterAxisLayoutStrategy = new BottomAlignedOuterAxisLayoutStrategy(); } // override measureAxes from the base class measureAxes(sciChartSurface, chartLayoutState, axes) { const [firstAxis, ...stackedAxes] = axes; // measure stacked axes and max height (stackedAreaSize) required by them super.measureAxes(sciChartSurface, chartLayoutState, stackedAxes); const stackedAreaSize = chartLayoutState.bottomOuterAreaSize; // measure first axis with the regular logic this.defaultBottomOuterAxisLayoutStrategy.measureAxes( sciChartSurface, chartLayoutState, [firstAxis] ); // calculate height required by the first axis and then the total height const firstAxisSize = getHorizontalAxisRequiredSize(firstAxis.axisLayoutState); chartLayoutState.bottomOuterAreaSize = firstAxisSize + stackedAreaSize; } // Override layoutAxes from the base class layoutAxes(left, top, right, bottom, axes) { const [firstAxis, ...stackedAxes] = axes; // layout first axis with the regular logic this.defaultBottomOuterAxisLayoutStrategy.layoutAxes( left, top, right, bottom, [firstAxis] ); // after the layout phase we get axis.viewRect which specifies size and position of an axis // and then we can layout rest of the axes with stacked strategy beneath it. super.layoutAxes(left, firstAxis.viewRect.bottom, right, bottom, stackedAxes); } } // #endregion async function customLayoutManager(divElementId) { // Demonstrates how to apply a custom layout manager in SciChart.js const { SciChartSurface, NumericAxis, SciChartJsNavyTheme, } = SciChart; // or, for npm, import { SciChartSurface, ... } from "scichart" // #region ExampleB const { wasmContext, sciChartSurface } = await SciChartSurface.create(divElementId, { theme: new SciChartJsNavyTheme() }); // Apply your layout manager sciChartSurface.layoutManager.bottomOuterAxesLayoutStrategy = new CustomAxisLayoutStrategy(); // Create some X Axis const ID_X_AXIS_1 = "xAxis0"; const ID_X_AXIS_2 = "xAxis1"; const ID_X_AXIS_3 = "xAxis2"; const ID_X_AXIS_4 = "xAxis3"; const options = { drawMajorBands: false, drawMajorGridLines: false, drawMinorGridLines: false }; const xAxis1 = new NumericAxis(wasmContext, { id: ID_X_AXIS_1, axisTitle: ID_X_AXIS_1, drawMajorBands: true, drawMajorGridLines: true, drawMinorGridLines: true, }); const xAxis2 = new NumericAxis(wasmContext, { id: ID_X_AXIS_2, axisTitle: ID_X_AXIS_2, ...options, }); const xAxis3 = new NumericAxis(wasmContext, { id: ID_X_AXIS_3, axisTitle: ID_X_AXIS_3, ...options, }); const xAxis4 = new NumericAxis(wasmContext, { id: ID_X_AXIS_4, axisTitle: ID_X_AXIS_4, ...options, }); const yAxis1 = new NumericAxis(wasmContext, { axisTitle: "yAxis", backgroundColor: "#50C7E022", axisBorder: {color: "#50C7E0", borderLeft: 1 }, axisTitleStyle: { fontSize: 13 }, }); // Add the axis to the chart sciChartSurface.xAxes.add(xAxis1, xAxis2, xAxis3, xAxis4); sciChartSurface.yAxes.add(yAxis1); // To make it clearer what's happening, colour the axis backgrounds & borders const axisColors = ["#50C7E0", "#EC0F6C", "#30BC9A", "#F48420" ]; sciChartSurface.xAxes.asArray().forEach((xAxis, index) => { xAxis.backgroundColor = axisColors[index] + "22"; xAxis.axisBorder = {color: axisColors[index], borderTop: 1}; xAxis.axisTitleStyle.fontSize = 13; }); // #endregion const { ZoomPanModifier, PinchZoomModifier, ZoomExtentsModifier, MouseWheelZoomModifier, FastLineRenderableSeries, XyDataSeries, TextAnnotation } = SciChart; // Let's add some series to the chart to show how they also behave with axis const getOptions = (index, offset = 0) => { const xValues = Array.from(Array(50).keys()); const yValues = xValues.map(x => Math.sin(x * 0.4 + index) + offset); return { xAxisId: `xAxis${index}`, stroke: axisColors[index], strokeThickness: 3, dataSeries: new XyDataSeries(wasmContext, { xValues, yValues }) }; }; sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {...getOptions(0, 1)})); sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {...getOptions(1)})); sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {...getOptions(2)})); sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {...getOptions(3)})); // We will also add some annotations to explain to the user sciChartSurface.annotations.add(new TextAnnotation({ text: "Blue series uses xAxis0 and is stretched horizontally", x1: 0, y1: 2, textColor: axisColors[0], })); sciChartSurface.annotations.add(new TextAnnotation({ text: "Using xAxis1", x1: 0, y1: 1.1, xAxisId: ID_X_AXIS_2, textColor: axisColors[1], })); sciChartSurface.annotations.add(new TextAnnotation({ text: "Using xAxis2", x1: 0, y1: 1.1, xAxisId: ID_X_AXIS_3, textColor: axisColors[2], })); sciChartSurface.annotations.add(new TextAnnotation({ text: "Using xAxis3", x1: 0, y1: 1.1, xAxisId: ID_X_AXIS_4, textColor: axisColors[3], })); sciChartSurface.chartModifiers.add( new ZoomPanModifier(), new PinchZoomModifier(), new ZoomExtentsModifier(), new MouseWheelZoomModifier({ applyToSeriesViewRect: false }) ); }; customLayoutManager("scichart-root");
Reversing the Order of Stacked & Stretched Axis
What if you wanted to swap the order of the stretched xAxis and the stacked xAxis?
This is really simple, just modify the layoutAxes function like this.
Now the axis order are swapped, resulting in this output.
<div id="scichart-root" ></div>
body { margin: 0; } #scichart-root { width: 100%; height: 100vh; }
// or for npm ... import { BottomAlignedOuterHorizontallyStackedAxisLayoutStrategy } from "scichart"; const { BottomAlignedOuterHorizontallyStackedAxisLayoutStrategy, BottomAlignedOuterAxisLayoutStrategy, getHorizontalAxisRequiredSize } = SciChart; // Example of creating a custom layout manager. First requested here https://www.scichart.com/questions/js/is-it-possible-to-create-two-xaxis-where-one-is-normal-and-the-other-one-is-horizontally-stacked-axis-layout // // Axis rendering happens in 2 phases: measure & layout. // Axis size and positioning is calculated by an axis layout strategy accordingly to the axisAlignment and isInner properties // This custom Layout Strategy applies normal layout strategy to the first axis and the stacked strategy to the rest of bottom-aligned outer axes class CustomAxisLayoutStrategy extends BottomAlignedOuterHorizontallyStackedAxisLayoutStrategy { constructor() { super(); /** The strategy used for normal (non-stacked) layout */ this.defaultBottomOuterAxisLayoutStrategy = new BottomAlignedOuterAxisLayoutStrategy(); } // override measureAxes from the base class measureAxes(sciChartSurface, chartLayoutState, axes) { const [firstAxis, ...stackedAxes] = axes; // measure stacked axes and max height (stackedAreaSize) required by them super.measureAxes(sciChartSurface, chartLayoutState, stackedAxes); const stackedAreaSize = chartLayoutState.bottomOuterAreaSize; // measure first axis with the regular logic this.defaultBottomOuterAxisLayoutStrategy.measureAxes( sciChartSurface, chartLayoutState, [firstAxis] ); // calculate height required by the first axis and then the total height const firstAxisSize = getHorizontalAxisRequiredSize(firstAxis.axisLayoutState); chartLayoutState.bottomOuterAreaSize = firstAxisSize + stackedAreaSize; } // #region ExampleA // Use the base horizontal stacked layout first, before default layout to switch the order of axis layoutAxes(left, top, right, bottom, axes) { const [firstAxis, ...stackedAxes] = axes; // layout stacked axes first super.layoutAxes(left, top, right, bottom, stackedAxes); // then get the top offset for the normalAxis with stackedAxis.viewRect.bottom const stackedAxis = stackedAxes[0] this.defaultBottomOuterAxisLayoutStrategy.layoutAxes( left, stackedAxis.viewRect.bottom, right, bottom, [firstAxis] // normal axis ); } // #endregion } async function customLayoutManager(divElementId) { // Demonstrates how to apply a custom layout manager in SciChart.js const { SciChartSurface, NumericAxis, SciChartJsNavyTheme, } = SciChart; // or, for npm, import { SciChartSurface, ... } from "scichart" const { wasmContext, sciChartSurface } = await SciChartSurface.create(divElementId, { theme: new SciChartJsNavyTheme() }); // Apply your layout manager sciChartSurface.layoutManager.bottomOuterAxesLayoutStrategy = new CustomAxisLayoutStrategy(); // Create some X Axis const ID_X_AXIS_1 = "xAxis0"; const ID_X_AXIS_2 = "xAxis1"; const ID_X_AXIS_3 = "xAxis2"; const ID_X_AXIS_4 = "xAxis3"; const options = { drawMajorBands: false, drawMajorGridLines: false, drawMinorGridLines: false }; const xAxis1 = new NumericAxis(wasmContext, { id: ID_X_AXIS_1, axisTitle: ID_X_AXIS_1, drawMajorBands: true, drawMajorGridLines: true, drawMinorGridLines: true, }); const xAxis2 = new NumericAxis(wasmContext, { id: ID_X_AXIS_2, axisTitle: ID_X_AXIS_2, ...options, }); const xAxis3 = new NumericAxis(wasmContext, { id: ID_X_AXIS_3, axisTitle: ID_X_AXIS_3, ...options, }); const xAxis4 = new NumericAxis(wasmContext, { id: ID_X_AXIS_4, axisTitle: ID_X_AXIS_4, ...options, }); const yAxis1 = new NumericAxis(wasmContext, { axisTitle: "yAxis", backgroundColor: "#50C7E022", axisBorder: {color: "#50C7E0", borderLeft: 1 }, axisTitleStyle: { fontSize: 13 }, }); // Add the axis to the chart sciChartSurface.xAxes.add(xAxis1, xAxis2, xAxis3, xAxis4); sciChartSurface.yAxes.add(yAxis1); // To make it clearer what's happening, colour the axis backgrounds & borders const axisColors = ["#50C7E0", "#EC0F6C", "#30BC9A", "#F48420" ]; sciChartSurface.xAxes.asArray().forEach((xAxis, index) => { xAxis.backgroundColor = axisColors[index] + "22"; xAxis.axisBorder = {color: axisColors[index], borderTop: 1}; xAxis.axisTitleStyle.fontSize = 13; }); const { TextAnnotation, EHorizontalAnchorPoint, ECoordinateMode, EAnnotationLayer } = SciChart; const textOpts = { xCoordinateMode: ECoordinateMode.Relative, yCoordinateMode: ECoordinateMode.Relative, x1: 0.5, y1: 0.5, horizontalAnchorPoint: EHorizontalAnchorPoint.Center, opacity: 0.33, textColor: "White", }; sciChartSurface.annotations.add(new TextAnnotation({ text: "Custom Layout Manager Example", fontSize: 36, yCoordShift: -50, ... textOpts, })); sciChartSurface.annotations.add(new TextAnnotation({ text: "Reversing the order of Stacked and Stretched X-Axis", fontSize: 20, ... textOpts, })); }; customLayoutManager("scichart-root");