score:9
Here is a revised version of the original with a little more flexibility. The logic is pretty much the same, but extended to more than 2 datasets
Preview
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 send 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 could use one shade of colors for the series on the y axes and another for colors on the y2 axes (otherwise it's a bit confusing). Additionally you could modify your tooltip function to show the y2 values a bit differently. For example
return '[' + Math.round(d.value / normalizingFactor, 6) + ']';
would put square brackets around y2 values in the tooltip
If you are adding new points to the datasets using addData, there is an issue with dataset labels not being updated in the newly added points that you have to work around by updating the addData function.
If you don't want to do that just use the dataset point colors (instead of using dataset labels) to distinguish between the y and y2 series IF you use distinct point colors for y and y2 series. Here are the lines to substitute 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
score:1
https://github.com/Wikunia/Chart.js/tree/Double-Y-Axis was forked from an early (2 years back) version of Chart.js
currentChart.addData is not a function
The fork does not have this function. That's why you are getting this error.
You might want to take a look at the latest version of Chart.js by the way. There is a related issue that's available in the alpha version - https://github.com/nnnick/Chart.js/issues/17
This said: Is it possible to add a second Y-axis in another way?
With the current version? Yes, if you are willing to compromise on a couple of options. I believe you could workaround these compromises too, but the added complexity is a bit too much :-)
High Level Steps
- One of the datasets drives the scale - pick the other dataset, figure out the scale based on it alone and then normalize the values based on this scale and the actual scale
- You don't want the tooltips to show the normalized value, so you need to modify the tooltip function to denormalize the values
- Render the labels for the secondary y axis
The first 2 can be done in the initialize override and the last 1 in the draw override.
Declaration and Initialization
Of course, we need to extend the chart first. And let's begin the scale at 0 and turn off the grid lines to reduce the 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
});
Calculating the Normalizing Factor
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 the scale for both datasets, calculate the normalizing factor (ratio of max value of both scales, since we set the chart scale to begin at 0)
var normalizingFactor = notScaleRange.max / this.scaleRange.max;
Normalizing (for plotting) and Denormalizing (for the Tooltips)
Use this to update the dataset that does not drive the 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 (notice the Math.round - that takes care of a slight loss of precision converting back and forth)
this.options.multiTooltipTemplate = function (d) {
if (d.datasetLabel == datasetToScaleLabel)
return Math.round(d.value / normalizingFactor, 6);
else
return d.value;
}
Rendering the Secondary Axis Labels
First make sure you have enough space on the right hand side
draw: function () {
this.scale.xScalePaddingRight = this.scale.xScalePaddingLeft;
Then, once the actual chart is drawn, draw our secondary axis labels
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 - overlaying 2 charts with a mirrored y axis (like we did above) is another (slightly less invasive) option, but the problem is you lose tooltips for the underlying chart.
Source: stackoverflow.com
Related Query
- How to add second Y-axis for Bar and Line chart in Chart.js?
- Add a second Y-axis for Linechart in Chart.js?
- show label in tooltip but not in x axis for chartjs line chart
- Numeric X axis for linechart
- Chart JS - set start of week for x axis time series
- How to hide the y axis and x axis line and label in my bar chart for chart.js
- Chart js different background for y axis
- Add a text as tooltip for each point on a line chart
- How to add label in chart.js for polar chart area
- Add buffer to Y axis with chart js
- chart.js add second line in a Line chart that starts from end of first line
- add info for points in line chart (js)
- How do I add time sourced from an external source as an X axis to a ChartJS graph?
- Minimum value for x Axis doesn't work for horizontal bar chart | ChartJS
- How to add null value rows into pandas dataframe for missing years in a multi-line chart plot
- Chartjs - Add backgroundColor for labels radar chart
- Chart Js reduce text size for label on X axis
- Linear x axis for bar chart chartjs
- How to add new x axis in chart JS?
- Chart.js two y axes line chart tooltip value incorrect for 2nd y axis
- How do you set x and y axis and Title for a line chart using charts.js?
- how to add second axes to chart JS
- ChartJS/High Charts Radar chart - Different radial axis labels for each category that appear on hover
- chart.js Second hidden value for Bar Chart
- Chart JS tick options not working for y axis
- How to add custom text inside the bar and how to reduce the step size in y axis in chart js( Bar chart )
- How to start Y Axis Line at 0 from right for negative values in chart js 2?
- How to Add X axis Padding in chart js Line Graph
- getting additional value fields from data source for dx.chartjs doughnut chart
- I am using chart js to draw a chart. I did everything right but i don't know why the x axis and y axis label is not comming in chart. code below
More Query from same tag
- Setting min and max values chart.js
- How to add an extra legend item in chart.js?
- Chart.js Bar Chart - grouping and mapping data
- "The radius provided (-0.5) is negative" when used with Modal
- Space between start and end of axis
- ChartJS v2: how to remove strange line in Line Chart
- Chart.JS Multiline Chart with unknown numbers of Lines from JSON
- how to remove old data from Chart js on mouse hover using mvc c#?
- Add options to Charts.js
- Tooltip overriding the text in chart js
- Chart.js - draw an arbitrary point with tooltip
- Chart.js zoom start at zoomed in
- add shadow for line in vue chartjs
- Charts js and laravel: Render chart after passing in json data
- How to set axes' step size in Chart.js 2?
- chart.js - display control timescale time zone
- angular chartjs not showing data properly
- JavaScript - Convert dates and times (Chart.js and Moment.js)
- Angular ChartJS 2.9.4 issue updating chart
- How to bind touchmove in chartjs canvas?
- Multiple charts in background ChartJS
- How to create dynamic D3 tickFormat in dc charts
- How can I pass the data into the chartjs label?
- Chart.js tooltips callback function with pie charts
- Chart.js 2.0.0-beta2 disable points on line Charts
- How to plot date/time on X but display only dates in Chart.js?
- Chart js - render images on only one dataset
- ChartJS xAxis label position
- How to hide zero data value from chartjs?
- Change bar color depending on value