chartjs-plugin-trendline 3.2.0 → 3.2.3
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/.github/copilot-instructions.md +40 -40
- package/.github/workflows/release.yml +64 -61
- package/.github/workflows/tests.yml +26 -26
- package/.prettierrc +5 -5
- package/CLAUDE.md +44 -44
- package/GEMINI.md +40 -40
- package/LICENSE +21 -21
- package/MIGRATION.md +126 -126
- package/README.md +166 -166
- package/babel.config.js +3 -3
- package/changelog.md +39 -39
- package/dist/chartjs-plugin-trendline.cjs +884 -885
- package/dist/chartjs-plugin-trendline.esm.js +882 -883
- package/dist/chartjs-plugin-trendline.js +890 -891
- package/dist/chartjs-plugin-trendline.min.js +8 -8
- package/dist/chartjs-plugin-trendline.min.js.map +1 -1
- package/example/barChart.html +165 -165
- package/example/barChartWithNullValues.html +168 -168
- package/example/barChart_label.html +174 -174
- package/example/exponentialChart.html +244 -244
- package/example/lineChart.html +210 -210
- package/example/lineChartProjection.html +261 -261
- package/example/lineChartTypeTime.html +190 -190
- package/example/scatterChart.html +136 -136
- package/example/scatterProjection.html +141 -141
- package/example/test-null-handling.html +59 -59
- package/index.html +215 -215
- package/jest.config.js +4 -4
- package/package.json +45 -40
- package/rollup.config.js +54 -54
- package/src/components/label.js +56 -56
- package/src/components/label.test.js +129 -129
- package/src/components/trendline.js +375 -375
- package/src/components/trendline.test.js +789 -789
- package/src/core/plugin.js +78 -79
- package/src/core/plugin.test.js +307 -0
- package/src/index.js +12 -12
- package/src/utils/drawing.js +125 -125
- package/src/utils/drawing.test.js +308 -308
- package/src/utils/exponentialFitter.js +146 -146
- package/src/utils/exponentialFitter.test.js +362 -362
- package/src/utils/lineFitter.js +86 -86
- package/src/utils/lineFitter.test.js +340 -340
|
@@ -1,262 +1,262 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
8
|
-
<title>Trendline Projection & Offset Example</title>
|
|
9
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.js"></script>
|
|
10
|
-
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-trendline/dist/chartjs-plugin-trendline.min.js"></script>
|
|
11
|
-
<style>
|
|
12
|
-
body {
|
|
13
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
14
|
-
margin: 0;
|
|
15
|
-
padding: 40px 20px;
|
|
16
|
-
background-color: #f8f9fa;
|
|
17
|
-
color: #333;
|
|
18
|
-
line-height: 1.6;
|
|
19
|
-
}
|
|
20
|
-
.container {
|
|
21
|
-
max-width: 1000px;
|
|
22
|
-
margin: 0 auto;
|
|
23
|
-
}
|
|
24
|
-
h1 {
|
|
25
|
-
text-align: center;
|
|
26
|
-
color: #2c3e50;
|
|
27
|
-
margin-bottom: 10px;
|
|
28
|
-
font-size: 2.5rem;
|
|
29
|
-
}
|
|
30
|
-
.subtitle {
|
|
31
|
-
text-align: center;
|
|
32
|
-
color: #7f8c8d;
|
|
33
|
-
margin-bottom: 40px;
|
|
34
|
-
font-size: 1.1rem;
|
|
35
|
-
}
|
|
36
|
-
.chart-container {
|
|
37
|
-
background: white;
|
|
38
|
-
padding: 30px;
|
|
39
|
-
border-radius: 8px;
|
|
40
|
-
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
41
|
-
margin-bottom: 30px;
|
|
42
|
-
}
|
|
43
|
-
.chart-wrapper {
|
|
44
|
-
position: relative;
|
|
45
|
-
height: 500px;
|
|
46
|
-
}
|
|
47
|
-
.info-section {
|
|
48
|
-
background: white;
|
|
49
|
-
padding: 25px;
|
|
50
|
-
border-radius: 8px;
|
|
51
|
-
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
52
|
-
margin-bottom: 20px;
|
|
53
|
-
}
|
|
54
|
-
.info-section h3 {
|
|
55
|
-
margin-top: 0;
|
|
56
|
-
color: #2c3e50;
|
|
57
|
-
border-bottom: 2px solid #3498db;
|
|
58
|
-
padding-bottom: 10px;
|
|
59
|
-
}
|
|
60
|
-
.nav-links {
|
|
61
|
-
text-align: center;
|
|
62
|
-
margin-top: 30px;
|
|
63
|
-
}
|
|
64
|
-
.nav-links a {
|
|
65
|
-
display: inline-block;
|
|
66
|
-
margin: 0 10px;
|
|
67
|
-
padding: 10px 20px;
|
|
68
|
-
background: #3498db;
|
|
69
|
-
color: white;
|
|
70
|
-
text-decoration: none;
|
|
71
|
-
border-radius: 4px;
|
|
72
|
-
font-weight: 500;
|
|
73
|
-
transition: background-color 0.2s;
|
|
74
|
-
}
|
|
75
|
-
.nav-links a:hover {
|
|
76
|
-
background: #2980b9;
|
|
77
|
-
}
|
|
78
|
-
code {
|
|
79
|
-
background: #f8f9fa;
|
|
80
|
-
padding: 2px 6px;
|
|
81
|
-
border-radius: 3px;
|
|
82
|
-
font-family: 'Monaco', 'Menlo', monospace;
|
|
83
|
-
}
|
|
84
|
-
</style>
|
|
85
|
-
<script>
|
|
86
|
-
document.addEventListener("DOMContentLoaded", function (event) {
|
|
87
|
-
new Chart(document.getElementById("line-chart"), {
|
|
88
|
-
type: 'line',
|
|
89
|
-
data: {
|
|
90
|
-
datasets: [
|
|
91
|
-
{
|
|
92
|
-
// Dataset 1: Original data, demonstrates basic projection.
|
|
93
|
-
// Observe: Trendline extends to the edges of the chart area.
|
|
94
|
-
data: [{ x: 0, y: 30 }, { x: 5, y: 25 }, { x: 10, y: 23 }, { x: 25, y: 23 }, { x: 35, y: 17 }, { x: 45, y: 17 }],
|
|
95
|
-
label: "Dataset 1 (Projected)",
|
|
96
|
-
borderColor: "#3e95cd",
|
|
97
|
-
backgroundColor: "rgba(62,149,205,0.1)",
|
|
98
|
-
fill: false,
|
|
99
|
-
trendlineLinear: {
|
|
100
|
-
colorMin: "#3e95cd", // Should match borderColor or be distinct
|
|
101
|
-
lineStyle: "dotted", // "line" was not a valid Chart.js option, "solid" or "dotted", "dashed"
|
|
102
|
-
width: 2,
|
|
103
|
-
projection: true,
|
|
104
|
-
label: {
|
|
105
|
-
display: true,
|
|
106
|
-
text: "Trend D1 (Proj)",
|
|
107
|
-
displayValue: true,
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
// Dataset 2: Clustered data, non-projected.
|
|
113
|
-
// Observe: Trendline only spans the range of its data points (approx x=15 to x=35).
|
|
114
|
-
// This contrasts with projected lines that extend to chart boundaries.
|
|
115
|
-
data: [{ x: 15, y: 10 }, { x: 20, y: 12 }, { x: 25, y: 9 }, { x: 30, y: 11 }, { x: 35, y: 8 }],
|
|
116
|
-
label: "Dataset 2 (Non-Projected, Clustered)",
|
|
117
|
-
borderColor: "#FF6384",
|
|
118
|
-
backgroundColor: "rgba(255,99,132,0.1)",
|
|
119
|
-
fill: false,
|
|
120
|
-
trendlineLinear: {
|
|
121
|
-
colorMin: "#FF6384",
|
|
122
|
-
lineStyle: "dashed",
|
|
123
|
-
width: 2,
|
|
124
|
-
projection: false, // Explicitly false
|
|
125
|
-
label: {
|
|
126
|
-
display: true,
|
|
127
|
-
text: "Trend D2 (No Proj)",
|
|
128
|
-
displayValue: true,
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
// Dataset 3: Projected, with positive trendoffset and null data points.
|
|
134
|
-
// Observe: Trendline calculation starts after skipping the first 2 data points (due to trendoffset: 2).
|
|
135
|
-
// Null values (e.g., at x=65) are correctly ignored by the fitter. The line projects to chart edges.
|
|
136
|
-
data: [{ x: 50, y: 10 }, { x: 55, y: 12 }, { x: 60, y: 15 }, { x: 70, y: 18 }, { x: 75, y: 22 }, {x: 65, y: null}],
|
|
137
|
-
label: "Dataset 3 (Projected, Offset+, Nulls)",
|
|
138
|
-
borderColor: "#4BC0C0",
|
|
139
|
-
backgroundColor: "rgba(75,192,192,0.1)",
|
|
140
|
-
fill: false,
|
|
141
|
-
trendlineLinear: {
|
|
142
|
-
colorMin: "#4BC0C0",
|
|
143
|
-
lineStyle: "solid",
|
|
144
|
-
width: 2,
|
|
145
|
-
projection: true,
|
|
146
|
-
trendoffset: 2, // Skips first 2 data points from calculation {50,y:10}, {55,y:12}
|
|
147
|
-
label: {
|
|
148
|
-
display: true,
|
|
149
|
-
text: "Trend D3 (Proj, Offset+)",
|
|
150
|
-
displayValue: true,
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
// Dataset 4: Projected, with negative trendoffset.
|
|
156
|
-
// Observe: Trendline calculation excludes the last 2 data points from the set.
|
|
157
|
-
// The line projects to chart edges based on the remaining points.
|
|
158
|
-
data: [{ x: 0, y: 5 }, { x: 10, y: 8 }, { x: 20, y: 6}, { x: 30, y: 9 }, { x: 40, y: 7 }, { x: 50, y: 10 } ],
|
|
159
|
-
label: "Dataset 4 (Projected, Offset-)",
|
|
160
|
-
borderColor: "#FF9F40",
|
|
161
|
-
backgroundColor: "rgba(255,159,64,0.1)",
|
|
162
|
-
fill: false,
|
|
163
|
-
trendlineLinear: {
|
|
164
|
-
colorMin: "#FF9F40",
|
|
165
|
-
lineStyle: "dotted",
|
|
166
|
-
width: 2,
|
|
167
|
-
projection: true,
|
|
168
|
-
trendoffset: -2, // Excludes last 2 data points {40,y:7}, {50,y:10}
|
|
169
|
-
label: {
|
|
170
|
-
display: true,
|
|
171
|
-
text: "Trend D4 (Proj, Offset-)",
|
|
172
|
-
displayValue: true,
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
},
|
|
176
|
-
// Dummy dataset to ensure chart scales to desired max, if other data doesn't reach it.
|
|
177
|
-
// { data: [{ x: 80, y: 35 }], label: "ScaleHelper", hidden: true}
|
|
178
|
-
]
|
|
179
|
-
},
|
|
180
|
-
options: {
|
|
181
|
-
plugins: {
|
|
182
|
-
title: {
|
|
183
|
-
display: true,
|
|
184
|
-
text: 'Trendline Projection, Offset, and Data Handling Demo',
|
|
185
|
-
font: { size: 18 }
|
|
186
|
-
},
|
|
187
|
-
legend: {
|
|
188
|
-
position: 'top',
|
|
189
|
-
},
|
|
190
|
-
tooltip: {
|
|
191
|
-
mode: 'index',
|
|
192
|
-
intersect: false,
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
maintainAspectRatio: false,
|
|
196
|
-
responsive: true,
|
|
197
|
-
scales: {
|
|
198
|
-
x: {
|
|
199
|
-
type: 'linear',
|
|
200
|
-
position: 'bottom',
|
|
201
|
-
title: { text: 'X Value / Days', display: true },
|
|
202
|
-
min: -5, // Adjusted min to give space
|
|
203
|
-
max: 80, // Adjusted max to give space for projection
|
|
204
|
-
},
|
|
205
|
-
y: {
|
|
206
|
-
type: 'linear',
|
|
207
|
-
position: 'left',
|
|
208
|
-
title: { text: 'Y Value / Count', display: true },
|
|
209
|
-
min: 0, // Adjusted min
|
|
210
|
-
max: 35, // Adjusted max
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
</script>
|
|
217
|
-
</head>
|
|
218
|
-
|
|
219
|
-
<body>
|
|
220
|
-
<div class="container">
|
|
221
|
-
<h1>Line Chart Projection</h1>
|
|
222
|
-
<p class="subtitle">Trendline projection, data offset, and null value handling</p>
|
|
223
|
-
|
|
224
|
-
<div class="chart-container">
|
|
225
|
-
<div class="chart-wrapper">
|
|
226
|
-
<canvas id="line-chart"></canvas>
|
|
227
|
-
</div>
|
|
228
|
-
</div>
|
|
229
|
-
|
|
230
|
-
<div class="info-section">
|
|
231
|
-
<h3>Observations Guide</h3>
|
|
232
|
-
<ul>
|
|
233
|
-
<li><strong>Dataset 1 (Blue, Dotted Trend):</strong> Shows a standard projected trendline extending to the chart's X-axis boundaries.</li>
|
|
234
|
-
<li><strong>Dataset 2 (Red, Dashed Trend):</strong> Demonstrates a <strong>non-projected</strong> trendline. Notice how it only covers the span of its own data points (approx. x=15 to x=35).</li>
|
|
235
|
-
<li><strong>Dataset 3 (Teal, Solid Trend):</strong>
|
|
236
|
-
<ul>
|
|
237
|
-
<li>Uses <code>projection: true</code>.</li>
|
|
238
|
-
<li><code>trendoffset: 2</code> means the first two data points ( (50,10) and (55,12) ) are excluded from the trendline calculation.</li>
|
|
239
|
-
<li>Contains a <code>null</code> data point (originally at x=65) which is correctly ignored by the trendline fitter.</li>
|
|
240
|
-
</ul>
|
|
241
|
-
</li>
|
|
242
|
-
<li><strong>Dataset 4 (Orange, Dotted Trend):</strong>
|
|
243
|
-
<ul>
|
|
244
|
-
<li>Uses <code>projection: true</code>.</li>
|
|
245
|
-
<li><code>trendoffset: -2</code> means the last two data points ( (40,7) and (50,10) ) are excluded from the trendline calculation.</li>
|
|
246
|
-
</ul>
|
|
247
|
-
</li>
|
|
248
|
-
<li>All trendlines should be clipped correctly within the chart area boundaries, even if their mathematical extension would go beyond.</li>
|
|
249
|
-
<li>The slope displayed in the labels should consistently reflect the underlying data used for fitting, regardless of projection.</li>
|
|
250
|
-
</ul>
|
|
251
|
-
</div>
|
|
252
|
-
|
|
253
|
-
<div class="nav-links">
|
|
254
|
-
<a href="../index.html">← Back to Examples</a>
|
|
255
|
-
<a href="lineChart.html">← Line Chart</a>
|
|
256
|
-
<a href="lineChartTypeTime.html">Time Series Line Chart →</a>
|
|
257
|
-
</div>
|
|
258
|
-
</div>
|
|
259
|
-
|
|
260
|
-
</body>
|
|
261
|
-
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
8
|
+
<title>Trendline Projection & Offset Example</title>
|
|
9
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.js"></script>
|
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-trendline/dist/chartjs-plugin-trendline.min.js"></script>
|
|
11
|
+
<style>
|
|
12
|
+
body {
|
|
13
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 40px 20px;
|
|
16
|
+
background-color: #f8f9fa;
|
|
17
|
+
color: #333;
|
|
18
|
+
line-height: 1.6;
|
|
19
|
+
}
|
|
20
|
+
.container {
|
|
21
|
+
max-width: 1000px;
|
|
22
|
+
margin: 0 auto;
|
|
23
|
+
}
|
|
24
|
+
h1 {
|
|
25
|
+
text-align: center;
|
|
26
|
+
color: #2c3e50;
|
|
27
|
+
margin-bottom: 10px;
|
|
28
|
+
font-size: 2.5rem;
|
|
29
|
+
}
|
|
30
|
+
.subtitle {
|
|
31
|
+
text-align: center;
|
|
32
|
+
color: #7f8c8d;
|
|
33
|
+
margin-bottom: 40px;
|
|
34
|
+
font-size: 1.1rem;
|
|
35
|
+
}
|
|
36
|
+
.chart-container {
|
|
37
|
+
background: white;
|
|
38
|
+
padding: 30px;
|
|
39
|
+
border-radius: 8px;
|
|
40
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
41
|
+
margin-bottom: 30px;
|
|
42
|
+
}
|
|
43
|
+
.chart-wrapper {
|
|
44
|
+
position: relative;
|
|
45
|
+
height: 500px;
|
|
46
|
+
}
|
|
47
|
+
.info-section {
|
|
48
|
+
background: white;
|
|
49
|
+
padding: 25px;
|
|
50
|
+
border-radius: 8px;
|
|
51
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
52
|
+
margin-bottom: 20px;
|
|
53
|
+
}
|
|
54
|
+
.info-section h3 {
|
|
55
|
+
margin-top: 0;
|
|
56
|
+
color: #2c3e50;
|
|
57
|
+
border-bottom: 2px solid #3498db;
|
|
58
|
+
padding-bottom: 10px;
|
|
59
|
+
}
|
|
60
|
+
.nav-links {
|
|
61
|
+
text-align: center;
|
|
62
|
+
margin-top: 30px;
|
|
63
|
+
}
|
|
64
|
+
.nav-links a {
|
|
65
|
+
display: inline-block;
|
|
66
|
+
margin: 0 10px;
|
|
67
|
+
padding: 10px 20px;
|
|
68
|
+
background: #3498db;
|
|
69
|
+
color: white;
|
|
70
|
+
text-decoration: none;
|
|
71
|
+
border-radius: 4px;
|
|
72
|
+
font-weight: 500;
|
|
73
|
+
transition: background-color 0.2s;
|
|
74
|
+
}
|
|
75
|
+
.nav-links a:hover {
|
|
76
|
+
background: #2980b9;
|
|
77
|
+
}
|
|
78
|
+
code {
|
|
79
|
+
background: #f8f9fa;
|
|
80
|
+
padding: 2px 6px;
|
|
81
|
+
border-radius: 3px;
|
|
82
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
85
|
+
<script>
|
|
86
|
+
document.addEventListener("DOMContentLoaded", function (event) {
|
|
87
|
+
new Chart(document.getElementById("line-chart"), {
|
|
88
|
+
type: 'line',
|
|
89
|
+
data: {
|
|
90
|
+
datasets: [
|
|
91
|
+
{
|
|
92
|
+
// Dataset 1: Original data, demonstrates basic projection.
|
|
93
|
+
// Observe: Trendline extends to the edges of the chart area.
|
|
94
|
+
data: [{ x: 0, y: 30 }, { x: 5, y: 25 }, { x: 10, y: 23 }, { x: 25, y: 23 }, { x: 35, y: 17 }, { x: 45, y: 17 }],
|
|
95
|
+
label: "Dataset 1 (Projected)",
|
|
96
|
+
borderColor: "#3e95cd",
|
|
97
|
+
backgroundColor: "rgba(62,149,205,0.1)",
|
|
98
|
+
fill: false,
|
|
99
|
+
trendlineLinear: {
|
|
100
|
+
colorMin: "#3e95cd", // Should match borderColor or be distinct
|
|
101
|
+
lineStyle: "dotted", // "line" was not a valid Chart.js option, "solid" or "dotted", "dashed"
|
|
102
|
+
width: 2,
|
|
103
|
+
projection: true,
|
|
104
|
+
label: {
|
|
105
|
+
display: true,
|
|
106
|
+
text: "Trend D1 (Proj)",
|
|
107
|
+
displayValue: true,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
// Dataset 2: Clustered data, non-projected.
|
|
113
|
+
// Observe: Trendline only spans the range of its data points (approx x=15 to x=35).
|
|
114
|
+
// This contrasts with projected lines that extend to chart boundaries.
|
|
115
|
+
data: [{ x: 15, y: 10 }, { x: 20, y: 12 }, { x: 25, y: 9 }, { x: 30, y: 11 }, { x: 35, y: 8 }],
|
|
116
|
+
label: "Dataset 2 (Non-Projected, Clustered)",
|
|
117
|
+
borderColor: "#FF6384",
|
|
118
|
+
backgroundColor: "rgba(255,99,132,0.1)",
|
|
119
|
+
fill: false,
|
|
120
|
+
trendlineLinear: {
|
|
121
|
+
colorMin: "#FF6384",
|
|
122
|
+
lineStyle: "dashed",
|
|
123
|
+
width: 2,
|
|
124
|
+
projection: false, // Explicitly false
|
|
125
|
+
label: {
|
|
126
|
+
display: true,
|
|
127
|
+
text: "Trend D2 (No Proj)",
|
|
128
|
+
displayValue: true,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
// Dataset 3: Projected, with positive trendoffset and null data points.
|
|
134
|
+
// Observe: Trendline calculation starts after skipping the first 2 data points (due to trendoffset: 2).
|
|
135
|
+
// Null values (e.g., at x=65) are correctly ignored by the fitter. The line projects to chart edges.
|
|
136
|
+
data: [{ x: 50, y: 10 }, { x: 55, y: 12 }, { x: 60, y: 15 }, { x: 70, y: 18 }, { x: 75, y: 22 }, {x: 65, y: null}],
|
|
137
|
+
label: "Dataset 3 (Projected, Offset+, Nulls)",
|
|
138
|
+
borderColor: "#4BC0C0",
|
|
139
|
+
backgroundColor: "rgba(75,192,192,0.1)",
|
|
140
|
+
fill: false,
|
|
141
|
+
trendlineLinear: {
|
|
142
|
+
colorMin: "#4BC0C0",
|
|
143
|
+
lineStyle: "solid",
|
|
144
|
+
width: 2,
|
|
145
|
+
projection: true,
|
|
146
|
+
trendoffset: 2, // Skips first 2 data points from calculation {50,y:10}, {55,y:12}
|
|
147
|
+
label: {
|
|
148
|
+
display: true,
|
|
149
|
+
text: "Trend D3 (Proj, Offset+)",
|
|
150
|
+
displayValue: true,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
// Dataset 4: Projected, with negative trendoffset.
|
|
156
|
+
// Observe: Trendline calculation excludes the last 2 data points from the set.
|
|
157
|
+
// The line projects to chart edges based on the remaining points.
|
|
158
|
+
data: [{ x: 0, y: 5 }, { x: 10, y: 8 }, { x: 20, y: 6}, { x: 30, y: 9 }, { x: 40, y: 7 }, { x: 50, y: 10 } ],
|
|
159
|
+
label: "Dataset 4 (Projected, Offset-)",
|
|
160
|
+
borderColor: "#FF9F40",
|
|
161
|
+
backgroundColor: "rgba(255,159,64,0.1)",
|
|
162
|
+
fill: false,
|
|
163
|
+
trendlineLinear: {
|
|
164
|
+
colorMin: "#FF9F40",
|
|
165
|
+
lineStyle: "dotted",
|
|
166
|
+
width: 2,
|
|
167
|
+
projection: true,
|
|
168
|
+
trendoffset: -2, // Excludes last 2 data points {40,y:7}, {50,y:10}
|
|
169
|
+
label: {
|
|
170
|
+
display: true,
|
|
171
|
+
text: "Trend D4 (Proj, Offset-)",
|
|
172
|
+
displayValue: true,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
// Dummy dataset to ensure chart scales to desired max, if other data doesn't reach it.
|
|
177
|
+
// { data: [{ x: 80, y: 35 }], label: "ScaleHelper", hidden: true}
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
options: {
|
|
181
|
+
plugins: {
|
|
182
|
+
title: {
|
|
183
|
+
display: true,
|
|
184
|
+
text: 'Trendline Projection, Offset, and Data Handling Demo',
|
|
185
|
+
font: { size: 18 }
|
|
186
|
+
},
|
|
187
|
+
legend: {
|
|
188
|
+
position: 'top',
|
|
189
|
+
},
|
|
190
|
+
tooltip: {
|
|
191
|
+
mode: 'index',
|
|
192
|
+
intersect: false,
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
maintainAspectRatio: false,
|
|
196
|
+
responsive: true,
|
|
197
|
+
scales: {
|
|
198
|
+
x: {
|
|
199
|
+
type: 'linear',
|
|
200
|
+
position: 'bottom',
|
|
201
|
+
title: { text: 'X Value / Days', display: true },
|
|
202
|
+
min: -5, // Adjusted min to give space
|
|
203
|
+
max: 80, // Adjusted max to give space for projection
|
|
204
|
+
},
|
|
205
|
+
y: {
|
|
206
|
+
type: 'linear',
|
|
207
|
+
position: 'left',
|
|
208
|
+
title: { text: 'Y Value / Count', display: true },
|
|
209
|
+
min: 0, // Adjusted min
|
|
210
|
+
max: 35, // Adjusted max
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
</script>
|
|
217
|
+
</head>
|
|
218
|
+
|
|
219
|
+
<body>
|
|
220
|
+
<div class="container">
|
|
221
|
+
<h1>Line Chart Projection</h1>
|
|
222
|
+
<p class="subtitle">Trendline projection, data offset, and null value handling</p>
|
|
223
|
+
|
|
224
|
+
<div class="chart-container">
|
|
225
|
+
<div class="chart-wrapper">
|
|
226
|
+
<canvas id="line-chart"></canvas>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<div class="info-section">
|
|
231
|
+
<h3>Observations Guide</h3>
|
|
232
|
+
<ul>
|
|
233
|
+
<li><strong>Dataset 1 (Blue, Dotted Trend):</strong> Shows a standard projected trendline extending to the chart's X-axis boundaries.</li>
|
|
234
|
+
<li><strong>Dataset 2 (Red, Dashed Trend):</strong> Demonstrates a <strong>non-projected</strong> trendline. Notice how it only covers the span of its own data points (approx. x=15 to x=35).</li>
|
|
235
|
+
<li><strong>Dataset 3 (Teal, Solid Trend):</strong>
|
|
236
|
+
<ul>
|
|
237
|
+
<li>Uses <code>projection: true</code>.</li>
|
|
238
|
+
<li><code>trendoffset: 2</code> means the first two data points ( (50,10) and (55,12) ) are excluded from the trendline calculation.</li>
|
|
239
|
+
<li>Contains a <code>null</code> data point (originally at x=65) which is correctly ignored by the trendline fitter.</li>
|
|
240
|
+
</ul>
|
|
241
|
+
</li>
|
|
242
|
+
<li><strong>Dataset 4 (Orange, Dotted Trend):</strong>
|
|
243
|
+
<ul>
|
|
244
|
+
<li>Uses <code>projection: true</code>.</li>
|
|
245
|
+
<li><code>trendoffset: -2</code> means the last two data points ( (40,7) and (50,10) ) are excluded from the trendline calculation.</li>
|
|
246
|
+
</ul>
|
|
247
|
+
</li>
|
|
248
|
+
<li>All trendlines should be clipped correctly within the chart area boundaries, even if their mathematical extension would go beyond.</li>
|
|
249
|
+
<li>The slope displayed in the labels should consistently reflect the underlying data used for fitting, regardless of projection.</li>
|
|
250
|
+
</ul>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<div class="nav-links">
|
|
254
|
+
<a href="../index.html">← Back to Examples</a>
|
|
255
|
+
<a href="lineChart.html">← Line Chart</a>
|
|
256
|
+
<a href="lineChartTypeTime.html">Time Series Line Chart →</a>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
</body>
|
|
261
|
+
|
|
262
262
|
</html>
|