Example shows synchronized chart in Vuejs with tooltip, crosshair, zooming / panning synced across charts. For example, if you mouse over on one chart to show tooltip, it automatically updates tooltip across all the other charts.
/* App.vue */ <script> import data from './assets/server-metrics.json'; export default { data() { let userDps=[], systemDps=[], waitDps=[], buffersDps=[], cacheDps=[], usedDps=[], inboundDps=[], outboundDps=[], writeDps=[], readDps=[], toolTip = { shared: true }, legend = { cursor: "pointer", itemclick: function (e) { if (typeof (e.dataSeries.visible) === "undefined" || e.dataSeries.visible) { e.dataSeries.visible = false; } else { e.dataSeries.visible = true; } e.chart.render(); } }; return { userDps: userDps, systemDps: systemDps, waitDps: waitDps, buffersDps:buffersDps, cacheDps:cacheDps, usedDps:usedDps, inboundDps:inboundDps, outboundDps:outboundDps, writeDps:writeDps, readDps:readDps, charts: [], jsonData: data, cpuChartOptions: { animationEnabled: true, theme: "light2", // "light1", "light2", "dark1", "dark2" title:{ text: "CPU Utilization" }, toolTip: toolTip, axisY: { valueFormatString: "#0.#%", }, legend: legend, data: [{ type: "splineArea", showInLegend: "true", name: "User", yValueFormatString: "#0.#%", color: "#80DEEA", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", legendMarkerType: "square", dataPoints: userDps },{ type: "splineArea", showInLegend: "true", name: "System", yValueFormatString: "#0.#%", color: "#26C6DA", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", legendMarkerType: "square", dataPoints: systemDps },{ type: "splineArea", showInLegend: "true", name: "Wait", yValueFormatString: "#0.#%", color: "#00ACC1", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", legendMarkerType: "square", dataPoints: waitDps }] }, memoryChartOptions: { animationEnabled: true, theme: "light2", title:{ text: "Memory Usage" }, axisY: { suffix: " GB" }, toolTip: toolTip, legend: legend, data: [{ type: "splineArea", showInLegend: "true", name: "Cache", color: "#EF9A9A", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", yValueFormatString: "#.## GB", legendMarkerType: "square", dataPoints: cacheDps },{ type: "splineArea", showInLegend: "true", name: "Buffers", color: "#EF5350", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", yValueFormatString: "#.## GB", legendMarkerType: "square", dataPoints: buffersDps },{ type: "splineArea", showInLegend: "true", name: "Used", color: "#E53935", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", yValueFormatString: "#.## GB", legendMarkerType: "square", dataPoints: usedDps }] }, networkChartOptions: { animationEnabled: true, theme: "light2", title:{ text: "Network Traffic" }, axisY: { suffix: " Kb/s" }, toolTip: toolTip, legend: legend, data: [{ type: "splineArea", showInLegend: "true", name: "Outbound", color: "#C5E1A5", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", yValueFormatString: "#.## Kb/s", legendMarkerType: "square", dataPoints: outboundDps },{ type: "splineArea", showInLegend: "true", name: "Inbound", color: "#43A047", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", yValueFormatString: "#.## Kb/s", legendMarkerType: "square", dataPoints: inboundDps }] }, diskChartOptions: { animationEnabled: true, theme: "light2", title:{ text: "Disk I/O (IOPS)" }, axisY: {}, toolTip: toolTip, legend: legend, data: [{ type: "splineArea", showInLegend: "true", name: "Write", color: "#FFAB91", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", yValueFormatString: "#.## ops/second", legendMarkerType: "square", dataPoints: writeDps },{ type: "splineArea", showInLegend: "true", name: "Read", color: "#F4511E", xValueType: "dateTime", xValueFormatString: "DD MMM YY HH:mm", yValueFormatString: "#.## ops/second", legendMarkerType: "square", dataPoints: readDps }] }, styleOptions: { width: "50%", height: "360px", float: "left" } } }, methods: { chartRef(chartInstance) { this.charts.push(chartInstance); }, syncCharts(charts, syncToolTip, syncCrosshair, syncAxisXRange) { if(!this.onToolTipUpdated){ this.onToolTipUpdated = function(e) { for (var j = 0; j < charts.length; j++) { if (charts[j] != e.chart) charts[j].toolTip.showAtX(e.entries[0].xValue); } } } if(!this.onToolTipHidden){ this.onToolTipHidden = function(e) { for( var j = 0; j < charts.length; j++){ if(charts[j] != e.chart) charts[j].toolTip.hide(); } } } if(!this.onCrosshairUpdated){ this.onCrosshairUpdated = function(e) { for(var j = 0; j < charts.length; j++) { if(charts[j] != e.chart) charts[j].axisX[0].crosshair.showAt(e.value); } } } if(!this.onCrosshairHidden){ this.onCrosshairHidden = function(e) { for( var j = 0; j < charts.length; j++) { if(charts[j] != e.chart) charts[j].axisX[0].crosshair.hide(); } } } if(!this.onRangeChanged){ this.onRangeChanged = function(e) { for (var j = 0; j < charts.length; j++) { if (e.trigger === "reset") { charts[j].options.axisX.viewportMinimum = charts[j].options.axisX.viewportMaximum = null; charts[j].options.axisY.viewportMinimum = charts[j].options.axisY.viewportMaximum = null; charts[j].render(); } else if (charts[j] !== e.chart) { charts[j].options.axisX.viewportMinimum = e.axisX[0].viewportMinimum; charts[j].options.axisX.viewportMaximum = e.axisX[0].viewportMaximum; charts[j].render(); } } } } for(var i = 0; i < charts.length; i++) { //Sync ToolTip if(syncToolTip) { if(!charts[i].options.toolTip) charts[i].options.toolTip = {}; charts[i].options.toolTip.updated = this.onToolTipUpdated; charts[i].options.toolTip.hidden = this.onToolTipHidden; } //Sync Crosshair if(syncCrosshair) { if(!charts[i].options.axisX) charts[i].options.axisX = { labelAngle: 0, valueFormatString: "MMM D", crosshair: { enabled: true, snapToDataPoint: true, valueFormatString: "HH:mm" }}; charts[i].options.axisX.crosshair.updated = this.onCrosshairUpdated; charts[i].options.axisX.crosshair.hidden = this.onCrosshairHidden; } //Sync Zoom / Pan if(syncAxisXRange) { charts[i].options.zoomEnabled = true; charts[i].options.rangeChanged = this.onRangeChanged; } charts[i].render(); } } }, created() { for(var i = 0; i < this.jsonData.length; i++) { this.systemDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].system)}); this.userDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].user)}); this.waitDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].wait)}); this.buffersDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].buffers)}); this.cacheDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].cache)}); this.usedDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].used)}); this.inboundDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].inbound)}); this.outboundDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].outbound)}); this.writeDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].write)}); this.readDps.push({x: Number(this.jsonData[i].time), y: Number(this.jsonData[i].read)}); } }, mounted() { this.syncCharts(this.charts, true, true, true) } } </script> <template> <CanvasJSChart :options="cpuChartOptions" :styles="styleOptions" @chart-ref="chartRef"/> <CanvasJSChart :options="memoryChartOptions" :styles="styleOptions" @chart-ref="chartRef"/> <CanvasJSChart :options="networkChartOptions" :styles="styleOptions" @chart-ref="chartRef"/> <CanvasJSChart :options="diskChartOptions" :styles="styleOptions" @chart-ref="chartRef"/> </template>
/*main.js*/ import { createApp } from 'vue' import App from './App.vue' import CanvasJSChart from '@canvasjs/vue-charts'; const app = createApp(App); app.use(CanvasJSChart); // install the CanvasJS Vuejs Chart Plugin app.mount('#app');
In the above example, we are using following events and methods which you can further customize if required.
Chart - rangeChanging, rangeChanged
ToolTip - updated, hidden, showAtX, hide
Crosshair - updated, hidden, showAt, hide