DEV Community

Cover image for How to Link JavaScript Charts and Synchronise zooming, panning, crosshairs
Andrew Bt
Andrew Bt

Posted on • Originally published at scichart.com

How to Link JavaScript Charts and Synchronise zooming, panning, crosshairs

In this blog post, we're going to answer a frequently asked question of How to Synchronise multiple linked charts, so that zooming, panning, cursor / crosshair and tooltip interactions are duplicated across all linked JavaScript Charts.

Here's the Requirement

In many dashboards you might want to "link" two or more time-series charts, so that when either one is zoomed, the link would update the other chart to have the same X-range as the other chart.

Perhaps you are creating a timeline control or visualising other processes which occur over time. Having multiple charts allows you to create a layout like this, but you want the charts to scroll, zoom and pan together, and cursor events to track across all the charts at the same time.

Synchronising or linking multiple JavaScript Charts

How to do this with SciChart.js

Using SciChart's JavaScript Chart Library it's possible to do this quite easily. There are a number of ways we can create the required layout where all charts zoom and pan together. These are:

  • Method #1 - Creating separate charts then grouping them (linking)
  • Method #2 - Creating a single chart with Vertically Stacked Axis
  • Method #3 - Using the SubCharts feature to group multiple charts onto a single canvas.

We're going to use Method #1 in this post, as it's the simplest. It should perform well for several charts linked but once you get into large numbers of charts (10 to 20 on page) then one of the other methods may be better.

Initialising the Charts

For this method each chart is hosted in its own <div>. For this example we're going to use simple HTML / JS. Chart divs can be placed in HTML or included in the page using React, Vue, Angular. Take a look at the getting started & boilerplate guides here for more info on how to setup each project type.

For the layout we just need some simple HTML & CSS. Start off with this to place two <div>s in the page which occupy 50% of the height of a parent <div>.

<div id="container">
  <div id="chart0"></div>
  <div id="chart1"></div>
</div>

#container {
  width: 100%;
  height: 100vh;
}

#chart0 {
  width: 100%;
  height: 50%;
  background-color: red;
}

#chart1 {
  width: 100%;
  height: 50%;
  background-color: blue;
}

Next, we want to load in SciChart.js. For this sample we're going to use index.min.js from JSDelivr. Include a <script> tag to load SciChart.js in your HTML page as follows:

<script src="https://cdn.jsdelivr.net/npm/scichart/index.min.js" crossorigin="anonymous"></script>

Now, in JavaScript we want to create the two charts. We'll add a single X, Y axis to each chart and a line series, plus some zooming, panning modifiers to the chart.

const { 
  SciChartSurface, 
  NumericAxis,
  ZoomPanModifier,
  MouseWheelZoomModifier,
  SciChartJSLightTheme,
  FastLineRenderableSeries,
  XyDataSeries,
  RolloverModifier,
  ZoomExtentsModifier
} = SciChart;

async function initSciChart(divId) {
  // create a chart
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(divId, { theme: new SciChartJSLightTheme() });
  
  // Add an X and Y axis
  sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { axisTitle: "X Axis"}));
  sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { axisTitle: "Y Axis"}));
  
  // Create some x, y values to display on the chart
  const xValues = Array.from(Array(100).keys());
  const yValues = xValues.map(x => Math.sin(x*0.3) * (150-x));
  
  // Add a simple line series
  sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
    dataSeries: new XyDataSeries(wasmContext, { xValues, yValues }),
    stroke: "Steelblue", 
    strokeThickness: 3
  }))
  
  // Add zooming, panning, crosshair behaviours
  sciChartSurface.chartModifiers.add(
    new ZoomPanModifier(),
    new MouseWheelZoomModifier(),
    new RolloverModifier(),
    new ZoomExtentsModifier()
  );
  
  return sciChartSurface;
}

// Initialize the two charts
async function createLinkedCharts() {
  const sciChart0 = await initSciChart("chart0");
  const sciChart1 = await initSciChart("chart1");
  
  // TODO: We will add behaviour here to link charts later
}

createLinkedCharts();

This should result in the following output so far:

Synchronising or linking multiple JavaScript Charts

Applying the Grouping to Synchronise Charts

There are three steps which need to be done to link multiple JavaScript charts with SciChart.

  • Apply a modifier group to share mouse events across charts
  • Apply xAxis.visibleRange synchronization to ensure the horizontal scaling is sync'd
  • (optional) Apply vertical grouping to synchronise the yAxis sizes across two different charts

Update the BLAH function to link the two charts as follows:

// Initialize the two charts
async function createLinkedCharts() {
  const sciChart0 = await initSciChart("chart0");
  const sciChart1 = await initSciChart("chart1");
  
  // Don't forget to import SciChartVerticalGroup
  const { SciChartVerticalGroup } = SciChart;
  
  // Step 1 Apply modifier group to sync mouse events
  sciChart0.chartModifiers.asArray().forEach(cm => cm.modifierGroup = "ModifierGroupId");
  sciChart1.chartModifiers.asArray().forEach(cm => cm.modifierGroup = "ModifierGroupId");
  
  // Step 2 Ensure xAxis ranges synced
  // This additional step stops rounding errors from above mouse-even synchronisation from creeping in
  const xAxis0 = sciChart0.xAxes.get(0);
  const xAxis1 = sciChart1.xAxes.get(0); 
  xAxis0.visibleRangeChanged.subscribe((data1) => {
    xAxis1.visibleRange = data1.visibleRange;
  });
  xAxis1.visibleRangeChanged.subscribe((data1) => {
    xAxis0.visibleRange = data1.visibleRange;
  });
  
  // Step 3 Vertical chart group synchronizes axis sizes
  const verticalGroup = new SciChartVerticalGroup();
  verticalGroup.addSurfaceToGroup(sciChart0);
  verticalGroup.addSurfaceToGroup(sciChart1);
  
  // Demonstrate the axis size by modifying one chart's decimal places
  const yAxis0 = sciChart0.yAxes.get(0);
  yAxis0.labelProvider.precision = 4;
}

You should now have the following output:

See the Pen Synchronise or Link Multiple JavaScript Charts by SciChart.js Official (@scichart) on CodePen.

Dynamically Adding/Removing Charts to the Group

In the above example we have a static number of charts linked together with synchronised zooming, panning operations. However, a common use-case is to have a dynamic number of charts, where you can add/remove chart panes from a linked group.

This is a topic for a further blog post, however we have a React demo on Dynamically synchronising multiple charts at the SciChart.js JavaScript Charts Demo.

Further Reading

Here’s some further resources you may find helpful related to this topic:

Top comments (0)