chartjs-plugin-trendline 2.0.5 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/chartjs-plugin-trendline.min.js +2 -2
- package/dist/chartjs-plugin-trendline.min.js.LICENSE.txt +10 -10
- package/example/barChart.html +2 -2
- package/example/barChartWithNullValues.html +2 -2
- package/example/lineChart.html +73 -0
- package/example/lineChartProjection.html +2 -2
- package/package.json +2 -2
- package/src/chartjs-plugin-trendline.js +51 -98
- package/src/classes/lineFitter.js +52 -0
- package/example/lineChart-blood-sugar.html +0 -72
- package/example/lineChart-double.html +0 -111
- package/example/lineChart-time-scale.html +0 -67
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# chartjs-plugin-trendline
|
|
2
2
|
|
|
3
3
|
This plugin draws an linear trendline in your Chart.
|
|
4
|
-
It has been tested with Chart.js version 4.3.
|
|
4
|
+
It has been tested with Chart.js version 4.4.3.
|
|
5
5
|
|
|
6
6
|
## Installation
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@ It has been tested with Chart.js version 4.3.0.
|
|
|
10
10
|
Load Chart.js first, then the plugin which will automatically register itself with Chart.js
|
|
11
11
|
|
|
12
12
|
```html
|
|
13
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.3
|
|
13
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.min.js"></script>
|
|
14
14
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-trendline"></script>
|
|
15
15
|
```
|
|
16
16
|
|
|
@@ -38,6 +38,8 @@ To configure the trendline plugin you simply add a new config options to your da
|
|
|
38
38
|
colorMax: "green",
|
|
39
39
|
lineStyle: "dotted|solid",
|
|
40
40
|
width: 2,
|
|
41
|
+
xAxisKey: "time" (optional),
|
|
42
|
+
yAxisKey: "usage" (optional),
|
|
41
43
|
projection: true|false (optional)
|
|
42
44
|
}
|
|
43
45
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! For license information please see chartjs-plugin-trendline.min.js.LICENSE.txt */
|
|
2
|
-
(()=>{var t={
|
|
1
|
+
/*! For license information please see chartjs-plugin-trendline.min.js.LICENSE.txt */
|
|
2
|
+
(()=>{"use strict";var t={228:(t,e,s)=>{class i{constructor(){this.count=0,this.sumX=0,this.sumX2=0,this.sumXY=0,this.sumY=0,this.minx=1e100,this.maxx=-1e100,this.maxy=-1e100}add=(t,e)=>{t=parseFloat(t),e=parseFloat(e),this.count++,this.sumX+=t,this.sumX2+=t*t,this.sumXY+=t*e,this.sumY+=e,t<this.minx&&(this.minx=t),t>this.maxx&&(this.maxx=t),e>this.maxy&&(this.maxy=e)};f=t=>{t=parseFloat(t);let e=this.count*this.sumX2-this.sumX*this.sumX;return(this.sumX2*this.sumY-this.sumX*this.sumXY)/e+t*((this.count*this.sumXY-this.sumX*this.sumY)/e)};fo=()=>{let t=this.count*this.sumX2-this.sumX*this.sumX;return-(this.sumX2*this.sumY-this.sumX*this.sumXY)/t/((this.count*this.sumXY-this.sumX*this.sumY)/t)};scale=()=>{let t=this.count*this.sumX2-this.sumX*this.sumX;return(this.count*this.sumXY-this.sumX*this.sumY)/t}}t=s.hmd(t);const a={id:"chartjs-plugin-trendline",afterDatasetsDraw:t=>{let e,s;for(let i in t.scales)if("x"==i[0]?s=t.scales[i]:e=t.scales[i],s&&e)break;const i=t.ctx;t.data.datasets.forEach(((e,a)=>{const o=e.alwaysShowTrendline||t.isDatasetVisible(a);if(e.trendlineLinear&&o&&e.data.length>1){const o=t.getDatasetMeta(a);r(o,i,e,s,t.scales[o.yAxisID])}})),i.setLineDash([])}},r=(t,e,s,a,r)=>{let o=s.borderColor||"rgba(169,169,169, .6)",n=s.trendlineLinear.colorMin||o,l=s.trendlineLinear.colorMax||o,h=s.trendlineLinear.width??s.borderWidth??3,d=s.trendlineLinear.lineStyle||"solid",u=s.trendlineLinear.fillColor;const m=t.controller.chart.options,x="object"==typeof m.parsing?m.parsing:void 0,c=s.trendlineLinear.xAxisKey||x?.xAxisKey||"x",p=s.trendlineLinear.yAxisKey||x?.yAxisKey||"y";let f=new i,y=s.data.findIndex((t=>null!=t)),X=s.data.length-1,g=t.data[y][c],w=t.data[X][c],Y="object"==typeof s.data[y];s.data.forEach(((t,e)=>{if(null!=t)if(["time","timeseries"].includes(a.options.type)){let s=null!=t[c]?t[c]:t.t;void 0!==s?f.add(new Date(s).getTime(),t[p]):f.add(e,t)}else Y?isNaN(t.x)||isNaN(t.y)?isNaN(t.x)?isNaN(t.y)||f.add(e,t.y):f.add(e,t.x):f.add(t.x,t.y):f.add(e,t)}));let b,L,P=a.getPixelForValue(f.minx),C=r.getPixelForValue(f.f(f.minx));if(s.trendlineLinear.projection&&f.scale()<0){let t=f.fo();t<f.minx&&(t=f.maxx),b=a.getPixelForValue(t),L=r.getPixelForValue(f.f(t))}else b=a.getPixelForValue(f.maxx),L=r.getPixelForValue(f.f(f.maxx));Y||(P=g,b=w);let S=t.controller.chart.chartArea.bottom,D=t.controller.chart.width;if(C>S){let t=C-S,e=C-L;C=S,P+=D*(t/e)}else if(L>S){let t=L-S,e=L-C;L=S,b=D-(b-(D-D*(t/e)))}e.lineWidth=h,"dotted"===d?e.setLineDash([2,3]):e.setLineDash([]),e.beginPath(),e.moveTo(P,C),e.lineTo(b,L);let F=e.createLinearGradient(P,C,b,L);L<C?(F.addColorStop(0,l),F.addColorStop(1,n)):(F.addColorStop(0,n),F.addColorStop(1,l)),e.strokeStyle=F,e.stroke(),e.closePath(),u&&(e.fillStyle=u,e.beginPath(),e.moveTo(P,C),e.lineTo(b,L),e.lineTo(b,S),e.lineTo(P,S),e.closePath(),e.fill())};"undefined"!=typeof window&&window.Chart&&(window.Chart.hasOwnProperty("register")?window.Chart.register(a):window.Chart.plugins.register(a));try{t.exports=exports=a}catch(t){}}},e={};function s(i){var a=e[i];if(void 0!==a)return a.exports;var r=e[i]={id:i,loaded:!1,exports:{}};return t[i](r,r.exports,s),r.loaded=!0,r.exports}s.hmd=t=>((t=Object.create(t)).children||(t.children=[]),Object.defineProperty(t,"exports",{enumerable:!0,set:()=>{throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+t.id)}}),t),s(228)})();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* chartjs-plugin-trendline.js
|
|
3
|
-
* Version: 2.
|
|
4
|
-
*
|
|
5
|
-
* Copyright
|
|
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
|
-
*/
|
|
1
|
+
/*!
|
|
2
|
+
* chartjs-plugin-trendline.js
|
|
3
|
+
* Version: 2.1.1
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2024 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
|
+
*/
|
package/example/barChart.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
7
|
<title>BarChart Example</title>
|
|
8
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.3
|
|
9
|
-
<script src="./../
|
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.js"></script>
|
|
9
|
+
<script src="./../dist/chartjs-plugin-trendline.min.js"></script>
|
|
10
10
|
<script>
|
|
11
11
|
document.addEventListener("DOMContentLoaded", function(event) {
|
|
12
12
|
// Bar chart
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
7
|
<title>BarChart Example</title>
|
|
8
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.3
|
|
9
|
-
<script src="./../
|
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.js"></script>
|
|
9
|
+
<script src="./../dist/chartjs-plugin-trendline.min.js"></script>
|
|
10
10
|
<script>
|
|
11
11
|
document.addEventListener("DOMContentLoaded", function(event) {
|
|
12
12
|
// Bar chart
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
|
+
<title>LineChart Example</title>
|
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.js"></script>
|
|
9
|
+
<script src="./../dist/chartjs-plugin-trendline.min.js"></script>
|
|
10
|
+
<script>
|
|
11
|
+
document.addEventListener("DOMContentLoaded", function(event) {
|
|
12
|
+
new Chart(document.getElementById("line-chart"), {
|
|
13
|
+
type: 'line',
|
|
14
|
+
data: {
|
|
15
|
+
labels: [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050],
|
|
16
|
+
datasets: [{
|
|
17
|
+
data: [86,114,106,106,107,111,133,221,783,2478],
|
|
18
|
+
label: "Africa",
|
|
19
|
+
borderColor: "#3e95cd",
|
|
20
|
+
fill: false,
|
|
21
|
+
trendlineLinear: {
|
|
22
|
+
colorMin: "#3e95cd",
|
|
23
|
+
lineStyle: "line",
|
|
24
|
+
width: 1
|
|
25
|
+
}
|
|
26
|
+
}, {
|
|
27
|
+
data: [282,350,411,502,635,809,947,1402,3700,5267],
|
|
28
|
+
label: "Asia",
|
|
29
|
+
borderColor: "#8e5ea2",
|
|
30
|
+
fill: false,
|
|
31
|
+
trendlineLinear: {
|
|
32
|
+
colorMin: "red",
|
|
33
|
+
colorMax: "green",
|
|
34
|
+
lineStyle: "line",
|
|
35
|
+
width: 1
|
|
36
|
+
}
|
|
37
|
+
}, {
|
|
38
|
+
data: [168,170,178,190,203,276,408,547,675,734],
|
|
39
|
+
label: "Europe",
|
|
40
|
+
borderColor: "#3cba9f",
|
|
41
|
+
fill: false
|
|
42
|
+
}, {
|
|
43
|
+
data: [40,20,10,16,24,38,74,167,508,784],
|
|
44
|
+
label: "Latin America",
|
|
45
|
+
borderColor: "#e8c3b9",
|
|
46
|
+
fill: false
|
|
47
|
+
}, {
|
|
48
|
+
data: [6,3,2,2,7,26,82,172,312,433],
|
|
49
|
+
label: "North America",
|
|
50
|
+
borderColor: "#c45850",
|
|
51
|
+
fill: false
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
options: {
|
|
56
|
+
title: {
|
|
57
|
+
display: true,
|
|
58
|
+
text: 'World population per region (in millions)'
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
</script>
|
|
64
|
+
</head>
|
|
65
|
+
<body>
|
|
66
|
+
<h1>Line Chart</h1>
|
|
67
|
+
|
|
68
|
+
<div style="width: 800px;">
|
|
69
|
+
<canvas id="line-chart"></canvas>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
</body>
|
|
73
|
+
</html>
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
7
|
<title>XYlineChart Projection Example</title>
|
|
8
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.3
|
|
9
|
-
<script src="./../
|
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.js"></script>
|
|
9
|
+
<script src="./../dist/chartjs-plugin-trendline.min.js"></script>
|
|
10
10
|
<script>
|
|
11
11
|
document.addEventListener("DOMContentLoaded", function(event) {
|
|
12
12
|
new Chart(document.getElementById("line-chart"), {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chartjs-plugin-trendline",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Trendline for Chart.js",
|
|
5
5
|
"main": "src/chartjs-plugin-trendline.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"homepage": "https://github.com/Makanz/chartjs-plugin-trendline#readme",
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"webpack": "^5.
|
|
20
|
+
"webpack": "^5.90.0",
|
|
21
21
|
"webpack-cli": "^5.1.4"
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -1,37 +1,38 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* chartjs-plugin-trendline.js
|
|
3
|
-
* Version: 2.
|
|
3
|
+
* Version: 2.1.1
|
|
4
4
|
*
|
|
5
|
-
* Copyright
|
|
5
|
+
* Copyright 2024 Marcus Alsterfjord
|
|
6
6
|
* Released under the MIT license
|
|
7
7
|
* https://github.com/Makanz/chartjs-plugin-trendline/blob/master/README.md
|
|
8
8
|
*
|
|
9
9
|
* Mod by: vesal: accept also xy-data so works with scatter
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
import { LineFitter } from './classes/lineFitter.js';
|
|
12
|
+
|
|
13
|
+
const pluginTrendlineLinear = {
|
|
12
14
|
id: 'chartjs-plugin-trendline',
|
|
13
|
-
afterDatasetsDraw:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
for (
|
|
15
|
+
afterDatasetsDraw: (chartInstance) => {
|
|
16
|
+
let yScale;
|
|
17
|
+
let xScale;
|
|
18
|
+
for (let axis in chartInstance.scales) {
|
|
17
19
|
if (axis[0] == 'x') xScale = chartInstance.scales[axis];
|
|
18
20
|
else yScale = chartInstance.scales[axis];
|
|
19
21
|
if (xScale && yScale) break;
|
|
20
22
|
}
|
|
21
|
-
|
|
23
|
+
const ctx = chartInstance.ctx;
|
|
22
24
|
|
|
23
|
-
chartInstance.data.datasets.forEach(
|
|
24
|
-
|
|
25
|
+
chartInstance.data.datasets.forEach((dataset, index) => {
|
|
26
|
+
const showTrendline =
|
|
25
27
|
dataset.alwaysShowTrendline ||
|
|
26
28
|
chartInstance.isDatasetVisible(index);
|
|
27
29
|
|
|
28
|
-
// TODO: Add support for objects
|
|
29
30
|
if (
|
|
30
31
|
dataset.trendlineLinear &&
|
|
31
32
|
showTrendline &&
|
|
32
33
|
dataset.data.length > 1
|
|
33
34
|
) {
|
|
34
|
-
|
|
35
|
+
const datasetMeta = chartInstance.getDatasetMeta(index);
|
|
35
36
|
addFitter(
|
|
36
37
|
datasetMeta,
|
|
37
38
|
ctx,
|
|
@@ -46,33 +47,35 @@ var pluginTrendlineLinear = {
|
|
|
46
47
|
},
|
|
47
48
|
};
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
const addFitter = (datasetMeta, ctx, dataset, xScale, yScale) => {
|
|
51
|
+
let defaultColor = dataset.borderColor || 'rgba(169,169,169, .6)';
|
|
52
|
+
let colorMin = dataset.trendlineLinear.colorMin || defaultColor;
|
|
53
|
+
let colorMax = dataset.trendlineLinear.colorMax || defaultColor;
|
|
54
|
+
let lineWidth = dataset.trendlineLinear.width ?? dataset.borderWidth ?? 3;
|
|
55
|
+
let lineStyle = dataset.trendlineLinear.lineStyle || 'solid';
|
|
56
|
+
let fillColor = dataset.trendlineLinear.fillColor;
|
|
57
|
+
|
|
58
|
+
const chartOptions = datasetMeta.controller.chart.options;
|
|
59
|
+
const parsingOptions = typeof chartOptions.parsing === 'object' ? chartOptions.parsing : undefined;
|
|
60
|
+
const xAxisKey = dataset.trendlineLinear.xAxisKey || parsingOptions?.xAxisKey || 'x';
|
|
61
|
+
const yAxisKey = dataset.trendlineLinear.yAxisKey || parsingOptions?.yAxisKey || 'y';
|
|
62
|
+
|
|
63
|
+
let fitter = new LineFitter();
|
|
64
|
+
let firstIndex = dataset.data.findIndex((d) => {
|
|
61
65
|
return d !== undefined && d !== null;
|
|
62
66
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
let lastIndex = dataset.data.length - 1;
|
|
68
|
+
let startPos = datasetMeta.data[firstIndex][xAxisKey];
|
|
69
|
+
let endPos = datasetMeta.data[lastIndex][xAxisKey];
|
|
70
|
+
let xy = typeof dataset.data[firstIndex] === 'object';
|
|
67
71
|
|
|
68
|
-
dataset.data.forEach(
|
|
69
|
-
console.log('forEach');
|
|
72
|
+
dataset.data.forEach((data, index) => {
|
|
70
73
|
if (data == null) return;
|
|
71
74
|
|
|
72
75
|
if (['time', 'timeseries'].includes(xScale.options.type)) {
|
|
73
|
-
|
|
76
|
+
let x = data[xAxisKey] != null ? data[xAxisKey] : data.t;
|
|
74
77
|
if (x !== undefined) {
|
|
75
|
-
fitter.add(new Date(x).getTime(), data
|
|
78
|
+
fitter.add(new Date(x).getTime(), data[yAxisKey]);
|
|
76
79
|
} else {
|
|
77
80
|
fitter.add(index, data);
|
|
78
81
|
}
|
|
@@ -89,16 +92,16 @@ function addFitter(datasetMeta, ctx, dataset, xScale, yScale) {
|
|
|
89
92
|
}
|
|
90
93
|
});
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
let x1 = xScale.getPixelForValue(fitter.minx);
|
|
96
|
+
let y1 = yScale.getPixelForValue(fitter.f(fitter.minx));
|
|
94
97
|
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
let x2;
|
|
99
|
+
let y2;
|
|
97
100
|
|
|
98
101
|
// Project only on x axes, do not project if trendline will never hit x axes
|
|
99
102
|
if (dataset.trendlineLinear.projection && fitter.scale() < 0) {
|
|
100
103
|
// X
|
|
101
|
-
|
|
104
|
+
let x2value = fitter.fo();
|
|
102
105
|
if (x2value < fitter.minx) x2value = fitter.maxx;
|
|
103
106
|
x2 = xScale.getPixelForValue(x2value);
|
|
104
107
|
|
|
@@ -114,24 +117,24 @@ function addFitter(datasetMeta, ctx, dataset, xScale, yScale) {
|
|
|
114
117
|
x2 = endPos;
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
let drawBottom = datasetMeta.controller.chart.chartArea.bottom;
|
|
121
|
+
let chartWidth = datasetMeta.controller.chart.width;
|
|
119
122
|
|
|
120
123
|
if (y1 > drawBottom) {
|
|
121
124
|
// Left side is below zero
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
let diff = y1 - drawBottom;
|
|
126
|
+
let lineHeight = y1 - y2;
|
|
127
|
+
let overlapPercentage = diff / lineHeight;
|
|
128
|
+
let addition = chartWidth * overlapPercentage;
|
|
126
129
|
|
|
127
130
|
y1 = drawBottom;
|
|
128
131
|
x1 = x1 + addition;
|
|
129
132
|
} else if (y2 > drawBottom) {
|
|
130
133
|
// right side is below zero
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
let diff = y2 - drawBottom;
|
|
135
|
+
let lineHeight = y2 - y1;
|
|
136
|
+
let overlapPercentage = diff / lineHeight;
|
|
137
|
+
let subtraction = chartWidth - chartWidth * overlapPercentage;
|
|
135
138
|
|
|
136
139
|
y2 = drawBottom;
|
|
137
140
|
x2 = chartWidth - (x2 - subtraction);
|
|
@@ -149,7 +152,7 @@ function addFitter(datasetMeta, ctx, dataset, xScale, yScale) {
|
|
|
149
152
|
ctx.moveTo(x1, y1);
|
|
150
153
|
ctx.lineTo(x2, y2);
|
|
151
154
|
|
|
152
|
-
|
|
155
|
+
let gradient = ctx.createLinearGradient(x1, y1, x2, y2);
|
|
153
156
|
if (y2 < y1) {
|
|
154
157
|
gradient.addColorStop(0, colorMax);
|
|
155
158
|
gradient.addColorStop(1, colorMin);
|
|
@@ -173,56 +176,6 @@ function addFitter(datasetMeta, ctx, dataset, xScale, yScale) {
|
|
|
173
176
|
ctx.closePath();
|
|
174
177
|
ctx.fill();
|
|
175
178
|
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function LineFitter() {
|
|
179
|
-
this.count = 0;
|
|
180
|
-
this.sumX = 0;
|
|
181
|
-
this.sumX2 = 0;
|
|
182
|
-
this.sumXY = 0;
|
|
183
|
-
this.sumY = 0;
|
|
184
|
-
this.minx = 1e100;
|
|
185
|
-
this.maxx = -1e100;
|
|
186
|
-
this.maxy = -1e100;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
LineFitter.prototype = {
|
|
190
|
-
add: function (x, y) {
|
|
191
|
-
x = parseFloat(x);
|
|
192
|
-
y = parseFloat(y);
|
|
193
|
-
|
|
194
|
-
this.count++;
|
|
195
|
-
this.sumX += x;
|
|
196
|
-
this.sumX2 += x * x;
|
|
197
|
-
this.sumXY += x * y;
|
|
198
|
-
this.sumY += y;
|
|
199
|
-
if (x < this.minx) this.minx = x;
|
|
200
|
-
if (x > this.maxx) this.maxx = x;
|
|
201
|
-
if (y > this.maxy) this.maxy = y;
|
|
202
|
-
},
|
|
203
|
-
f: function (x) {
|
|
204
|
-
x = parseFloat(x);
|
|
205
|
-
|
|
206
|
-
var det = this.count * this.sumX2 - this.sumX * this.sumX;
|
|
207
|
-
var offset = (this.sumX2 * this.sumY - this.sumX * this.sumXY) / det;
|
|
208
|
-
var scale = (this.count * this.sumXY - this.sumX * this.sumY) / det;
|
|
209
|
-
return offset + x * scale;
|
|
210
|
-
},
|
|
211
|
-
fo: function () {
|
|
212
|
-
var det = this.count * this.sumX2 - this.sumX * this.sumX;
|
|
213
|
-
var offset = (this.sumX2 * this.sumY - this.sumX * this.sumXY) / det;
|
|
214
|
-
var scale = (this.count * this.sumXY - this.sumX * this.sumY) / det;
|
|
215
|
-
|
|
216
|
-
// Get x when y = 0
|
|
217
|
-
var xo = -offset / scale;
|
|
218
|
-
return xo;
|
|
219
|
-
},
|
|
220
|
-
scale: function () {
|
|
221
|
-
var det = this.count * this.sumX2 - this.sumX * this.sumX;
|
|
222
|
-
var scale = (this.count * this.sumXY - this.sumX * this.sumY) / det;
|
|
223
|
-
|
|
224
|
-
return scale;
|
|
225
|
-
},
|
|
226
179
|
};
|
|
227
180
|
|
|
228
181
|
// If we're in the browser and have access to the global Chart obj, register plugin automatically
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export class LineFitter {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.count = 0;
|
|
4
|
+
this.sumX = 0;
|
|
5
|
+
this.sumX2 = 0;
|
|
6
|
+
this.sumXY = 0;
|
|
7
|
+
this.sumY = 0;
|
|
8
|
+
this.minx = 1e100;
|
|
9
|
+
this.maxx = -1e100;
|
|
10
|
+
this.maxy = -1e100;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
add = (x, y) => {
|
|
14
|
+
x = parseFloat(x);
|
|
15
|
+
y = parseFloat(y);
|
|
16
|
+
|
|
17
|
+
this.count++;
|
|
18
|
+
this.sumX += x;
|
|
19
|
+
this.sumX2 += x * x;
|
|
20
|
+
this.sumXY += x * y;
|
|
21
|
+
this.sumY += y;
|
|
22
|
+
if (x < this.minx) this.minx = x;
|
|
23
|
+
if (x > this.maxx) this.maxx = x;
|
|
24
|
+
if (y > this.maxy) this.maxy = y;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
f = (x) => {
|
|
28
|
+
x = parseFloat(x);
|
|
29
|
+
|
|
30
|
+
let det = this.count * this.sumX2 - this.sumX * this.sumX;
|
|
31
|
+
let offset = (this.sumX2 * this.sumY - this.sumX * this.sumXY) / det;
|
|
32
|
+
let scale = (this.count * this.sumXY - this.sumX * this.sumY) / det;
|
|
33
|
+
return offset + x * scale;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
fo = () => {
|
|
37
|
+
let det = this.count * this.sumX2 - this.sumX * this.sumX;
|
|
38
|
+
let offset = (this.sumX2 * this.sumY - this.sumX * this.sumXY) / det;
|
|
39
|
+
let scale = (this.count * this.sumXY - this.sumX * this.sumY) / det;
|
|
40
|
+
|
|
41
|
+
// Get x when y = 0
|
|
42
|
+
let xo = -offset / scale;
|
|
43
|
+
return xo;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
scale = () => {
|
|
47
|
+
let det = this.count * this.sumX2 - this.sumX * this.sumX;
|
|
48
|
+
let scale = (this.count * this.sumXY - this.sumX * this.sumY) / det;
|
|
49
|
+
|
|
50
|
+
return scale;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
<html>
|
|
2
|
-
|
|
3
|
-
<head>
|
|
4
|
-
<title>Time scale chart.js</title>
|
|
5
|
-
</head>
|
|
6
|
-
<style>
|
|
7
|
-
.chartBox {
|
|
8
|
-
width: 700px
|
|
9
|
-
}
|
|
10
|
-
</style>
|
|
11
|
-
|
|
12
|
-
<body>
|
|
13
|
-
<div class="chartBox">
|
|
14
|
-
<canvas id="myChart"></canvas>
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
18
|
-
|
|
19
|
-
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
|
20
|
-
|
|
21
|
-
<script src="./../src/chartjs-plugin-trendline.js"></script>
|
|
22
|
-
|
|
23
|
-
<script>
|
|
24
|
-
const data = {
|
|
25
|
-
"labels": [
|
|
26
|
-
"2023-08-10 20:00:00",
|
|
27
|
-
"2023-08-10 21:30:00",
|
|
28
|
-
"2023-08-10 22:30:30",
|
|
29
|
-
"2023-08-11 07:00:00",
|
|
30
|
-
"2023-08-11 07:15:00",
|
|
31
|
-
"2023-08-11 08:00:00"
|
|
32
|
-
],
|
|
33
|
-
"datasets": [
|
|
34
|
-
{
|
|
35
|
-
"type": "line",
|
|
36
|
-
"label": "Blood Sugar",
|
|
37
|
-
"data": {
|
|
38
|
-
"2023-08-10 21:30:00": "6.20",
|
|
39
|
-
"2023-08-10 22:30:30": "6.60",
|
|
40
|
-
"2023-08-11 07:00:00": "5.90",
|
|
41
|
-
"2023-08-11 08:00:00": "4.60",
|
|
42
|
-
"2023-08-11 11:17:00": "5.10",
|
|
43
|
-
"2023-08-11 12:15:00": "4.90"
|
|
44
|
-
},
|
|
45
|
-
"backgroundColor": "#A9CCE3",
|
|
46
|
-
"pointStyle": "circle",
|
|
47
|
-
"pointRadius": 6,
|
|
48
|
-
"pointHoverRadius": 12,
|
|
49
|
-
"borderColor": "#5DADE2",
|
|
50
|
-
"trendlineLinear": {
|
|
51
|
-
"colorMin": "red",
|
|
52
|
-
"colorMax": "green",
|
|
53
|
-
"lineStyle": "dotted",
|
|
54
|
-
"width": 3,
|
|
55
|
-
"projection": true
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
],
|
|
59
|
-
};
|
|
60
|
-
const config = {
|
|
61
|
-
type: 'line',
|
|
62
|
-
data
|
|
63
|
-
};
|
|
64
|
-
console.log("Loaded");
|
|
65
|
-
const myChart = new Chart(
|
|
66
|
-
document.getElementById('myChart'),
|
|
67
|
-
config,
|
|
68
|
-
);
|
|
69
|
-
</script>
|
|
70
|
-
</body>
|
|
71
|
-
|
|
72
|
-
</html>
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
7
|
-
<title>LineChart Example</title>
|
|
8
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.3.0/dist/chart.umd.js"></script>
|
|
9
|
-
<script src="./../src/chartjs-plugin-trendline.js"></script>
|
|
10
|
-
<script>
|
|
11
|
-
let data = {
|
|
12
|
-
"labels": [
|
|
13
|
-
202303,
|
|
14
|
-
202304,
|
|
15
|
-
202305,
|
|
16
|
-
202306,
|
|
17
|
-
202307,
|
|
18
|
-
202308,
|
|
19
|
-
202309
|
|
20
|
-
],
|
|
21
|
-
"datasets": [
|
|
22
|
-
{
|
|
23
|
-
"label": "Dataset 1",
|
|
24
|
-
"data": [
|
|
25
|
-
10,
|
|
26
|
-
30,
|
|
27
|
-
50,
|
|
28
|
-
20,
|
|
29
|
-
25,
|
|
30
|
-
44,
|
|
31
|
-
-10
|
|
32
|
-
],
|
|
33
|
-
"borderColor": "#4dc9f6",
|
|
34
|
-
"backgroundColor": "#4dc9f6",
|
|
35
|
-
"trendlineLinear": {
|
|
36
|
-
"colorMin": "#4dc9f6",
|
|
37
|
-
"colorMax": "#4dc9f6",
|
|
38
|
-
"lineStyle": "dotted",
|
|
39
|
-
"width": 2,
|
|
40
|
-
"projection": false
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"label": "Dataset 2",
|
|
45
|
-
"data": [
|
|
46
|
-
100,
|
|
47
|
-
33,
|
|
48
|
-
22,
|
|
49
|
-
19,
|
|
50
|
-
11,
|
|
51
|
-
49,
|
|
52
|
-
200
|
|
53
|
-
],
|
|
54
|
-
"borderColor": "#f67019",
|
|
55
|
-
"backgroundColor": "#f67019",
|
|
56
|
-
"trendlineLinear": {
|
|
57
|
-
"colorMin": "#f67019",
|
|
58
|
-
"colorMax": "#f67019",
|
|
59
|
-
"lineStyle": "solid",
|
|
60
|
-
"width": 2,
|
|
61
|
-
"projection": false
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
]
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
var lineOptions = {
|
|
68
|
-
plugins: {
|
|
69
|
-
legend: {
|
|
70
|
-
display: true,
|
|
71
|
-
labels: {
|
|
72
|
-
fontSize: 10,
|
|
73
|
-
fontColor: "#000",
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
scales: {
|
|
78
|
-
x:
|
|
79
|
-
{
|
|
80
|
-
gridLines: {
|
|
81
|
-
display: true,
|
|
82
|
-
},
|
|
83
|
-
display: true,
|
|
84
|
-
color: "rgba(255, 255, 255, 0.1)",
|
|
85
|
-
},
|
|
86
|
-
y:
|
|
87
|
-
{
|
|
88
|
-
display: false,
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
responsive: true,
|
|
92
|
-
maintainAspectRatio: true,
|
|
93
|
-
};
|
|
94
|
-
document.addEventListener("DOMContentLoaded", function(event) {
|
|
95
|
-
new Chart(document.getElementById("line-chart"), {
|
|
96
|
-
type: 'line',
|
|
97
|
-
data: data,
|
|
98
|
-
options:lineOptions
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
</script>
|
|
102
|
-
</head>
|
|
103
|
-
<body>
|
|
104
|
-
<h1>Line Chart</h1>
|
|
105
|
-
|
|
106
|
-
<div style="width: 800px;">
|
|
107
|
-
<canvas id="line-chart"></canvas>
|
|
108
|
-
</div>
|
|
109
|
-
|
|
110
|
-
</body>
|
|
111
|
-
</html>
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
<html>
|
|
2
|
-
|
|
3
|
-
<head>
|
|
4
|
-
<title>Time scale chart.js</title>
|
|
5
|
-
</head>
|
|
6
|
-
<style>
|
|
7
|
-
.chartBox {
|
|
8
|
-
width: 700px
|
|
9
|
-
}
|
|
10
|
-
</style>
|
|
11
|
-
|
|
12
|
-
<body>
|
|
13
|
-
<div class="chartBox">
|
|
14
|
-
<canvas id="myChart"></canvas>
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
18
|
-
|
|
19
|
-
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
|
20
|
-
|
|
21
|
-
<script src="./../src/chartjs-plugin-trendline.js"></script>
|
|
22
|
-
|
|
23
|
-
<script>
|
|
24
|
-
const data = {
|
|
25
|
-
labels: [
|
|
26
|
-
"2023-01-01T08:00:00.000Z",
|
|
27
|
-
"2023-01-02T08:00:00.000Z",
|
|
28
|
-
"2023-01-03T08:00:00.000Z",
|
|
29
|
-
"2023-01-04T08:00:00.000Z",
|
|
30
|
-
],
|
|
31
|
-
datasets: [{
|
|
32
|
-
label: "Default",
|
|
33
|
-
data: [65, 59, 80, 81],
|
|
34
|
-
trendlineLinear: {
|
|
35
|
-
colorMin: "red",
|
|
36
|
-
colorMax: "green",
|
|
37
|
-
lineStyle: "dotted",
|
|
38
|
-
width: 2,
|
|
39
|
-
projection: false,
|
|
40
|
-
},
|
|
41
|
-
}],
|
|
42
|
-
};
|
|
43
|
-
const config = {
|
|
44
|
-
type: 'line',
|
|
45
|
-
data,
|
|
46
|
-
options: {
|
|
47
|
-
scales: {
|
|
48
|
-
x: {
|
|
49
|
-
type: 'time',
|
|
50
|
-
time: {
|
|
51
|
-
unit: 'day',
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
y: {
|
|
55
|
-
beginAtZero: true
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
const myChart = new Chart(
|
|
61
|
-
document.getElementById('myChart'),
|
|
62
|
-
config,
|
|
63
|
-
);
|
|
64
|
-
</script>
|
|
65
|
-
</body>
|
|
66
|
-
|
|
67
|
-
</html>
|