]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Fix segment styling with gaps (#9644)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Mon, 13 Sep 2021 20:31:34 +0000 (23:31 +0300)
committerGitHub <noreply@github.com>
Mon, 13 Sep 2021 20:31:34 +0000 (16:31 -0400)
docs/charts/line.md
docs/samples/line/segments.md
src/elements/element.line.js
src/helpers/helpers.segment.js
test/fixtures/controller.line/segments/gap.js
test/fixtures/controller.line/segments/spanGaps.js [new file with mode: 0644]
test/fixtures/controller.line/segments/spanGaps.png [new file with mode: 0644]
test/fixtures/plugin.filler/line/segments/gap.js

index fd674a51910b12635f507a61c51a9b43afc68784..fc245fcb035309466ae71f4d67da09933d95c19d 100644 (file)
@@ -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'`
index b1b6a8735540629742b7991bd2e6b775a8a48ffc..287730cfe5737a5a8090c7841221776a3f4a5b2f 100644 (file)
@@ -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
index 55752c6c1997c221255d63a4ac3856b0f82e5079..87eb9f583b6dd53a7eb80547b91f3dbcef8df2ca 100644 (file)
@@ -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);
index dd9f4da9f2cb7e233e84551de6bc11539c818a39..8106c0629ebe367363ba64ae85efc1c1fbaf0401 100644 (file)
@@ -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++) {
@@ -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);
     }
   }
 
index fd08a4d55e3a5eaf0bff2d253028d51514752a8d..db0652caa7ac9ae15cade82231fd6e067a83b9e7 100644 (file)
@@ -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: {
diff --git a/test/fixtures/controller.line/segments/spanGaps.js b/test/fixtures/controller.line/segments/spanGaps.js
new file mode 100644 (file)
index 0000000..816d5be
--- /dev/null
@@ -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}
+      }
+    }
+  }
+};
diff --git a/test/fixtures/controller.line/segments/spanGaps.png b/test/fixtures/controller.line/segments/spanGaps.png
new file mode 100644 (file)
index 0000000..90b964a
Binary files /dev/null and b/test/fixtures/controller.line/segments/spanGaps.png differ
index 6dfb831563862b8a7530c03e06c0d48265004440..1038a0b118347e9830366ab46272743ceff98634 100644 (file)
@@ -10,7 +10,8 @@ module.exports = {
         fill: true,
         segment: {
           backgroundColor: ctx => ctx.p0.skip || ctx.p1.skip ? 'red' : undefined,
-        }
+        },
+        spanGaps: true
       }]
     },
     options: {