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'`
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
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);
*/
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++) {
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);
}
}
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: {
--- /dev/null
+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}
+ }
+ }
+ }
+};
fill: true,
segment: {
backgroundColor: ctx => ctx.p0.skip || ctx.p1.skip ? 'red' : undefined,
- }
+ },
+ spanGaps: true
}]
},
options: {