Skip to content

Commit 2a8fda7

Browse files
committed
added a trendline plugin
1 parent 923aa0e commit 2a8fda7

1 file changed

Lines changed: 123 additions & 0 deletions

File tree

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*!
2+
* chartjs-plugin-trendline.js
3+
* Version: 0.1.3
4+
*
5+
* Copyright 2017 Marcus Alsterfjord
6+
* Released under the MIT license
7+
* https://github.com/Makanz/chartjs-plugin-trendline/blob/master/README.md
8+
*
9+
* Mod by: vesal: accept also xy-data so works with scatter
10+
*/
11+
var pluginTrendlineLinear = {
12+
beforeDraw: function(chartInstance) {
13+
var yScale;
14+
var xScale;
15+
for (var axis in chartInstance.scales) {
16+
if ( axis[0] == 'x')
17+
xScale = chartInstance.scales[axis];
18+
else
19+
yScale = chartInstance.scales[axis];
20+
if ( xScale && yScale ) break;
21+
}
22+
var ctx = chartInstance.chart.ctx;
23+
24+
chartInstance.data.datasets.forEach(function(dataset, index) {
25+
if (dataset.trendlineLinear && chartInstance.isDatasetVisible(index)) {
26+
var datasetMeta = chartInstance.getDatasetMeta(index);
27+
addFitter(datasetMeta, ctx, dataset, xScale, yScale);
28+
}
29+
});
30+
31+
ctx.setLineDash([]);
32+
}
33+
};
34+
35+
function addFitter(datasetMeta, ctx, dataset, xScale, yScale) {
36+
if(datasetMeta.data == [] || datasetMeta.data == null || datasetMeta.data.length == 0) return;
37+
var style = dataset.trendlineLinear.style || dataset.borderColor;
38+
var lineWidth = dataset.trendlineLinear.width || dataset.borderWidth;
39+
var lineStyle = dataset.trendlineLinear.lineStyle || "solid";
40+
41+
style = (style !== undefined) ? style : "rgba(169,169,169, .6)";
42+
lineWidth = (lineWidth !== undefined) ? lineWidth : 3;
43+
44+
var fitter = new LineFitter();
45+
var lastIndex = dataset.data.length - 1;
46+
var startPos = datasetMeta.data[0]._model.x;
47+
var endPos = datasetMeta.data[lastIndex]._model.x;
48+
49+
var xy = false;
50+
if ( dataset.data && typeof dataset.data[0] === 'object') xy = true;
51+
52+
dataset.data.forEach(function(data, index) {
53+
if(data == null)
54+
return;
55+
if ( xy ) fitter.add(data.x, data.y);
56+
else fitter.add(index, data);
57+
});
58+
59+
var x1 = xScale.getPixelForValue(fitter.minx);
60+
var x2 = xScale.getPixelForValue(fitter.maxx);
61+
var y1 = yScale.getPixelForValue(fitter.f(fitter.minx));
62+
var y2 = yScale.getPixelForValue(fitter.f(fitter.maxx));
63+
if ( !xy ) { x1 = startPos; x2 = endPos; }
64+
65+
var drawBottom = datasetMeta.controller.chart.chartArea.bottom;
66+
var chartWidth = datasetMeta.controller.chart.width;
67+
68+
if(y1 > drawBottom) { // Left side is below zero
69+
var diff = y1 - drawBottom;
70+
var lineHeight = y1 - y2;
71+
var overlapPercentage = diff / lineHeight;
72+
var addition = chartWidth * overlapPercentage;
73+
74+
y1 = drawBottom;
75+
x1 = (x1 + addition);
76+
} else if(y2 > drawBottom) { // right side is below zero
77+
var diff = y2 - drawBottom;
78+
var lineHeight = y2 - y1;
79+
var overlapPercentage = diff / lineHeight;
80+
var subtraction = chartWidth - (chartWidth * overlapPercentage);
81+
82+
y2 = drawBottom;
83+
x2 = chartWidth - (x2 - subtraction);
84+
}
85+
86+
ctx.lineWidth = lineWidth;
87+
if (lineStyle === "dotted") { ctx.setLineDash([2, 3]); }
88+
ctx.beginPath();
89+
ctx.moveTo(x1, y1);
90+
ctx.lineTo(x2, y2);
91+
ctx.strokeStyle = style;
92+
ctx.stroke();
93+
}
94+
95+
Chart.plugins.register(pluginTrendlineLinear);
96+
97+
function LineFitter() {
98+
this.count = 0;
99+
this.sumX = 0;
100+
this.sumX2 = 0;
101+
this.sumXY = 0;
102+
this.sumY = 0;
103+
this.minx = 1e100;
104+
this.maxx = -1e100;
105+
}
106+
107+
LineFitter.prototype = {
108+
'add': function (x, y) {
109+
this.count++;
110+
this.sumX += x;
111+
this.sumX2 += x * x;
112+
this.sumXY += x * y;
113+
this.sumY += y;
114+
if ( x < this.minx ) this.minx = x;
115+
if ( x > this.maxx ) this.maxx = x;
116+
},
117+
'f': function (x) {
118+
var det = this.count * this.sumX2 - this.sumX * this.sumX;
119+
var offset = (this.sumX2 * this.sumY - this.sumX * this.sumXY) / det;
120+
var scale = (this.count * this.sumXY - this.sumX * this.sumY) / det;
121+
return offset + x * scale;
122+
}
123+
};

0 commit comments

Comments
 (0)