Synchronized Charts in Vue Chart component

13 Jun 202424 minutes to read

Tooltip synchronization

The tooltip can be synchronized across multiple charts using the showTooltip and hideTooltip methods. When we hover over a data point in one chart, we call the showTooltip method for the other charts to display related information in other connected charts simultaneously.

In the showTooltip method, specify the following parameters programmatically to enable tooltip for a particular chart:

  • x - Data point x-value or x-coordinate value.
  • y - Data point y-value or y-coordinate value.
<template>
  <div class="control-section">
      <div class="row">
          <div class="col" id="container1">
              <ejs-chart style='display:block' align='center' id='chartcontainer1' :title='title1'
                  :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis1' :tooltip='tooltip1' :titleStyle='titleStyle' 
                  :chartMouseLeave='chartMouseLeave1' :chartMouseMove='chartMouseMove1' :chartMouseUp='chartMouseUp1' ref="chart1">
                  <e-series-collection>
                      <e-series :dataSource='seriesData' type='Line' xName='USD' yName='EUR' width = 2
                          :emptyPointSetting='emptyPointSettings'> </e-series>
                  </e-series-collection>
              </ejs-chart>
          </div>
          <div class="col" id="container2">
              <ejs-chart style='display:block' align='center' id='chartcontainer2' :title='title2'
                  :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis2' :tooltip='tooltip2' :titleStyle='titleStyle'
                  :chartMouseLeave='chartMouseLeave2' :chartMouseMove='chartMouseMove2' :chartMouseUp='chartMouseUp2' ref="chart2">
                  <e-series-collection>
                      <e-series :dataSource='seriesData' type='Area' xName='USD' yName='INR' opacity=0.6
                          :border='border'>
                      </e-series>
                  </e-series-collection>
              </ejs-chart>
          </div>
      </div>
  </div>
</template>
<script setup>
import { provide } from "vue";

import { ChartComponent as EjsChart, SeriesCollectionDirective as ESeriesCollection, SeriesDirective as ESeries, AreaSeries, LineSeries, DateTime, Tooltip } from "@syncfusion/ej2-vue-charts";
import { synchronizedData } from './dataSource.js';
import { Browser } from '@syncfusion/ej2-base';
import {ref} from 'vue';

const chart1=ref(null);
const chart2=ref(null);
const seriesData= synchronizedData;
const  primaryXAxis= {
        minimum: new Date(2023, 1, 18),
        maximum: new Date(2023, 7, 18),
        valueType: 'DateTime',
        labelFormat: 'MMM d',
        lineStyle: { width: 0 },
        majorGridLines: { width: 0 },
        interval: Browser.isDevice ? 2 : 1,
        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
        labelRotation: Browser.isDevice ? -45 : 0
      };
      const primaryYAxis1= {
        labelFormat: 'n2',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 0.86,
        maximum: 0.96,
        interval: 0.025
      };
      const primaryYAxis2= {
        labelFormat: 'n1',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 79,
        maximum: 85,
        interval: 1.5
      };
      const border= { width: 2 };
      const tooltip1= {
        enable: true,
        shared: true,
        header: '', 
        enableMarker: false,
        format: '<b>€${point.y}</b> <br>${point.x} 2023',
        fadeOutDuration: Browser.isDevice ? 2500 : 1000
      };
      const tooltip2= {
        enable: true,
        shared: true,
        header: '', 
        enableMarker: false,
        format: '<b>₹${point.y}</b> <br>${point.x} 2023',
        fadeOutDuration: Browser.isDevice ? 2500 : 1000
      };
      const emptyPointSettings= { mode: 'Drop' };
     const titleStyle= { textAlignment: 'Near' };
     const title1= "US to EURO";
     const title2= "US to INR";

provide('chart',  [AreaSeries, LineSeries, DateTime, Tooltip]);

 const chartMouseLeave1= function () {
    chart2.value.ej2Instances.hideTooltip();
  };
  const chartMouseUp1= function () {
    if (Browser.isDevice || chart1.value.ej2Instances.startMove) {
      chart2.value.ej2Instances.hideTooltip();
    }
  };
  const chartMouseMove1= function (args) {
    if ((!Browser.isDevice && !chart1.value.ej2Instances.isTouch && !chart1.value.ej2Instances.isChartDrag) || chart1.value.ej2Instances.startMove) {
      chart2.value.ej2Instances.startMove = chart1.value.ej2Instances.startMove;
      chart2.value.ej2Instances.showTooltip(args.x, args.y);
    }
  };
  const chartMouseMove2= function (args) {
    if ((!Browser.isDevice && !chart2.value.ej2Instances.isTouch && !chart2.value.ej2Instances.isChartDrag) || chart2.value.ej2Instances.startMove) {
      chart1.value.ej2Instances.startMove = chart2.value.ej2Instances.startMove;
      chart1.value.ej2Instances.showTooltip(args.x, args.y);
    }
  };
  const chartMouseLeave2= function () {
    chart1.value.ej2Instances.hideTooltip();
  };
  const chartMouseUp2= function () {
    if (Browser.isDevice || chart2.value.ej2Instances.startMove) {
      chart1.value.ej2Instances.hideTooltip();
    }
  };

</script>
<style>
#container {
  height: 350px;
}
#control-container {
  padding: 1px !important;
}

.row {
  display: flex;
}

.col {
  width: 50%;
  margin: 10px;
  height: 270px;
}
</style>
<template>
  <div class="control-section">
    <div class="row">
      <div class="col" id="container1">
        <ejs-chart style='display:block' align='center' id='chartcontainer1' :title='title1'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis1' :tooltip='tooltip1' :titleStyle='titleStyle'
          :chartMouseLeave='chartMouseLeave1' :chartMouseMove='chartMouseMove1' :chartMouseUp='chartMouseUp1'
          ref="chart1">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Line' xName='USD' yName='EUR' width=2
              :emptyPointSetting='emptyPointSettings'> </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
      <div class="col" id="container2">
        <ejs-chart style='display:block' align='center' id='chartcontainer2' :title='title2'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis2' :tooltip='tooltip2' :titleStyle='titleStyle'
          :chartMouseLeave='chartMouseLeave2' :chartMouseMove='chartMouseMove2' :chartMouseUp='chartMouseUp2'
          ref="chart2">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Area' xName='USD' yName='INR' opacity=0.6 :border='border'>
            </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
    </div>
  </div>
</template>
<script>

import { ChartComponent, SeriesDirective, SeriesCollectionDirective, AreaSeries, LineSeries, DateTime, Tooltip } from "@syncfusion/ej2-vue-charts";
import { synchronizedData } from './dataSource.js';
import { Browser } from '@syncfusion/ej2-base';


export default {
  name: "App",
  components: {
    "ejs-chart": ChartComponent,
    "e-series-collection": SeriesCollectionDirective,
    "e-series": SeriesDirective
  },
  data() {
    return {
      seriesData: synchronizedData,
      primaryXAxis: {
        minimum: new Date(2023, 1, 18),
        maximum: new Date(2023, 7, 18),
        valueType: 'DateTime',
        labelFormat: 'MMM d',
        lineStyle: { width: 0 },
        majorGridLines: { width: 0 },
        interval: Browser.isDevice ? 2 : 1,
        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
        labelRotation: Browser.isDevice ? -45 : 0
      },
      primaryYAxis1: {
        labelFormat: 'n2',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 0.86,
        maximum: 0.96,
        interval: 0.025
      },
      primaryYAxis2: {
        labelFormat: 'n1',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 79,
        maximum: 85,
        interval: 1.5
      },
      border: { width: 2 },
      tooltip1: {
        enable: true,
        shared: true,
        header: '',
        enableMarker: false,
        format: '<b>€${point.y}</b> <br>${point.x} 2023',
        fadeOutDuration: Browser.isDevice ? 2500 : 1000
      },
      tooltip2: {
        enable: true,
        shared: true,
        header: '',
        enableMarker: false,
        format: '<b>₹${point.y}</b> <br>${point.x} 2023',
        fadeOutDuration: Browser.isDevice ? 2500 : 1000
      },
      emptyPointSettings: { mode: 'Drop' },
      titleStyle: { textAlignment: 'Near' },
      title1: "US to EURO",
      title2: "US to INR"
    };
  },
  provide: {
    chart: [AreaSeries, LineSeries, DateTime, Tooltip]
  },
  methods: {
    chartMouseLeave1: function () {
      this.$refs.chart2.ej2Instances.hideTooltip();
    },
    chartMouseUp1: function () {
      if (Browser.isDevice || this.$refs.chart1.ej2Instances.startMove) {
        this.$refs.chart2.ej2Instances.hideTooltip();
      }
    },
    chartMouseMove1: function (args) {
      if ((!Browser.isDevice && !this.$refs.chart1.ej2Instances.isTouch && !this.$refs.chart1.ej2Instances.isChartDrag) || this.$refs.chart1.ej2Instances.startMove) {
        this.$refs.chart2.ej2Instances.startMove = this.$refs.chart1.ej2Instances.startMove;
        this.$refs.chart2.ej2Instances.showTooltip(args.x, args.y);
      }
    },
    chartMouseMove2: function (args) {
      if ((!Browser.isDevice && !this.$refs.chart2.ej2Instances.isTouch && !this.$refs.chart2.ej2Instances.isChartDrag) || this.$refs.chart2.ej2Instances.startMove) {
        this.$refs.chart1.ej2Instances.startMove = this.$refs.chart2.ej2Instances.startMove;
        this.$refs.chart1.ej2Instances.showTooltip(args.x, args.y);
      }
    },
    chartMouseLeave2: function () {
      this.$refs.chart1.ej2Instances.hideTooltip();
    },
    chartMouseUp2: function () {
      if (Browser.isDevice || this.$refs.chart2.ej2Instances.startMove) {
        this.$refs.chart1.ej2Instances.hideTooltip();
      }
    }
  }
};
</script>
<style>
#container {
  height: 350px;
}

#control-container {
  padding: 1px !important;
}

.row {
  display: flex;
}

.col {
  width: 50%;
  margin: 10px;
  height: 270px;
}
</style>

Crosshair synchronization

The crosshair can be synchronized across multiple charts using the showCrosshair and hideCrosshair methods. When we hover over one chart, we call the showCrosshair method for the other charts to align with data points in other connected charts, simplifying data comparison and analysis.

In the showCrosshair method, specify the following parameters programmatically to enable crosshair for a particular chart:

  • x - Specifies the x-value of the point or x-coordinate.
  • y - Specifies the y-value of the point or y-coordinate.
<template>
  <div class="control-section">
    <div class="row">
      <div class="col" id="container1">
        <ejs-chart style='display:block' align='center' id='chartcontainer1' :title='title1'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis1' :crosshair='crosshair' :titleStyle='titleStyle'
          :chartMouseLeave='chartMouseLeave1' :chartMouseMove='chartMouseMove1' :chartMouseUp='chartMouseUp1'
          ref="chart1">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Spline' xName='USD' yName='EUR' width=2
              :emptyPointSetting='emptyPointSettings'> </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
      <div class="col" id="container2">
        <ejs-chart style='display:block' align='center' id='chartcontainer2' :title='title2'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis2' :crosshair='crosshair' :titleStyle='titleStyle'
          :chartMouseLeave='chartMouseLeave2' :chartMouseMove='chartMouseMove2' :chartMouseUp='chartMouseUp2'
          ref="chart2">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Area' xName='USD' yName='INR' opacity=0.6 :border='border'>
            </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
    </div>
  </div>
</template>
<script setup>
import { provide } from "vue";
import { ref } from 'vue';
import { ChartComponent as EjsChart, SeriesCollectionDirective as ESeriesCollection, SeriesDirective as ESeries, AreaSeries, SplineSeries, DateTime, Crosshair } from "@syncfusion/ej2-vue-charts";
import { synchronizedData } from './dataSource.js';
import { Browser } from '@syncfusion/ej2-base';


const chart1 = ref(null);
const chart2 = ref(null);
const seriesData = synchronizedData;
const primaryXAxis = {
  minimum: new Date(2023, 1, 18),
  maximum: new Date(2023, 7, 18),
  valueType: 'DateTime',
  labelFormat: 'MMM d',
  lineStyle: { width: 0 },
  majorGridLines: { width: 0 },
  interval: Browser.isDevice ? 2 : 1,
  edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
  labelRotation: Browser.isDevice ? -45 : 0,
  crosshairTooltip: { enable: true }
};
const primaryYAxis1 = {
  labelFormat: 'n2',
  majorTickLines: { width: 0 },
  lineStyle: { width: 0 },
  minimum: 0.86,
  maximum: 0.96,
  interval: 0.025
};
const primaryYAxis2 = {
  labelFormat: 'n1',
  majorTickLines: { width: 0 },
  lineStyle: { width: 0 },
  minimum: 79,
  maximum: 85,
  interval: 1.5
};
const border = { width: 2 };
const crosshair = { enable: true, lineType: 'Vertical', dashArray: '2,2' };
const emptyPointSettings = { mode: 'Drop' };
const titleStyle = { textAlignment: 'Near' };
const title1 = "US to EURO";
const title2 = "US to INR";

provide('chart', [AreaSeries, SplineSeries, DateTime, Crosshair]);

const chartMouseLeave1 = () => {
  chart2.value.ej2Instances.hideCrosshair();
};
const chartMouseUp1 = () => {
  if (Browser.isDevice || chart1.value.ej2Instances.startMove) {
    chart2.value.ej2Instances.hideCrosshair();
  }
};
const chartMouseMove1 = (args) => {
  if ((!Browser.isDevice && !chart1.value.ej2Instances.isTouch && !chart1.value.ej2Instances.isChartDrag) || chart1.value.ej2Instances.startMove) {
    chart2.value.ej2Instances.startMove = chart1.value.ej2Instances.startMove;
    chart2.value.ej2Instances.showCrosshair(args.x, args.y);
  }
};
const chartMouseMove2 = (args) => {
  if ((!Browser.isDevice && !chart2.value.ej2Instances.isTouch && !chart2.value.ej2Instances.isChartDrag) || chart2.value.ej2Instances.startMove) {
    chart1.value.ej2Instances.startMove = chart2.value.ej2Instances.startMove;
    chart1.value.ej2Instances.showCrosshair(args.x, args.y);
  }
};
const chartMouseLeave2 = () => {
  chart1.value.ej2Instances.hideCrosshair();
};
const chartMouseUp2 = () => {
  if (Browser.isDevice || chart2.value.ej2Instances.startMove) {
    chart1.value.ej2Instances.hideCrosshair();
  };
}

</script>
<style>
#container {
  height: 350px;
}

#control-container {
  padding: 1px !important;
}

.row {
  display: flex;
}

.col {
  width: 50%;
  margin: 10px;
  height: 270px;
}
</style>
<template>
  <div class="control-section">
    <div class="row">
      <div class="col" id="container1">
        <ejs-chart style='display:block' align='center' id='chartcontainer1' :title='title1'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis1' :crosshair='crosshair' :titleStyle='titleStyle'
          :chartMouseLeave='chartMouseLeave1' :chartMouseMove='chartMouseMove1' :chartMouseUp='chartMouseUp1'
          ref="chart1">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Spline' xName='USD' yName='EUR' width=2
              :emptyPointSetting='emptyPointSettings'> </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
      <div class="col" id="container2">
        <ejs-chart style='display:block' align='center' id='chartcontainer2' :title='title2'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis2' :crosshair='crosshair' :titleStyle='titleStyle'
          :chartMouseLeave='chartMouseLeave2' :chartMouseMove='chartMouseMove2' :chartMouseUp='chartMouseUp2'
          ref="chart2">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Area' xName='USD' yName='INR' opacity=0.6 :border='border'>
            </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
    </div>
  </div>
</template>
<script>

import { ChartComponent, SeriesDirective, SeriesCollectionDirective, AreaSeries, SplineSeries, DateTime, Crosshair } from "@syncfusion/ej2-vue-charts";
import { synchronizedData } from './dataSource.js';
import { Browser } from '@syncfusion/ej2-base';


export default {
  name: "App",
  components: {
    "ejs-chart": ChartComponent,
    "e-series-collection": SeriesCollectionDirective,
    "e-series": SeriesDirective
  },
  data() {
    return {
      seriesData: synchronizedData,
      primaryXAxis: {
        minimum: new Date(2023, 1, 18),
        maximum: new Date(2023, 7, 18),
        valueType: 'DateTime',
        labelFormat: 'MMM d',
        lineStyle: { width: 0 },
        majorGridLines: { width: 0 },
        interval: Browser.isDevice ? 2 : 1,
        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
        labelRotation: Browser.isDevice ? -45 : 0,
        crosshairTooltip: { enable: true }
      },
      primaryYAxis1: {
        labelFormat: 'n2',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 0.86,
        maximum: 0.96,
        interval: 0.025
      },
      primaryYAxis2: {
        labelFormat: 'n1',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 79,
        maximum: 85,
        interval: 1.5
      },
      border: { width: 2 },
      crosshair: { enable: true, lineType: 'Vertical', dashArray: '2,2' },
      emptyPointSettings: { mode: 'Drop' },
      titleStyle: { textAlignment: 'Near' },
      title1: "US to EURO",
      title2: "US to INR"
    };
  },
  provide: {
    chart: [AreaSeries, SplineSeries, DateTime, Crosshair]
  },
  methods: {
    chartMouseLeave1: function () {
      this.$refs.chart2.ej2Instances.hideCrosshair();
    },
    chartMouseUp1: function () {
      if (Browser.isDevice || this.$refs.chart1.ej2Instances.startMove) {
        this.$refs.chart2.ej2Instances.hideCrosshair();
      }
    },
    chartMouseMove1: function (args) {
      if ((!Browser.isDevice && !this.$refs.chart1.ej2Instances.isTouch && !this.$refs.chart1.ej2Instances.isChartDrag) || this.$refs.chart1.ej2Instances.startMove) {
        this.$refs.chart2.ej2Instances.startMove = this.$refs.chart1.ej2Instances.startMove;
        this.$refs.chart2.ej2Instances.showCrosshair(args.x, args.y);
      }
    },
    chartMouseMove2: function (args) {
      if ((!Browser.isDevice && !this.$refs.chart2.ej2Instances.isTouch && !this.$refs.chart2.ej2Instances.isChartDrag) || this.$refs.chart2.ej2Instances.startMove) {
        this.$refs.chart1.ej2Instances.startMove = this.$refs.chart2.ej2Instances.startMove;
        this.$refs.chart1.ej2Instances.showCrosshair(args.x, args.y);
      }
    },
    chartMouseLeave2: function () {
      this.$refs.chart1.ej2Instances.hideCrosshair();
    },
    chartMouseUp2: function () {
      if (Browser.isDevice || this.$refs.chart2.ej2Instances.startMove) {
        this.$refs.chart1.ej2Instances.hideCrosshair();
      }
    }
  }
};
</script>
<style>
#container {
  height: 350px;
}

#control-container {
  padding: 1px !important;
}

.row {
  display: flex;
}

.col {
  width: 50%;
  margin: 10px;
  height: 270px;
}
</style>

Zooming synchronization

You can maintain constant zoom levels across multiple charts using the zoomComplete event. In the zoomComplete event, obtain the zoomFactor and zoomPosition values of the particular chart, and then apply those values to the other charts.

<template>
  <div class="control-section">
    <div class="row">
      <div class="col" id="container1">
        <ejs-chart style='display:block' align='center' id='chartcontainer1' :title='title1'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis1' :zoomSettings='zoomSettings'
          :titleStyle='titleStyle' :zoomComplete='zoomComplete' ref="chart1">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Line' xName='USD' yName='EUR' width=2
              :emptyPointSetting='emptyPointSettings'> </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
      <div class="col" id="container2">
        <ejs-chart style='display:block' align='center' id='chartcontainer2' :title='title2'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis2' :zoomSettings='zoomSettings'
          :titleStyle='titleStyle' :zoomComplete='zoomComplete' ref="chart2">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='SplineArea' xName='USD' yName='INR' opacity=0.6 :border='border'>
            </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
    </div>
  </div>
</template>
<script setup>
import { onMounted, provide } from "vue";

import { ChartComponent as EjsChart, SeriesCollectionDirective as ESeriesCollection, SeriesDirective as ESeries, LineSeries, SplineAreaSeries, DateTime, Zoom } from "@syncfusion/ej2-vue-charts";
import { synchronizedData } from './dataSource.js';
import { Browser } from '@syncfusion/ej2-base';

import {ref} from'vue';

const chart1=ref(null);
const chart2=ref(null);

let zoomFactor = 0;
let zoomPosition = 0;

const seriesData = synchronizedData;
const primaryXAxis = {
  minimum: new Date(2023, 1, 18),
  maximum: new Date(2023, 7, 18),
  valueType: 'DateTime',
  labelFormat: 'MMM d',
  lineStyle: { width: 0 },
  majorGridLines: { width: 0 },
  interval: Browser.isDevice ? 2 : 1,
  edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
  labelRotation: Browser.isDevice ? -45 : 0
};
const primaryYAxis1 = {
  labelFormat: 'n2',
  majorTickLines: { width: 0 },
  lineStyle: { width: 0 },
  minimum: 0.86,
  maximum: 0.96,
  interval: 0.025
};
const primaryYAxis2 = {
  labelFormat: 'n1',
  majorTickLines: { width: 0 },
  lineStyle: { width: 0 },
  minimum: 79,
  maximum: 85,
  interval: 1.5
};
const border = { width: 2 };
const zoomSettings = {
  enableMouseWheelZooming: true,
  enablePinchZooming: true,
  enableScrollbar: false,
  enableDeferredZooming: false,
  enableSelectionZooming: true,
  enablePan: true,
  mode: 'X',
  toolbarItems: ['Pan', 'Reset']
};
const emptyPointSettings = { mode: 'Drop' };
const titleStyle = { textAlignment: 'Near' };
const title1 = "US to EURO";
const title2 = "US to INR";
let charts = [];

provide('chart', [LineSeries, SplineAreaSeries, DateTime, Zoom]);

const zoomComplete = function (args) {
  if (args.axis.name === 'primaryXAxis') {
    zoomFactor = args.currentZoomFactor;
    zoomPosition = args.currentZoomPosition;
    zoomCompleteFunction(args);
  }
};
const zoomCompleteFunction = function (args) {
  for (var i = 0; i < charts.length; i++) {
    if (args.axis.series[0].chart.element.id !== charts[i].element.id) {
      charts[i].primaryXAxis.zoomFactor = zoomFactor;
      charts[i].primaryXAxis.zoomPosition = zoomPosition;
      charts[i].zoomModule.isZoomed = args.axis.series[0].chart.zoomModule.isZoomed;
      charts[i].zoomModule.isPanning = args.axis.series[0].chart.zoomModule.isPanning;
    }
  }
};

onMounted(()=> {
  charts = [chart1.value.ej2Instances, chart2.value.ej2Instances];
});

</script>
<style>
#container {
  height: 350px;
}

#control-container {
  padding: 1px !important;
}

.row {
  display: flex;
}

.col {
  width: 50%;
  margin: 10px;
  height: 270px;
}
</style>
<template>
  <div class="control-section">
    <div class="row">
      <div class="col" id="container1">
        <ejs-chart style='display:block' align='center' id='chartcontainer1' :title='title1'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis1' :zoomSettings='zoomSettings'
          :titleStyle='titleStyle' :zoomComplete='zoomComplete' ref="chart1">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Line' xName='USD' yName='EUR' width=2
              :emptyPointSetting='emptyPointSettings'> </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
      <div class="col" id="container2">
        <ejs-chart style='display:block' align='center' id='chartcontainer2' :title='title2'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis2' :zoomSettings='zoomSettings'
          :titleStyle='titleStyle' :zoomComplete='zoomComplete' ref="chart2">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='SplineArea' xName='USD' yName='INR' opacity=0.6 :border='border'>
            </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
    </div>
  </div>
</template>
<script>

import { ChartComponent, SeriesDirective, SeriesCollectionDirective, LineSeries, SplineAreaSeries, DateTime, Zoom } from "@syncfusion/ej2-vue-charts";
import { synchronizedData } from './dataSource.js';
import { Browser } from '@syncfusion/ej2-base';

let zoomFactor = 0;
let zoomPosition = 0;
export default {
  name: "App",
  components: {
    "ejs-chart": ChartComponent,
    "e-series-collection": SeriesCollectionDirective,
    "e-series": SeriesDirective
  },
  data() {
    return {
      seriesData: synchronizedData,
      primaryXAxis: {
        minimum: new Date(2023, 1, 18),
        maximum: new Date(2023, 7, 18),
        valueType: 'DateTime',
        labelFormat: 'MMM d',
        lineStyle: { width: 0 },
        majorGridLines: { width: 0 },
        interval: Browser.isDevice ? 2 : 1,
        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
        labelRotation: Browser.isDevice ? -45 : 0
      },
      primaryYAxis1: {
        labelFormat: 'n2',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 0.86,
        maximum: 0.96,
        interval: 0.025
      },
      primaryYAxis2: {
        labelFormat: 'n1',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 79,
        maximum: 85,
        interval: 1.5
      },
      border: { width: 2 },
      zoomSettings: {
        enableMouseWheelZooming: true,
        enablePinchZooming: true,
        enableScrollbar: false,
        enableDeferredZooming: false,
        enableSelectionZooming: true,
        enablePan: true,
        mode: 'X',
        toolbarItems: ['Pan', 'Reset']
      },
      emptyPointSettings: { mode: 'Drop' },
      titleStyle: { textAlignment: 'Near' },
      title1: "US to EURO",
      title2: "US to INR",
      charts: []
    };
  },
  provide: {
    chart: [LineSeries, SplineAreaSeries, DateTime, Zoom]
  },
  methods: {
    zoomComplete: function (args) {
      if (args.axis.name === 'primaryXAxis') {
        zoomFactor = args.currentZoomFactor;
        zoomPosition = args.currentZoomPosition;
        this.zoomCompleteFunction(args);
      }
    },
    zoomCompleteFunction: function (args) {
      for (var i = 0; i < this.charts.length; i++) {
        if (args.axis.series[0].chart.element.id !== this.charts[i].element.id) {
          this.charts[i].primaryXAxis.zoomFactor = zoomFactor;
          this.charts[i].primaryXAxis.zoomPosition = zoomPosition;
          this.charts[i].zoomModule.isZoomed = args.axis.series[0].chart.zoomModule.isZoomed;
          this.charts[i].zoomModule.isPanning = args.axis.series[0].chart.zoomModule.isPanning;
        }
      }
    },
  },
  mounted() {
    this.charts = [this.$refs.chart1.ej2Instances, this.$refs.chart2.ej2Instances];
  }
};
</script>
<style>
#container {
  height: 350px;
}

#control-container {
  padding: 1px !important;
}

.row {
  display: flex;
}

.col {
  width: 50%;
  margin: 10px;
  height: 270px;
}
</style>

Selection synchronization

You can select the data across multiple charts using the selectionComplete event. In the selectionComplete event, obtain the selected values of the particular chart, and then apply those values to the other charts.

<template>
  <div class="control-section">
    <div class="row">
      <div class="col" id="container1">
        <ejs-chart style='display:block' align='center' id='chartcontainer1' :title='title1'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis1' :zoomSettings='zoomSettings'
          :titleStyle='titleStyle' :zoomComplete='zoomComplete' :selectionComplete='selectionComplete' ref="chart1">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Line' xName='USD' yName='EUR' width=2
              :emptyPointSetting='emptyPointSettings'> </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
      <div class="col" id="container2">
        <ejs-chart style='display:block' align='center' id='chartcontainer2' :title='title2'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis2' :zoomSettings='zoomSettings'
          :titleStyle='titleStyle' :zoomComplete='zoomComplete' :selectionComplete='selectionComplete' ref="chart2">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Spline' xName='USD' yName='INR' width=2 :border='border'>
            </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
    </div>
  </div>
</template>
<script setup>
import { provide } from "vue";
import { ref, onMounted } from 'vue';
import { ChartComponent as EjsChart, SeriesCollectionDirective as ESeriesCollection, SeriesDirective as ESeries, SplineSeries, LineSeries, DateTime, Zoom, Selection } from "@syncfusion/ej2-vue-charts";
import { synchronizedData } from './dataSource.js';
import { Browser } from '@syncfusion/ej2-base';

const chart1 = ref(null);
const chart2 = ref(null);

let zoomFactor = 0;
let zoomPosition = 0;
let count = 0;


const seriesData = synchronizedData;
const primaryXAxis = {
  minimum: new Date(2023, 1, 18),
  maximum: new Date(2023, 7, 18),
  valueType: 'DateTime',
  labelFormat: 'MMM d',
  lineStyle: { width: 0 },
  majorGridLines: { width: 0 },
  interval: Browser.isDevice ? 2 : 1,
  edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
  labelRotation: Browser.isDevice ? -45 : 0
};
const primaryYAxis1 = {
  labelFormat: 'n2',
  majorTickLines: { width: 0 },
  lineStyle: { width: 0 },
  minimum: 0.86,
  maximum: 0.96,
  interval: 0.025
};
const primaryYAxis2 = {
  labelFormat: 'n1',
  majorTickLines: { width: 0 },
  lineStyle: { width: 0 },
  minimum: 79,
  maximum: 85,
  interval: 1.5
};
const border = { width: 2 };
const zoomSettings = {
  enableSelectionZooming: true,
  mode: 'X'
};
const emptyPointSettings = { mode: 'Drop' };
const titleStyle = { textAlignment: 'Near' };
const title1 = "US to EURO";
const title2 = "US to INR";
let charts = [];

provide('chart', [SplineSeries, LineSeries, DateTime, Zoom, Selection]);

const zoomComplete = (args) => {
  if (args.axis.name === 'primaryXAxis') {
    zoomFactor = args.currentZoomFactor;
    zoomPosition = args.currentZoomPosition;
    zoomCompleteFunction(args);
  }
};
const zoomCompleteFunction = (args) => {
  for (var i = 0; i < charts.length; i++) {
    if (args.axis.series[0].chart.element.id !== charts[i].element.id) {
      charts[i].primaryXAxis.zoomFactor = zoomFactor;
      charts[i].primaryXAxis.zoomPosition = zoomPosition;
      charts[i].zoomModule.isZoomed = args.axis.series[0].chart.zoomModule.isZoomed;
      charts[i].zoomModule.isPanning = args.axis.series[0].chart.zoomModule.isPanning;
    }
  }
};
const selectionComplete = (args) => {
  if (count == 0) {
    for (var j = 0; j < args.selectedDataValues.length; j++) {
      args.selectedDataValues[j].point = args.selectedDataValues[j].pointIndex;
      args.selectedDataValues[j].series = args.selectedDataValues[j].seriesIndex;
    }
    for (var i = 0; i < charts.length; i++) {
      if (args.chart.element.id !== charts[i].element.id) {
        charts[i].selectedDataIndexes = args.selectedDataValues;
        count += 1;
        charts[i].dataBind();
      }
    }
    count = 0;
  }
};

onMounted(() => {
  charts = [chart1.value.ej2Instances, chart2.value.ej2Instances];
})

</script>
<style>
#container {
  height: 350px;
}

#control-container {
  padding: 1px !important;
}

.row {
  display: flex;
}

.col {
  width: 50%;
  margin: 10px;
  height: 270px;
}
</style>
<template>
  <div class="control-section">
    <div class="row">
      <div class="col" id="container1">
        <ejs-chart style='display:block' align='center' id='chartcontainer1' :title='title1'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis1' :zoomSettings='zoomSettings'
          :titleStyle='titleStyle' :zoomComplete='zoomComplete' :selectionComplete='selectionComplete' ref="chart1">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Line' xName='USD' yName='EUR' width=2
              :emptyPointSetting='emptyPointSettings'> </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
      <div class="col" id="container2">
        <ejs-chart style='display:block' align='center' id='chartcontainer2' :title='title2'
          :primaryXAxis='primaryXAxis' :primaryYAxis='primaryYAxis2' :zoomSettings='zoomSettings'
          :titleStyle='titleStyle' :zoomComplete='zoomComplete' :selectionComplete='selectionComplete' ref="chart2">
          <e-series-collection>
            <e-series :dataSource='seriesData' type='Spline' xName='USD' yName='INR' width=2 :border='border'>
            </e-series>
          </e-series-collection>
        </ejs-chart>
      </div>
    </div>
  </div>
</template>
<script>

import { ChartComponent, SeriesDirective, SeriesCollectionDirective, SplineSeries, LineSeries, DateTime, Zoom, Selection } from "@syncfusion/ej2-vue-charts";
import { synchronizedData } from './dataSource.js';
import { Browser } from '@syncfusion/ej2-base';

let zoomFactor = 0;
let zoomPosition = 0;
let count = 0;
export default {
  name: "App",
  components: {
    "ejs-chart": ChartComponent,
    "e-series-collection": SeriesCollectionDirective,
    "e-series": SeriesDirective
  },
  data() {
    return {
      seriesData: synchronizedData,
      primaryXAxis: {
        minimum: new Date(2023, 1, 18),
        maximum: new Date(2023, 7, 18),
        valueType: 'DateTime',
        labelFormat: 'MMM d',
        lineStyle: { width: 0 },
        majorGridLines: { width: 0 },
        interval: Browser.isDevice ? 2 : 1,
        edgeLabelPlacement: Browser.isDevice ? 'None' : 'Shift',
        labelRotation: Browser.isDevice ? -45 : 0
      },
      primaryYAxis1: {
        labelFormat: 'n2',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 0.86,
        maximum: 0.96,
        interval: 0.025
      },
      primaryYAxis2: {
        labelFormat: 'n1',
        majorTickLines: { width: 0 },
        lineStyle: { width: 0 },
        minimum: 79,
        maximum: 85,
        interval: 1.5
      },
      border: { width: 2 },
      zoomSettings: {
        enableSelectionZooming: true,
        mode: 'X'
      },
      emptyPointSettings: { mode: 'Drop' },
      titleStyle: { textAlignment: 'Near' },
      title1: "US to EURO",
      title2: "US to INR",
      charts: []
    };
  },
  provide: {
    chart: [SplineSeries, LineSeries, DateTime, Zoom, Selection]
  },
  methods: {
    zoomComplete: function (args) {
      if (args.axis.name === 'primaryXAxis') {
        zoomFactor = args.currentZoomFactor;
        zoomPosition = args.currentZoomPosition;
        this.zoomCompleteFunction(args);
      }
    },
    zoomCompleteFunction: function (args) {
      for (var i = 0; i < this.charts.length; i++) {
        if (args.axis.series[0].chart.element.id !== this.charts[i].element.id) {
          this.charts[i].primaryXAxis.zoomFactor = zoomFactor;
          this.charts[i].primaryXAxis.zoomPosition = zoomPosition;
          this.charts[i].zoomModule.isZoomed = args.axis.series[0].chart.zoomModule.isZoomed;
          this.charts[i].zoomModule.isPanning = args.axis.series[0].chart.zoomModule.isPanning;
        }
      }
    },
    selectionComplete: function (args) {
      if (count == 0) {
        for (var j = 0; j < args.selectedDataValues.length; j++) {
          args.selectedDataValues[j].point = args.selectedDataValues[j].pointIndex;
          args.selectedDataValues[j].series = args.selectedDataValues[j].seriesIndex;
        }
        for (var i = 0; i < this.charts.length; i++) {
          if (args.chart.element.id !== this.charts[i].element.id) {
            this.charts[i].selectedDataIndexes = args.selectedDataValues;
            count += 1;
            this.charts[i].dataBind();
          }
        }
        count = 0;
      }
    },
  },
  mounted() {
    this.charts = [this.$refs.chart1.ej2Instances, this.$refs.chart2.ej2Instances];
  }
};
</script>
<style>
#container {
  height: 350px;
}

#control-container {
  padding: 1px !important;
}

.row {
  display: flex;
}

.col {
  width: 50%;
  margin: 10px;
  height: 270px;
}
</style>