Skip to content

Commit 7eb0c2c

Browse files
kurkleetimberg
authored andcommitted
Allow specifying spanGaps as number (max distance) (#6993)
1 parent 9fda5ec commit 7eb0c2c

File tree

5 files changed

+165
-6
lines changed

5 files changed

+165
-6
lines changed

docs/charts/line.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ The line chart allows a number of properties to be specified for each dataset. T
7777
| [`pointRotation`](#point-styling) | `number` | Yes | Yes | `0`
7878
| [`pointStyle`](#point-styling) | <code>string&#124;Image</code> | Yes | Yes | `'circle'`
7979
| [`showLine`](#line-styling) | `boolean` | - | - | `undefined`
80-
| [`spanGaps`](#line-styling) | `boolean` | - | - | `undefined`
80+
| [`spanGaps`](#line-styling) | <code>boolean&#124;number</code> | - | - | `undefined`
8181
| [`steppedLine`](#stepped-line) | <code>boolean&#124;string</code> | - | - | `false`
8282
| [`xAxisID`](#general) | `string` | - | - | first x axis
8383
| [`yAxisID`](#general) | `string` | - | - | first y axis
@@ -124,7 +124,7 @@ The style of the line can be controlled with the following properties:
124124
| `fill` | How to fill the area under the line. See [area charts](area.md).
125125
| `lineTension` | Bezier curve tension of the line. Set to 0 to draw straightlines. This option is ignored if monotone cubic interpolation is used.
126126
| `showLine` | If false, the line is not drawn for this dataset.
127-
| `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.
127+
| `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. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used.
128128

129129
If the value is `undefined`, `showLine` and `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.
130130

samples/samples.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@
121121
}, {
122122
title: 'Line (point data)',
123123
path: 'scales/time/line-point-data.html'
124+
}, {
125+
title: 'Line (break on 2 day gap)',
126+
path: 'scales/time/line-max-span.html'
124127
}, {
125128
title: 'Time Series',
126129
path: 'scales/time/financial.html'
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<!doctype html>
2+
<html>
3+
4+
<head>
5+
<title>Time Scale Point Data</title>
6+
<script src="https://cdn.jsdelivr.net/npm/moment@2.24.0/moment.min.js"></script>
7+
<script src="../../../dist/Chart.min.js"></script>
8+
<script src="../../utils.js"></script>
9+
<style>
10+
canvas {
11+
-moz-user-select: none;
12+
-webkit-user-select: none;
13+
-ms-user-select: none;
14+
}
15+
</style>
16+
</head>
17+
18+
<body>
19+
<div style="width:75%;">
20+
<canvas id="canvas"></canvas>
21+
</div>
22+
<br>
23+
<br>
24+
<button id="randomizeData">Randomize Data</button>
25+
<button id="addData">Add Data</button>
26+
<button id="removeData">Remove Data</button>
27+
<script>
28+
function newDate(days) {
29+
return moment().add(days, 'd').toDate();
30+
}
31+
32+
function newDateString(days) {
33+
return moment().add(days, 'd').format();
34+
}
35+
36+
var color = Chart.helpers.color;
37+
var config = {
38+
type: 'line',
39+
data: {
40+
datasets: [{
41+
label: 'Dataset with string point data',
42+
backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
43+
borderColor: window.chartColors.red,
44+
fill: false,
45+
data: [{
46+
x: newDateString(0),
47+
y: randomScalingFactor()
48+
}, {
49+
x: newDateString(2),
50+
y: randomScalingFactor()
51+
}, {
52+
x: newDateString(4),
53+
y: randomScalingFactor()
54+
}, {
55+
x: newDateString(6),
56+
y: randomScalingFactor()
57+
}],
58+
}, {
59+
label: 'Dataset with date object point data',
60+
backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
61+
borderColor: window.chartColors.blue,
62+
fill: false,
63+
data: [{
64+
x: newDate(0),
65+
y: randomScalingFactor()
66+
}, {
67+
x: newDate(2),
68+
y: randomScalingFactor()
69+
}, {
70+
x: newDate(5),
71+
y: randomScalingFactor()
72+
}, {
73+
x: newDate(6),
74+
y: randomScalingFactor()
75+
}]
76+
}]
77+
},
78+
options: {
79+
spanGaps: 1000 * 60 * 60 * 24 * 2, // 2 days
80+
responsive: true,
81+
title: {
82+
display: true,
83+
text: 'Chart.js Time - spanGaps: 172800000 (2 days in ms)'
84+
},
85+
scales: {
86+
x: {
87+
type: 'time',
88+
display: true,
89+
scaleLabel: {
90+
display: true,
91+
labelString: 'Date'
92+
},
93+
ticks: {
94+
major: {
95+
fontStyle: 'bold',
96+
fontColor: '#FF0000'
97+
}
98+
}
99+
},
100+
y: {
101+
display: true,
102+
scaleLabel: {
103+
display: true,
104+
labelString: 'value'
105+
}
106+
}
107+
}
108+
}
109+
};
110+
111+
window.onload = function() {
112+
var ctx = document.getElementById('canvas').getContext('2d');
113+
window.myLine = new Chart(ctx, config);
114+
};
115+
116+
document.getElementById('randomizeData').addEventListener('click', function() {
117+
config.data.datasets.forEach(function(dataset) {
118+
dataset.data.forEach(function(dataObj) {
119+
dataObj.y = randomScalingFactor();
120+
});
121+
});
122+
123+
window.myLine.update();
124+
});
125+
document.getElementById('addData').addEventListener('click', function() {
126+
if (config.data.datasets.length > 0) {
127+
config.data.datasets[0].data.push({
128+
x: newDateString(config.data.datasets[0].data.length + 2),
129+
y: randomScalingFactor()
130+
});
131+
config.data.datasets[1].data.push({
132+
x: newDate(config.data.datasets[1].data.length + 2),
133+
y: randomScalingFactor()
134+
});
135+
136+
window.myLine.update();
137+
}
138+
});
139+
140+
document.getElementById('removeData').addEventListener('click', function() {
141+
config.data.datasets.forEach(function(dataset) {
142+
dataset.data.pop();
143+
});
144+
145+
window.myLine.update();
146+
});
147+
</script>
148+
</body>
149+
150+
</html>

src/controllers/controller.line.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ export default DatasetController.extend({
9898
const firstOpts = me._resolveDataElementOptions(start, mode);
9999
const sharedOptions = me._getSharedOptions(mode, points[start], firstOpts);
100100
const includeOptions = me._includeOptions(mode, sharedOptions);
101+
const spanGaps = valueOrDefault(me._config.spanGaps, me.chart.options.spanGaps);
102+
const maxGapLength = helpers.math.isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
103+
let prevParsed;
101104

102105
for (let i = 0; i < points.length; ++i) {
103106
const index = start + i;
@@ -108,14 +111,17 @@ export default DatasetController.extend({
108111
const properties = {
109112
x,
110113
y,
111-
skip: isNaN(x) || isNaN(y)
114+
skip: isNaN(x) || isNaN(y),
115+
stop: i > 0 && (parsed.x - prevParsed.x) > maxGapLength
112116
};
113117

114118
if (includeOptions) {
115119
properties.options = me._resolveDataElementOptions(index, mode);
116120
}
117121

118122
me._updateElement(point, index, properties, mode);
123+
124+
prevParsed = parsed;
119125
}
120126

121127
me._updateSharedOptions(sharedOptions, mode);

src/helpers/helpers.segment.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,11 @@ function solidSegments(points, start, max, loop) {
179179

180180
for (end = start + 1; end <= max; ++end) {
181181
const cur = points[end % count];
182-
if (cur.skip) {
182+
if (cur.skip || cur.stop) {
183183
if (!prev.skip) {
184184
loop = false;
185185
result.push({start: start % count, end: (end - 1) % count, loop});
186-
start = last = null;
186+
start = last = cur.stop ? end : null;
187187
}
188188
} else {
189189
last = end;
@@ -218,7 +218,7 @@ export function _computeSegments(line) {
218218
const loop = !!line._loop;
219219
const {start, end} = findStartAndEnd(points, count, loop, spanGaps);
220220

221-
if (spanGaps) {
221+
if (spanGaps === true) {
222222
return [{start, end, loop}];
223223
}
224224

0 commit comments

Comments
 (0)