Skip to content

Commit

Permalink
Fix segment styling with gaps (#9644)
Browse files Browse the repository at this point in the history
  • Loading branch information
kurkle committed Sep 13, 2021
1 parent 0e64e53 commit 183a1a9
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 11 deletions.
4 changes: 4 additions & 0 deletions docs/charts/line.md
Expand Up @@ -163,6 +163,10 @@ If left untouched (`undefined`), the global `options.elements.line.cubicInterpol

Line segment styles can be overridden by scriptable options in the `segment` object. Currently all of the `border*` and `backgroundColor` options are supported. The segment styles are resolved for each section of the line between each point. `undefined` fallbacks to main line styles.

:::tip
To be able to style gaps, you need the [`spanGaps`](#line-styling) option enabled.
:::

Context for the scriptable segment contains the following properties:

* `type`: `'segment'`
Expand Down
3 changes: 2 additions & 1 deletion docs/samples/line/segments.md
Expand Up @@ -29,7 +29,8 @@ const config = {
segment: {
borderColor: ctx => skipped(ctx, 'rgb(0,0,0,0.2)') || down(ctx, 'rgb(192,75,75)'),
borderDash: ctx => skipped(ctx, [6, 6]),
}
},
spanGaps: true
}]
},
options: genericOptions
Expand Down
2 changes: 1 addition & 1 deletion src/elements/element.line.js
Expand Up @@ -230,7 +230,7 @@ function strokePathDirect(ctx, line, start, count) {
const usePath2D = typeof Path2D === 'function';

function draw(ctx, line, start, count) {
if (usePath2D && line.segments.length === 1) {
if (usePath2D && !line.options.segment) {
strokePathWithCache(ctx, line, start, count);
} else {
strokePathDirect(ctx, line, start, count);
Expand Down
34 changes: 27 additions & 7 deletions src/helpers/helpers.segment.js
Expand Up @@ -276,12 +276,35 @@ function splitByStyles(line, segments, points, segmentOptions) {
*/
function doSplitByStyles(line, segments, points, segmentOptions) {
const baseStyle = readStyle(line.options);
const {_datasetIndex: datasetIndex, options: {spanGaps}} = line;
const count = points.length;
const result = [];
let prevStyle = baseStyle;
let start = segments[0].start;
let i = start;

function addStyle(s, e, l, st) {
const dir = spanGaps ? -1 : 1;
if (s === e) {
return;
}
// Style can not start/end on a skipped point, adjust indices accordingly
s += count;
while (points[s % count].skip) {
s -= dir;
}
while (points[e % count].skip) {
e += dir;
}
if (s % count !== e % count) {
result.push({start: s % count, end: e % count, loop: l, style: st});
prevStyle = st;
start = e % count;
}
}

for (const segment of segments) {
let prevStyle = baseStyle;
start = spanGaps ? start : segment.start;
let prev = points[start % count];
let style;
for (i = start + 1; i <= segment.end; i++) {
Expand All @@ -292,19 +315,16 @@ function doSplitByStyles(line, segments, points, segmentOptions) {
p1: pt,
p0DataIndex: (i - 1) % count,
p1DataIndex: i % count,
datasetIndex: line._datasetIndex
datasetIndex
}));
if (styleChanged(style, prevStyle)) {
result.push({start: start, end: i - 1, loop: segment.loop, style: prevStyle});
prevStyle = style;
start = i - 1;
addStyle(start, i - 1, segment.loop, prevStyle);
}
prev = pt;
prevStyle = style;
}
if (start < i - 1) {
result.push({start, end: i - 1, loop: segment.loop, style});
start = i - 1;
addStyle(start, i - 1, segment.loop, prevStyle);
}
}

Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/controller.line/segments/gap.js
Expand Up @@ -9,7 +9,8 @@ module.exports = {
segment: {
borderColor: ctx => ctx.p0.skip || ctx.p1.skip ? 'red' : undefined,
borderDash: ctx => ctx.p0.skip || ctx.p1.skip ? [5, 5] : undefined
}
},
spanGaps: true
}]
},
options: {
Expand Down
31 changes: 31 additions & 0 deletions test/fixtures/controller.line/segments/spanGaps.js
@@ -0,0 +1,31 @@
module.exports = {
config: {
type: 'line',
data: {
labels: ['a', 'b', 'c', 'd', 'e', 'f'],
datasets: [{
data: [1, 3, null, null, 2, 1],
segment: {
borderColor: ctx => ctx.p1.parsed.x > 2 ? 'red' : undefined,
borderDash: ctx => ctx.p1.parsed.x > 3 ? [6, 6] : undefined,
},
spanGaps: true
}, {
data: [0, 2, null, null, 1, 0],
segment: {
borderColor: ctx => ctx.p1.parsed.x > 2 ? 'red' : undefined,
borderDash: ctx => ctx.p1.parsed.x > 3 ? [6, 6] : undefined,
},
spanGaps: false
}]
},
options: {
borderColor: 'black',
radius: 0,
scales: {
x: {display: false},
y: {display: false}
}
}
}
};
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion test/fixtures/plugin.filler/line/segments/gap.js
Expand Up @@ -10,7 +10,8 @@ module.exports = {
fill: true,
segment: {
backgroundColor: ctx => ctx.p0.skip || ctx.p1.skip ? 'red' : undefined,
}
},
spanGaps: true
}]
},
options: {
Expand Down

0 comments on commit 183a1a9

Please sign in to comment.