Add second Y axis for Linechart in Chart.js?

I would like to add a second Y-axis on the line, possibly on the right side of the canvas . I tried using Chart.js , taken from https://github.com/Wikunia/Chart.js/tree/Double-Y-Axis , in which LineDoubleY defined. But:

  • I do not see an example in Firefox:

ReferenceError: xPos not defined Chart.js: 1147: 7

  1. If I use this Chart.js in my application:

currentChart.addData is not a function

This says: is it possible to add a second Y-axis in a different way?

+5
linechart
source share
2 answers

Here is a revised version of the original with a bit more flexibility. The logic is almost the same, but applies to more than 2 data sets


Preview

enter image description here


Script

 Chart.types.Line.extend({ name: "Line2Y", getScale: function(data) { var startPoint = this.options.scaleFontSize; var endPoint = this.chart.height - (this.options.scaleFontSize * 1.5) - 5; return Chart.helpers.calculateScaleRange( data, endPoint - startPoint, this.options.scaleFontSize, this.options.scaleBeginAtZero, this.options.scaleIntegersOnly); }, initialize: function (data) { var y2datasetLabels = []; var y2data = []; var y1data = []; data.datasets.forEach(function (dataset, i) { if (dataset.y2axis == true) { y2datasetLabels.push(dataset.label); y2data = y2data.concat(dataset.data); } else { y1data = y1data.concat(dataset.data); } }); // use the helper function to get the scale for both datasets var y1Scale = this.getScale(y1data); this.y2Scale = this.getScale(y2data); var normalizingFactor = y1Scale.max / this.y2Scale.max; // update y2 datasets data.datasets.forEach(function(dataset) { if (y2datasetLabels.indexOf(dataset.label) !== -1) { dataset.data.forEach(function (e, j) { dataset.data[j] = e * normalizingFactor; }) } }) // denormalize tooltip for y2 datasets this.options.multiTooltipTemplate = function (d) { if (y2datasetLabels.indexOf(d.datasetLabel) !== -1) return Math.round(d.value / normalizingFactor, 6); else return d.value; } Chart.types.Line.prototype.initialize.apply(this, arguments); }, draw: function () { this.scale.xScalePaddingRight = this.scale.xScalePaddingLeft; Chart.types.Line.prototype.draw.apply(this, arguments); this.chart.ctx.textAlign = 'left'; this.chart.ctx.textBaseline = "middle"; this.chart.ctx.fillStyle = "#666"; var yStep = (this.scale.endPoint - this.scale.startPoint) / this.y2Scale.steps for (var i = 0, y = this.scale.endPoint, label = this.y2Scale.min; i <= this.y2Scale.steps; i++) { this.chart.ctx.fillText(label, this.chart.width - this.scale.xScalePaddingRight + 10, y); y -= yStep; label += this.y2Scale.stepValue } } }); 

You are sending a dataset to the y2 axis with an additional property (y2axis: true). for example

 { label: "My Second dataset", fillColor: "rgba(151,187,205,0.5)", strokeColor: "rgba(151,187,205,1)", pointColor: "rgba(151,187,205,1)", pointStrokeColor: "#fff", data: [150, 48, 120, 19, 46, 27, 100], y2axis: true } 

Fiddle - http://jsfiddle.net/1va2kx18/


You can use one shade of colors for a series along the y axes, and another for colors on the y2 axes (otherwise this is a bit confusing). In addition, you can change the hint function to show y2 values ​​in a slightly different way. for example

 return '[' + Math.round(d.value / normalizingFactor, 6) + ']'; 

puts square brackets around y2 values ​​in tooltip


If you add new points to datasets using addData, there is an issue where dataset labels are not updated in the news, added moments that you must bypass by updating the addData function.

If you don't want to do this, simply use the colors of the dataset points (instead of using dataset labels) to distinguish between the y and y2 series if you use different dot colors for y and y2. Here are the lines that should be replaced in

  var y2datasetColors = []; ... y2datasetColors.push(dataset.pointColor); ... if (y2datasetColors.indexOf(dataset.pointColor) !== -1) { ... if (y2datasetColors.indexOf(d._saved.fillColor) !== -1) 

where you previously had y2datasets

+9
source share

https://github.com/Wikunia/Chart.js/tree/Double-Y-Axis was forked from the early (2 years ago) version of Chart.js

currentChart.addData is not a function

This plug does not have this feature. That is why you get this error.

By the way, you can take a look at the latest version of Chart.js. There is an alpha version issue - https://github.com/nnnick/Chart.js/issues/17


This says: is it possible to add the second Y axis differently?

With the current version? Yes, if you are willing to compromise on several parameters. I believe you could get around these tradeoffs, but the added complexity is too much :-)

High level steps

  • One of the data sets controls the scale β€” select another data set, determine the scale based on it, and then normalize the values ​​based on that scale and the actual scale.
  • You do not want the tooltips to display a normalized value, so you need to change the tooltip function to denormalize the values
  • Mark labels for secondary y axis

The first 2 can be performed in redefinition of initialization and in the last 1 in redefinition of a draw.


Declaration and Initialization

Of course, we need to expand the chart first. And let's start the scale from 0 and turn off the grid lines to reduce complexity.

So,

 Chart.types.Line.extend({ name: "Line2Y", 

and

 var ctx = document.getElementById("chart").getContext("2d"); var myLine1 = new Chart(ctx).Line2Y(lineChartData1, { scaleBeginAtZero: true, scaleShowGridLines: false }); 

Normalizing factor calculation

 initialize: function (data) { // figure out which dataset has the max value - that is the one that drives the scale var max = 0; var datasetToNotScale = 0; var datasetToScale = 1; data.datasets.forEach(function (dataset, i) { dataset.data.forEach(function (e) { if (e > max) { max = e; datasetToNotScale = i; datasetToScale = (i == 0 ? 1 : 0); } }) }) var datasetToScaleLabel = data.datasets[datasetToScale].label; var startPoint = this.options.scaleFontSize; var endPoint = this.chart.height - (this.options.scaleFontSize * 1.5) - 5; // use the helper function to get the scale for both datasets var notScaleRange = Chart.helpers.calculateScaleRange( data.datasets[datasetToNotScale].data, endPoint - startPoint, this.options.scaleFontSize, this.options.scaleBeginAtZero, this.options.scaleIntegersOnly ) this.scaleRange = Chart.helpers.calculateScaleRange( data.datasets[datasetToScale].data, endPoint - startPoint, this.options.scaleFontSize, this.options.scaleBeginAtZero, this.options.scaleIntegersOnly ) 

Once we have a scale for both datasets, calculate the normalizing coefficient (the ratio of the maximum value of both scales, since we set the scale of the scale starting at 0)

 var normalizingFactor = notScaleRange.max / this.scaleRange.max; 

Normalization (for plotting) and Denormalization (for tooltips)

Use this to update a dataset that does not control scale.

 // update one of our datasets! data.datasets[datasetToScale].data.forEach(function (e, i) { data.datasets[datasetToScale].data[i] = e * normalizingFactor; }) 

And of course, counteract this by denormalizing in the tooltip function (pay attention to Math.round - which takes care of a slight loss in the accuracy of the conversion back and forth)

 this.options.multiTooltipTemplate = function (d) { if (d.datasetLabel == datasetToScaleLabel) return Math.round(d.value / normalizingFactor, 6); else return d.value; } 

Secondary axis label display

First, make sure you have enough space on the right side.

 draw: function () { this.scale.xScalePaddingRight = this.scale.xScalePaddingLeft; 

Then, as soon as the actual chart is drawn, draw our marks on the secondary axis

 this.chart.ctx.font = Chart.helpers.fontString(self.fontSize, self.fontStyle, self.fontFamily) this.chart.ctx.textAlign = 'left'; this.chart.ctx.textBaseline = "middle"; this.chart.ctx.fillStyle = "#666"; var label = this.scaleRange.min; var yStep = (this.scale.endPoint - this.scale.startPoint) / this.scaleRange.steps for (var i = 0, y = this.scale.endPoint; i <= this.scaleRange.steps; i++) { this.chart.ctx.fillText(label, this.chart.width - this.scale.xScalePaddingRight + 10, y); y -= yStep; label += this.scaleRange.stepValue } 

and we are done!


Fiddle - http://jsfiddle.net/u2Lru6vv/

Note. Overlapping two charts with the y-axis (as we did above) is another (slightly less invasive) parameter, but the problem is that you are losing tooltips for the base chart.

+1
source share

All Articles