]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Scale: draw offset grid for labels before autoSkip (#8748)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Mon, 29 Mar 2021 20:53:47 +0000 (23:53 +0300)
committerGitHub <noreply@github.com>
Mon, 29 Mar 2021 20:53:47 +0000 (16:53 -0400)
* Scale: draw offset grid for labels before autoSkip
* fix tests

21 files changed:
docs/docs/axes/styling.mdx
src/core/core.scale.js
src/scales/scale.category.js
test/fixtures/core.scale/autoSkip/offset.png
test/fixtures/core.scale/crossAlignment/cross-align-bottom-center.png
test/fixtures/core.scale/crossAlignment/cross-align-bottom-far.png
test/fixtures/core.scale/crossAlignment/cross-align-bottom-near.png
test/fixtures/core.scale/crossAlignment/cross-align-top-center.png
test/fixtures/core.scale/crossAlignment/cross-align-top-far.png
test/fixtures/core.scale/crossAlignment/cross-align-top-near.png
test/fixtures/scale.category/autoskip-grid-x.js [new file with mode: 0644]
test/fixtures/scale.category/autoskip-grid-x.png [new file with mode: 0644]
test/fixtures/scale.category/autoskip-grid-y.js [new file with mode: 0644]
test/fixtures/scale.category/autoskip-grid-y.png [new file with mode: 0644]
test/fixtures/scale.time/offset-with-1-tick.png
test/fixtures/scale.time/offset-with-2-ticks.png
test/fixtures/scale.timeseries/financial-daily.png
test/specs/core.layouts.tests.js
test/specs/platform.basic.tests.js
test/specs/scale.category.tests.js
test/specs/scale.time.tests.js

index 5a98fe3fea349dc70f0d5c8dbb8aed835b71f884..76ce82152fad8e823ce8974b77cc959594c70864 100644 (file)
@@ -23,7 +23,7 @@ Namespace: `options.scales[scaleId].grid`, it defines options for the grid lines
 | `drawOnChartArea` | `boolean` | | | `true` | If true, draw lines on the chart area inside the axis lines. This is useful when there are multiple axes and you need to control which grid lines are drawn.
 | `drawTicks` | `boolean` | | | `true` | If true, draw lines beside the ticks in the axis area beside the chart.
 | `lineWidth` | `number` | Yes | Yes | `1` | Stroke width of grid lines.
-| `offset` | `boolean` | | | `false` | If true, grid lines will be shifted to be between labels. This is set to `true` for a bar chart by default.
+| `offset` | `boolean` | | | `false` | If true, grid lines will be shifted to be between labels. This is set to `true` for a bar chart by default. Note: AutoSkip does not remove offset grid lines.
 | `tickBorderDash` | `number[]` | | | | Length and spacing of the tick mark line. If not set, defaults to the grid line `borderDash` value.
 | `tickBorderDashOffset` | `number` | Yes | Yes |  | Offset for the line dash of the tick mark. If unset, defaults to the grid line `borderDashOffset` value
 | `tickColor` | [`Color`](../general/colors.md) | Yes | Yes | | Color of the tick line. If unset, defaults to the grid line color.
index 3913ad30291792607ea5c8e110c350a37251d1f9..9d8d14d4cc49f609bbbd81384210302cb8d6eaff 100644 (file)
@@ -42,21 +42,21 @@ function sample(arr, numItems) {
  * @param {boolean} offsetGridLines
  */
 function getPixelForGridLine(scale, index, offsetGridLines) {
-  const length = scale.ticks.length;
+  const length = offsetGridLines ? scale._allTicks.length : scale.ticks.length;
   const validIndex = Math.min(index, length - 1);
   const start = scale._startPixel;
   const end = scale._endPixel;
   const epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
-  let lineValue = scale.getPixelForTick(validIndex);
+  let lineValue = scale.getPixelForTick(validIndex, offsetGridLines);
   let offset;
 
   if (offsetGridLines) {
     if (length === 1) {
       offset = Math.max(lineValue - start, end - lineValue);
     } else if (index === 0) {
-      offset = (scale.getPixelForTick(1) - lineValue) / 2;
+      offset = (scale.getPixelForTick(1, offsetGridLines) - lineValue) / 2;
     } else {
-      offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2;
+      offset = (lineValue - scale.getPixelForTick(validIndex - 1, offsetGridLines)) / 2;
     }
     lineValue += validIndex < index ? offset : -offset;
 
@@ -205,7 +205,7 @@ export default class Scale extends Element {
     this.min = undefined;
     this.max = undefined;
     /** @type {Tick[]} */
-    this.ticks = [];
+    this.ticks = this._allTicks = [];
     /** @type {object[]|null} */
     this._gridLineItems = null;
     /** @type {object[]|null} */
@@ -428,6 +428,7 @@ export default class Scale extends Element {
     me.afterCalculateLabelRotation();
 
     // Auto-skip
+    me._allTicks = me.ticks;
     if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) {
       me.ticks = autoSkip(me, me.ticks);
       me._labelSizes = null;
@@ -666,7 +667,7 @@ export default class Scale extends Element {
 
   _calculatePadding(first, last, sin, cos) {
     const me = this;
-    const {ticks: {align, padding}, position} = me.options;
+    const {position, ticks: {align, padding}} = me.options;
     const isRotated = me.labelRotation !== 0;
     const labelsBelowTicks = position !== 'top' && me.axis === 'x';
 
@@ -696,8 +697,8 @@ export default class Scale extends Element {
       }
 
       // Adjust padding taking into account changes in offsets
-      me.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * me.width / (me.width - offsetLeft), 0);
-      me.paddingRight = Math.max((paddingRight - offsetRight + padding) * me.width / (me.width - offsetRight), 0);
+      me.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * me.width / (me.width - offsetLeft), padding);
+      me.paddingRight = Math.max((paddingRight - offsetRight + padding) * me.width / (me.width - offsetRight), padding);
     } else {
       let paddingTop = last.height / 2;
       let paddingBottom = first.height / 2;
@@ -871,10 +872,11 @@ export default class Scale extends Element {
         * Returns the location of the tick at the given index
         * The coordinate (0, 0) is at the upper-left corner of the canvas
         * @param {number} index
+   * @param {boolean} [all] - use ticks before autoSkip
         * @return {number}
         */
-  getPixelForTick(index) {
-    const ticks = this.ticks;
+  getPixelForTick(index, all = false) {
+    const ticks = all ? this._allTicks : this.ticks;
     if (index < 0 || index > ticks.length - 1) {
       return null;
     }
@@ -992,7 +994,7 @@ export default class Scale extends Element {
     const {grid, position} = options;
     const offset = grid.offset;
     const isHorizontal = me.isHorizontal();
-    const ticks = me.ticks;
+    const ticks = offset ? me._allTicks : me.ticks;
     const ticksLength = ticks.length + (offset ? 1 : 0);
     const tl = getTickMarkLength(grid);
     const items = [];
index 28f57b082c9199bb36d5aa0d80dfa9148d639704..ea0716a4caa1e86640e7cceedfab738c0eddca83 100644 (file)
@@ -109,17 +109,6 @@ export default class CategoryScale extends Scale {
     return value === null ? NaN : me.getPixelForDecimal((value - me._startValue) / me._valueRange);
   }
 
-  // Must override base implementation because it calls getPixelForValue
-  // and category scale can have duplicate values
-  getPixelForTick(index) {
-    const me = this;
-    const ticks = me.ticks;
-    if (index < 0 || index > ticks.length - 1) {
-      return null;
-    }
-    return me.getPixelForValue(ticks[index].value);
-  }
-
   getValueForPixel(pixel) {
     const me = this;
     return Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange);
index 72ee4eef4a161225e9a40c67f1d1c7f10914de02..95db4cd4b60d2f7b2e28de55db91b3a54016154b 100644 (file)
Binary files a/test/fixtures/core.scale/autoSkip/offset.png and b/test/fixtures/core.scale/autoSkip/offset.png differ
index 6f3aaed90fede449c7824693395e278962c9386c..4cd38a6bd63073f1cbfeba9123704aa0b64482a7 100644 (file)
Binary files a/test/fixtures/core.scale/crossAlignment/cross-align-bottom-center.png and b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-center.png differ
index abca217fa27f97b0c881af22ab4b1beebab8c863..e4c99e597e75a16f5ccfeeb32a6e60adf7aa4be8 100644 (file)
Binary files a/test/fixtures/core.scale/crossAlignment/cross-align-bottom-far.png and b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-far.png differ
index 02791ad91bb9cbc29a42dc2b0ff093d0c1d84e44..73dae717492eb294842c891c53dcfc0be6335416 100644 (file)
Binary files a/test/fixtures/core.scale/crossAlignment/cross-align-bottom-near.png and b/test/fixtures/core.scale/crossAlignment/cross-align-bottom-near.png differ
index c9083a830d8a17530ba41a7ecd7502496a0eae38..04865d402fbcab6c8084b8b2efdc1777f58ec3cc 100644 (file)
Binary files a/test/fixtures/core.scale/crossAlignment/cross-align-top-center.png and b/test/fixtures/core.scale/crossAlignment/cross-align-top-center.png differ
index b186168a8d9e3abf019ef101e624a5f6094ade07..8e0316424861859ab5016cac6458d8d505235d51 100644 (file)
Binary files a/test/fixtures/core.scale/crossAlignment/cross-align-top-far.png and b/test/fixtures/core.scale/crossAlignment/cross-align-top-far.png differ
index ce9e5c67a8b955dc925973b5989ae3f1da353ec3..c306c9b980108642b361f3ac4f14a64f874ea3f3 100644 (file)
Binary files a/test/fixtures/core.scale/crossAlignment/cross-align-top-near.png and b/test/fixtures/core.scale/crossAlignment/cross-align-top-near.png differ
diff --git a/test/fixtures/scale.category/autoskip-grid-x.js b/test/fixtures/scale.category/autoskip-grid-x.js
new file mode 100644 (file)
index 0000000..6912fa3
--- /dev/null
@@ -0,0 +1,38 @@
+module.exports = {
+  config: {
+    type: 'bar',
+    data: {
+      labels: ['Label 1', 'Label 2', 'Label 3', 'Label 4', 'Label 5', 'Label 6', 'Label 7', 'Label 8', 'Label 9', 'Label 10', 'Label 11', 'Label 12'],
+      datasets: [{
+        data: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+      }, {
+        data: [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
+      }, {
+        data: [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
+      }]
+    },
+    options: {
+      scales: {
+        x: {
+          ticks: {
+            maxRotation: 0
+          },
+          grid: {
+            color: 'red',
+            lineWidth: 1
+          }
+        },
+        y: {
+          display: false
+        }
+      }
+    }
+  },
+  options: {
+    spriteText: true,
+    canvas: {
+      width: 512,
+      height: 256
+    }
+  }
+};
diff --git a/test/fixtures/scale.category/autoskip-grid-x.png b/test/fixtures/scale.category/autoskip-grid-x.png
new file mode 100644 (file)
index 0000000..3706780
Binary files /dev/null and b/test/fixtures/scale.category/autoskip-grid-x.png differ
diff --git a/test/fixtures/scale.category/autoskip-grid-y.js b/test/fixtures/scale.category/autoskip-grid-y.js
new file mode 100644 (file)
index 0000000..0f9b83e
--- /dev/null
@@ -0,0 +1,37 @@
+module.exports = {
+  config: {
+    type: 'bar',
+    data: {
+      // labels: [['Label 1', 'Line 2'], ['Label 2', 'Line 2'], ['Label 3', 'Line 2'], ['Label 4', 'Line 2'], ['Label 5', 'Line 2'], ['Label 6', 'Line 2'], ['Label 7', 'Line 2'], ['Label 8', 'Line 2'], ['Label 9', 'Line 2'], ['Label 10', 'Line 2'], ['Label 11', 'Line 2'], ['Label 12', 'Line 2']],
+      labels: ['Label 1', 'Label 2', 'Label 3', 'Label 4', 'Label 5', 'Label 6', 'Label 7', 'Label 8', 'Label 9', 'Label 10', 'Label 11', 'Label 12'],
+      datasets: [{
+        data: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+      }, {
+        data: [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
+      }, {
+        data: [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
+      }]
+    },
+    options: {
+      indexAxis: 'y',
+      scales: {
+        y: {
+          grid: {
+            color: 'red',
+            lineWidth: 1
+          }
+        },
+        x: {
+          display: false
+        }
+      }
+    }
+  },
+  options: {
+    spriteText: true,
+    canvas: {
+      width: 128,
+      height: 200
+    }
+  }
+};
diff --git a/test/fixtures/scale.category/autoskip-grid-y.png b/test/fixtures/scale.category/autoskip-grid-y.png
new file mode 100644 (file)
index 0000000..a88c838
Binary files /dev/null and b/test/fixtures/scale.category/autoskip-grid-y.png differ
index 87870bff50b5aede877a1c4b5d6696304cd7d9bc..7088421b1c52b0846a2ef853f7d245a13079fcc7 100644 (file)
Binary files a/test/fixtures/scale.time/offset-with-1-tick.png and b/test/fixtures/scale.time/offset-with-1-tick.png differ
index 5b02f0015982de259b99f2c4e80864973cd0a6e4..8de98fa33df960d659aa0b543357c7392b3b05d3 100644 (file)
Binary files a/test/fixtures/scale.time/offset-with-2-ticks.png and b/test/fixtures/scale.time/offset-with-2-ticks.png differ
index 659c0a1dae273d629a5a893e041a830e6ad2ddf9..c5d5e4e7cf649668fa861562806801427e76cf41 100644 (file)
Binary files a/test/fixtures/scale.timeseries/financial-daily.png and b/test/fixtures/scale.timeseries/financial-daily.png differ
index 67a3d4b93fd4ec5becd94335fa446df0472a2662..c37ee5fc167b9b64b8424322e91c8f01b4146bf9 100644 (file)
@@ -32,13 +32,13 @@ describe('Chart.layouts', function() {
 
     expect(chart.chartArea.bottom).toBeCloseToPixel(120);
     expect(chart.chartArea.left).toBeCloseToPixel(31);
-    expect(chart.chartArea.right).toBeCloseToPixel(250);
+    expect(chart.chartArea.right).toBeCloseToPixel(247);
     expect(chart.chartArea.top).toBeCloseToPixel(32);
 
     // Is xScale at the right spot
     expect(chart.scales.x.bottom).toBeCloseToPixel(150);
     expect(chart.scales.x.left).toBeCloseToPixel(31);
-    expect(chart.scales.x.right).toBeCloseToPixel(250);
+    expect(chart.scales.x.right).toBeCloseToPixel(247);
     expect(chart.scales.x.top).toBeCloseToPixel(120);
     expect(chart.scales.x.labelRotation).toBeCloseTo(0);
 
@@ -79,13 +79,13 @@ describe('Chart.layouts', function() {
     });
 
     expect(chart.chartArea.bottom).toBeCloseToPixel(139);
-    expect(chart.chartArea.left).toBeCloseToPixel(0);
+    expect(chart.chartArea.left).toBeCloseToPixel(3);
     expect(chart.chartArea.right).toBeCloseToPixel(218);
     expect(chart.chartArea.top).toBeCloseToPixel(62);
 
     // Is xScale at the right spot
     expect(chart.scales.x.bottom).toBeCloseToPixel(62);
-    expect(chart.scales.x.left).toBeCloseToPixel(0);
+    expect(chart.scales.x.left).toBeCloseToPixel(3);
     expect(chart.scales.x.right).toBeCloseToPixel(218);
     expect(chart.scales.x.top).toBeCloseToPixel(32);
     expect(chart.scales.x.labelRotation).toBeCloseTo(0);
@@ -160,13 +160,13 @@ describe('Chart.layouts', function() {
 
     expect(chart.chartArea.bottom).toBeCloseToPixel(110);
     expect(chart.chartArea.left).toBeCloseToPixel(70);
-    expect(chart.chartArea.right).toBeCloseToPixel(250);
+    expect(chart.chartArea.right).toBeCloseToPixel(247);
     expect(chart.chartArea.top).toBeCloseToPixel(32);
 
     // Is xScale at the right spot
     expect(chart.scales.x.bottom).toBeCloseToPixel(150);
     expect(chart.scales.x.left).toBeCloseToPixel(70);
-    expect(chart.scales.x.right).toBeCloseToPixel(250);
+    expect(chart.scales.x.right).toBeCloseToPixel(247);
     expect(chart.scales.x.top).toBeCloseToPixel(110);
     expect(chart.scales.x.labelRotation).toBeCloseTo(40, -1);
 
index 16b9699123e0f204676444bd76067e29db644ffa..e117259f11782216a7be9eaaa21ab35d4c8b3093 100644 (file)
@@ -54,7 +54,7 @@ describe('Platform.basic', function() {
 
       expect(chart.chartArea.bottom).toBeCloseToPixel(120);
       expect(chart.chartArea.left).toBeCloseToPixel(31);
-      expect(chart.chartArea.right).toBeCloseToPixel(250);
+      expect(chart.chartArea.right).toBeCloseToPixel(247);
       expect(chart.chartArea.top).toBeCloseToPixel(32);
     });
 
@@ -84,7 +84,7 @@ describe('Platform.basic', function() {
 
       expect(chart.chartArea.bottom).toBeCloseToPixel(150);
       expect(chart.chartArea.left).toBeCloseToPixel(31);
-      expect(chart.chartArea.right).toBeCloseToPixel(300);
+      expect(chart.chartArea.right).toBeCloseToPixel(297);
       expect(chart.chartArea.top).toBeCloseToPixel(32);
     });
   });
index fe3aad52e7439f3003b846f259fd61adefdba9a0..f7da130156b863bbb785139283e8250823112194 100644 (file)
@@ -473,7 +473,7 @@ describe('Category scale tests', function() {
     var xScale = chart.scales.x;
     expect(xScale.getPixelForValue(0)).toBeCloseToPixel(89);
     expect(xScale.getPixelForValue(3)).toBeCloseToPixel(451);
-    expect(xScale.getPixelForValue(4)).toBeCloseToPixel(572);
+    expect(xScale.getPixelForValue(4)).toBeCloseToPixel(570);
   });
 
   it('Should get the correct pixel for an object value in a horizontal bar chart', function() {
index e9fccdf8faf584c4f673f4f00ff29cb37f6b2501..9309ffd2b2a8b8b80ab6435aa243ba11d2027d65 100644 (file)
@@ -1114,7 +1114,7 @@ describe('Time scale tests', function() {
     });
     const scale = chart.scales.x;
     expect(scale.getPixelForDecimal(0)).toBeCloseToPixel(29);
-    expect(scale.getPixelForDecimal(1.0)).toBeCloseToPixel(512);
+    expect(scale.getPixelForDecimal(1.0)).toBeCloseToPixel(509);
   });
 
   ['data', 'labels'].forEach(function(source) {