Skip to content

Commit 0316f3a

Browse files
nagixetimberg
authored andcommitted
Support spanGaps in radar charts (chartjs#6289)
* Support spanGaps in radar charts * Minor fixes based on feedback
1 parent eb3050f commit 0316f3a

29 files changed

+307
-48
lines changed

Diff for: docs/charts/radar.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ The radar chart allows a number of properties to be specified for each dataset.
8787
| [`pointRadius`](#point-styling) | `number` | Yes | Yes | `3`
8888
| [`pointRotation`](#point-styling) | `number` | Yes | Yes | `0`
8989
| [`pointStyle`](#point-styling) | <code>string&#124;Image</code> | Yes | Yes | `'circle'`
90+
| [`spanGaps`](#line-styling) | `boolean` | - | - | `undefined`
9091

9192
### General
9293

@@ -125,8 +126,9 @@ The style of the line can be controlled with the following properties:
125126
| `borderWidth` | The line width (in pixels).
126127
| `fill` | How to fill the area under the line. See [area charts](area.md).
127128
| `lineTension` | Bezier curve tension of the line. Set to 0 to draw straightlines.
129+
| `spanGaps` | If true, lines will be drawn between points with no or null data. If false, points with `NaN` data will create a break in the line.
128130

129-
All these values, if `undefined`, fallback to the associated [`elements.line.*`](../configuration/elements.md#line-configuration) options.
131+
If the value is `undefined`, `spanGaps` fallback to the associated [chart configuration options](#configuration-options). The rest of the values fallback to the associated [`elements.line.*`](../configuration/elements.md#line-configuration) options.
130132

131133
### Interactions
132134

Diff for: src/controllers/controller.radar.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,12 @@ module.exports = DatasetController.extend({
143143
*/
144144
_resolveDatasetElementOptions: function() {
145145
var me = this;
146+
var config = me._config;
147+
var options = me.chart.options;
146148
var values = DatasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);
147149

148-
values.tension = valueOrDefault(me._config.lineTension, me.chart.options.elements.line.tension);
150+
values.spanGaps = valueOrDefault(config.spanGaps, options.spanGaps);
151+
values.tension = valueOrDefault(config.lineTension, options.elements.line.tension);
149152

150153
return values;
151154
},
@@ -157,6 +160,13 @@ module.exports = DatasetController.extend({
157160
var points = meta.data || [];
158161
var i, ilen, model, controlPoints;
159162

163+
// Only consider points that are drawn in case the spanGaps option is used
164+
if (meta.dataset._model.spanGaps) {
165+
points = points.filter(function(pt) {
166+
return !pt._model.skip;
167+
});
168+
}
169+
160170
function capControlPoint(pt, min, max) {
161171
return Math.max(Math.min(pt, max), min);
162172
}

Diff for: src/elements/element.line.js

+10-12
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,16 @@ module.exports = Element.extend({
3838
var globalOptionLineElements = globalDefaults.elements.line;
3939
var lastDrawnIndex = -1;
4040
var closePath = me._loop;
41-
var index, current, previous, currentVM;
41+
var index, previous, currentVM;
4242

4343
if (me._loop && points.length) {
44-
if (!spanGaps) {
45-
for (index = points.length - 1; index >= 0; --index) {
46-
// If the line has an open path, shift the point array
47-
if (points[index]._view.skip) {
48-
points = points.slice(index).concat(points.slice(0, index));
49-
closePath = false;
50-
break;
51-
}
44+
for (index = 0; index < points.length; ++index) {
45+
previous = helpers.previousItem(points, index);
46+
// If the line has an open path, shift the point array
47+
if (!points[index]._view.skip && previous._view.skip) {
48+
points = points.slice(index).concat(points.slice(0, index));
49+
closePath = spanGaps;
50+
break;
5251
}
5352
}
5453
// If the line has a close path, add the first point again
@@ -77,9 +76,8 @@ module.exports = Element.extend({
7776
lastDrawnIndex = -1;
7877

7978
for (index = 0; index < points.length; ++index) {
80-
current = points[index];
8179
previous = helpers.previousItem(points, index);
82-
currentVM = current._view;
80+
currentVM = points[index]._view;
8381

8482
// First point moves to it's starting position no matter what
8583
if (index === 0) {
@@ -96,7 +94,7 @@ module.exports = Element.extend({
9694
ctx.moveTo(currentVM.x, currentVM.y);
9795
} else {
9896
// Line to next point
99-
helpers.canvas.lineTo(ctx, previous._view, current._view);
97+
helpers.canvas.lineTo(ctx, previous._view, currentVM);
10098
}
10199
lastDrawnIndex = index;
102100
}

Diff for: src/plugins/plugin.filler.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -271,17 +271,22 @@ function doFill(ctx, points, mapper, view, color, loop) {
271271
var curve1 = [];
272272
var len0 = 0;
273273
var len1 = 0;
274-
var i, ilen, index, p0, p1, d0, d1;
274+
var i, ilen, index, p0, p1, d0, d1, loopOffset;
275275

276276
ctx.beginPath();
277277

278-
for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
278+
for (i = 0, ilen = count; i < ilen; ++i) {
279279
index = i % count;
280280
p0 = points[index]._view;
281281
p1 = mapper(p0, index, view);
282282
d0 = isDrawable(p0);
283283
d1 = isDrawable(p1);
284284

285+
if (loop && loopOffset === undefined && d0) {
286+
loopOffset = i + 1;
287+
ilen = count + loopOffset;
288+
}
289+
285290
if (d0 && d1) {
286291
len0 = curve0.push(p0);
287292
len1 = curve1.push(p1);

Diff for: test/fixtures/plugin.filler/fill-radar-boundary-end-circular.json

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@
55
"labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"],
66
"datasets": [{
77
"backgroundColor": "rgba(0, 0, 192, 0.25)",
8-
"data": [null, null, 2, 4, 2, 1, -1, 1, 2]
8+
"data": [null, null, 2, 3, 4, -4, -2, 1, 0]
99
}, {
1010
"backgroundColor": "rgba(0, 192, 0, 0.25)",
11-
"data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3]
11+
"data": [5.5, 2, null, 4, 5, null, null, 2, 1]
1212
}, {
1313
"backgroundColor": "rgba(192, 0, 0, 0.25)",
14-
"data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null]
14+
"data": [7, 3, 4, 5, 6, 1, 4, null, null]
1515
}, {
16-
"backgroundColor": "rgba(128, 0, 128, 0.25)",
17-
"data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5]
16+
"backgroundColor": "rgba(0, 0, 192, 0.25)",
17+
"data": [8, 7, 6.5, -6, -4, -6, 4, 5, 8]
1818
}]
1919
},
2020
"options": {
2121
"responsive": false,
22+
"spanGaps": false,
2223
"legend": false,
2324
"title": false,
2425
"scale": {
-1.06 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"config": {
3+
"type": "radar",
4+
"data": {
5+
"labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"],
6+
"datasets": [{
7+
"backgroundColor": "rgba(0, 0, 192, 0.25)",
8+
"data": [null, null, 2, 3, 4, -4, -2, 1, 0]
9+
}, {
10+
"backgroundColor": "rgba(0, 192, 0, 0.25)",
11+
"data": [5.5, 2, null, 4, 5, null, null, 2, 1]
12+
}, {
13+
"backgroundColor": "rgba(192, 0, 0, 0.25)",
14+
"data": [7, 3, 4, 5, 6, 1, 4, null, null]
15+
}, {
16+
"backgroundColor": "rgba(0, 0, 192, 0.25)",
17+
"data": [8, 7, 6.5, -6, -4, -6, 4, 5, 8]
18+
}]
19+
},
20+
"options": {
21+
"responsive": false,
22+
"spanGaps": true,
23+
"legend": false,
24+
"title": false,
25+
"scale": {
26+
"display": false
27+
},
28+
"elements": {
29+
"point": {
30+
"radius": 0
31+
},
32+
"line": {
33+
"borderColor": "transparent",
34+
"fill": "end"
35+
}
36+
}
37+
}
38+
},
39+
"options": {
40+
"canvas": {
41+
"height": 256,
42+
"width": 256
43+
}
44+
}
45+
}
18.3 KB
Loading

Diff for: test/fixtures/plugin.filler/fill-radar-boundary-end.json

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@
55
"labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"],
66
"datasets": [{
77
"backgroundColor": "rgba(0, 0, 192, 0.25)",
8-
"data": [null, null, 2, 4, 2, 1, -1, 1, 2]
8+
"data": [null, null, 2, 3, 4, -4, -2, 1, 0]
99
}, {
1010
"backgroundColor": "rgba(0, 192, 0, 0.25)",
11-
"data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3]
11+
"data": [5.5, 2, null, 4, 5, null, null, 2, 1]
1212
}, {
1313
"backgroundColor": "rgba(192, 0, 0, 0.25)",
14-
"data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null]
14+
"data": [7, 3, 4, 5, 6, 1, 4, null, null]
1515
}, {
16-
"backgroundColor": "rgba(128, 0, 128, 0.25)",
17-
"data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5]
16+
"backgroundColor": "rgba(0, 0, 192, 0.25)",
17+
"data": [8, 7, 6.5, -6, -4, -6, 4, 5, 8]
1818
}]
1919
},
2020
"options": {
2121
"responsive": false,
22+
"spanGaps": false,
2223
"legend": false,
2324
"title": false,
2425
"scale": {
-1.63 KB
Loading

Diff for: test/fixtures/plugin.filler/fill-radar-boundary-origin-circular.json

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
},
2020
"options": {
2121
"responsive": false,
22+
"spanGaps": false,
2223
"legend": false,
2324
"title": false,
2425
"scale": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"config": {
3+
"type": "radar",
4+
"data": {
5+
"labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"],
6+
"datasets": [{
7+
"backgroundColor": "rgba(0, 0, 192, 0.25)",
8+
"data": [null, null, 2, 3, 4, -4, -2, 1, 0]
9+
}, {
10+
"backgroundColor": "rgba(0, 192, 0, 0.25)",
11+
"data": [6, 2, null, 4, 5, null, null, 2, 1]
12+
}, {
13+
"backgroundColor": "rgba(192, 0, 0, 0.25)",
14+
"data": [7, 3, 4, 5, 6, 1, 4, null, null]
15+
}, {
16+
"backgroundColor": "rgba(0, 64, 192, 0.25)",
17+
"data": [8, 7, 6, -6, -4, -6, 4, 5, 8]
18+
}]
19+
},
20+
"options": {
21+
"responsive": false,
22+
"spanGaps": true,
23+
"legend": false,
24+
"title": false,
25+
"scale": {
26+
"display": false
27+
},
28+
"elements": {
29+
"point": {
30+
"radius": 0
31+
},
32+
"line": {
33+
"borderColor": "transparent",
34+
"fill": "origin"
35+
}
36+
}
37+
}
38+
},
39+
"options": {
40+
"canvas": {
41+
"height": 256,
42+
"width": 256
43+
}
44+
}
45+
}
17.5 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"config": {
3+
"type": "radar",
4+
"data": {
5+
"labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"],
6+
"datasets": [{
7+
"backgroundColor": "rgba(0, 0, 192, 0.25)",
8+
"data": [null, null, 2, 4, 2, 1, -1, 1, 2]
9+
}, {
10+
"backgroundColor": "rgba(0, 192, 0, 0.25)",
11+
"data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3]
12+
}, {
13+
"backgroundColor": "rgba(192, 0, 0, 0.25)",
14+
"data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null]
15+
}, {
16+
"backgroundColor": "rgba(128, 0, 128, 0.25)",
17+
"data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5]
18+
}]
19+
},
20+
"options": {
21+
"responsive": false,
22+
"spanGaps": true,
23+
"legend": false,
24+
"title": false,
25+
"scale": {
26+
"display": false
27+
},
28+
"elements": {
29+
"point": {
30+
"radius": 0
31+
},
32+
"line": {
33+
"borderColor": "transparent",
34+
"tension": 0.5,
35+
"fill": "origin"
36+
}
37+
}
38+
}
39+
},
40+
"options": {
41+
"canvas": {
42+
"height": 256,
43+
"width": 256
44+
}
45+
}
46+
}
Loading

Diff for: test/fixtures/plugin.filler/fill-radar-boundary-origin-spline.json

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
},
2020
"options": {
2121
"responsive": false,
22+
"spanGaps": false,
2223
"legend": false,
2324
"title": false,
2425
"scale": {

Diff for: test/fixtures/plugin.filler/fill-radar-boundary-origin.json

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@
55
"labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"],
66
"datasets": [{
77
"backgroundColor": "rgba(0, 0, 192, 0.25)",
8-
"data": [null, null, 2, 4, 2, 1, -1, 1, 2]
8+
"data": [null, null, 2, 3, 4, -4, -2, 1, 0]
99
}, {
1010
"backgroundColor": "rgba(0, 192, 0, 0.25)",
11-
"data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3]
11+
"data": [6, 2, null, 4, 5, null, null, 2, 1]
1212
}, {
1313
"backgroundColor": "rgba(192, 0, 0, 0.25)",
14-
"data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null]
14+
"data": [7, 3, 4, 5, 6, 1, 4, null, null]
1515
}, {
16-
"backgroundColor": "rgba(128, 0, 128, 0.25)",
17-
"data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5]
16+
"backgroundColor": "rgba(0, 64, 192, 0.25)",
17+
"data": [8, 7, 6, -6, -4, -6, 4, 5, 8]
1818
}]
1919
},
2020
"options": {
2121
"responsive": false,
22+
"spanGaps": false,
2223
"legend": false,
2324
"title": false,
2425
"scale": {
166 Bytes
Loading

Diff for: test/fixtures/plugin.filler/fill-radar-boundary-start-circular.json

+9-8
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,22 @@
44
"data": {
55
"labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"],
66
"datasets": [{
7-
"backgroundColor": "rgba(0, 0, 192, 0.25)",
8-
"data": [null, null, 2, 4, 2, 1, -1, 1, 2]
7+
"backgroundColor": "rgba(0, 0, 255, 0.25)",
8+
"data": [null, null, 2, 3, 4, -4, -2, 1, 0]
99
}, {
10-
"backgroundColor": "rgba(0, 192, 0, 0.25)",
11-
"data": [4, 2, null, 3, 2.5, null, -2, 1.5, 3]
10+
"backgroundColor": "rgba(0, 255, 0, 0.25)",
11+
"data": [6, 2, null, 4, 5, null, null, 2, 1]
1212
}, {
13-
"backgroundColor": "rgba(192, 0, 0, 0.25)",
14-
"data": [3.5, 2, 1, 2.5, -2, 3, -1, null, null]
13+
"backgroundColor": "rgba(255, 0, 0, 0.25)",
14+
"data": [7, 3, 4, 5, 6, 1, 4, null, null]
1515
}, {
16-
"backgroundColor": "rgba(128, 0, 128, 0.25)",
17-
"data": [5, 6, 5, -2, -4, -3, 4, 2, 4.5]
16+
"backgroundColor": "rgba(0, 0, 255, 0.25)",
17+
"data": [8, 7, 6, -6, -4, -6, 4, 5, 8]
1818
}]
1919
},
2020
"options": {
2121
"responsive": false,
22+
"spanGaps": false,
2223
"legend": false,
2324
"title": false,
2425
"scale": {
-549 Bytes
Loading

0 commit comments

Comments
 (0)