]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Fix layout refit logic (#8567)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Fri, 5 Mar 2021 00:48:26 +0000 (02:48 +0200)
committerGitHub <noreply@github.com>
Fri, 5 Mar 2021 00:48:26 +0000 (19:48 -0500)
* Fix layout refit logic

* CC

* Update fixture

src/core/core.layouts.js
src/core/core.scale.js
test/fixtures/core.layouts/long-labels.png
test/fixtures/core.layouts/refit-vertical-boxes.js [new file with mode: 0644]
test/fixtures/core.layouts/refit-vertical-boxes.png [new file with mode: 0644]
test/fixtures/scale.time/invalid-data.png
test/specs/core.controller.tests.js

index 7868eb676216ecdf1f95722049ad8826841dc701..eba4250d789c3f88ff2177c70e3b66d0e538c3be 100644 (file)
@@ -60,6 +60,7 @@ function setLayoutDims(layouts, params) {
 
 function buildLayoutBoxes(boxes) {
   const layoutBoxes = wrapBoxes(boxes);
+  const fullSize = sortByWeight(layoutBoxes.filter(wrap => wrap.box.fullSize), true);
   const left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);
   const right = sortByWeight(filterByPosition(layoutBoxes, 'right'));
   const top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);
@@ -68,6 +69,7 @@ function buildLayoutBoxes(boxes) {
   const centerVertical = filterDynamicPositionByAxis(layoutBoxes, 'y');
 
   return {
+    fullSize,
     leftAndTop: left.concat(top),
     rightAndBottom: right.concat(centerVertical).concat(bottom).concat(centerHorizontal),
     chartArea: filterByPosition(layoutBoxes, 'chartArea'),
@@ -80,13 +82,20 @@ function getCombinedMax(maxPadding, chartArea, a, b) {
   return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
 }
 
+function updateMaxPadding(maxPadding, boxPadding) {
+  maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
+  maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
+  maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
+  maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
+}
+
 function updateDims(chartArea, params, layout) {
   const box = layout.box;
   const maxPadding = chartArea.maxPadding;
 
   if (isObject(layout.pos)) {
     // dynamically placed boxes are not considered
-    return;
+    return {same: false, other: false};
   }
   if (layout.size) {
     // this layout was already counted for, lets first reduce old size
@@ -96,23 +105,23 @@ function updateDims(chartArea, params, layout) {
   chartArea[layout.pos] += layout.size;
 
   if (box.getPadding) {
-    const boxPadding = box.getPadding();
-    maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
-    maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
-    maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
-    maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
+    updateMaxPadding(maxPadding, box.getPadding());
   }
 
   const newWidth = Math.max(0, params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'));
   const newHeight = Math.max(0, params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'));
 
-  if (newWidth !== chartArea.w || newHeight !== chartArea.h) {
+  const widthChanged = newWidth !== chartArea.w;
+  const heightChanged = newHeight !== chartArea.h;
+  if (widthChanged || heightChanged) {
     chartArea.w = newWidth;
     chartArea.h = newHeight;
-
-    // return true if chart area changed in layout's direction
-    return layout.horizontal ? newWidth !== chartArea.w : newHeight !== chartArea.h;
   }
+
+  // return booleans on the changes per direction
+  return layout.horizontal
+    ? {same: widthChanged, other: heightChanged}
+    : {same: heightChanged, other: widthChanged};
 }
 
 function handleMaxPadding(chartArea) {
@@ -158,13 +167,15 @@ function fitBoxes(boxes, chartArea, params) {
       layout.height || chartArea.h,
       getMargins(layout.horizontal, chartArea)
     );
-    if (updateDims(chartArea, params, layout)) {
+    const {same, other} = updateDims(chartArea, params, layout);
+    if (same && refitBoxes.length) {
+      // Dimensions changed and there were non full width boxes before this
+      // -> we have to refit those
+      refit = true;
+    }
+    if (other) {
+      // Chart area changed in the opposite direction
       changed = true;
-      if (refitBoxes.length) {
-        // Dimensions changed and there were non full width boxes before this
-        // -> we have to refit those
-        refit = true;
-      }
     }
     if (!box.fullSize) { // fullSize boxes don't need to be re-fitted in any case
       refitBoxes.push(layout);
@@ -365,7 +376,10 @@ export default {
 
     setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);
 
-    // First fit vertical boxes
+    // First fit the fullSize boxes, to reduce probability of re-fitting.
+    fitBoxes(boxes.fullSize, chartArea, params);
+
+    // Then fit vertical boxes
     fitBoxes(verticalBoxes, chartArea, params);
 
     // Then fit horizontal boxes
index 7a2674d09241765f7f46630ff6ae0572754d567e..4b3dd09790be905a5df4e314ba10de8f32965b22 100644 (file)
@@ -2,7 +2,7 @@ import defaults from './core.defaults';
 import Element from './core.element';
 import {_alignPixel, _measureText, renderText, clipArea, unclipArea} from '../helpers/helpers.canvas';
 import {callback as call, each, finiteOrDefault, isArray, isFinite, isNullOrUndef, isObject, valueOrDefault} from '../helpers/helpers.core';
-import {_factorize, toDegrees, toRadians, _int16Range, HALF_PI} from '../helpers/helpers.math';
+import {_factorize, toDegrees, toRadians, _int16Range, HALF_PI, _limitValue} from '../helpers/helpers.math';
 import {toFont, toPadding} from '../helpers/helpers.options';
 import Ticks from './core.ticks';
 
@@ -734,7 +734,7 @@ export default class Scale extends Element {
 
     // Estimate the width of each grid based on the canvas width, the maximum
     // label width and the number of tick intervals
-    const maxWidth = Math.min(me.maxWidth, me.chart.width - maxLabelWidth);
+    const maxWidth = _limitValue(me.chart.width - maxLabelWidth, 0, me.maxWidth);
     tickWidth = options.offset ? me.maxWidth / numTicks : maxWidth / (numTicks - 1);
 
     // Allow 3 pixels x2 padding either side for label readability
index ed33676753e1278d20fa5c518fd0b40f71245e3b..c12a70a8711a21b79869ca2dcabe5911326340f7 100644 (file)
Binary files a/test/fixtures/core.layouts/long-labels.png and b/test/fixtures/core.layouts/long-labels.png differ
diff --git a/test/fixtures/core.layouts/refit-vertical-boxes.js b/test/fixtures/core.layouts/refit-vertical-boxes.js
new file mode 100644 (file)
index 0000000..b49d788
--- /dev/null
@@ -0,0 +1,52 @@
+module.exports = {
+  config: {
+    type: 'line',
+    data: {
+      labels: [
+        'Aaron',
+        'Adam',
+        'Albert',
+        'Alex',
+        'Allan',
+        'Aman',
+        'Anthony',
+        'Autoenrolment',
+        'Avril',
+        'Bernard'
+      ],
+      datasets: [{
+        backgroundColor: 'rgba(252,233,79,0.5)',
+        borderColor: 'rgba(252,233,79,1)',
+        borderWidth: 1,
+        data: [101,
+          185,
+          24,
+          311,
+          17,
+          21,
+          462,
+          340,
+          140,
+          24
+        ]
+      }]
+    },
+    options: {
+      maintainAspectRatio: false,
+      plugins: {
+        legend: true,
+        title: {
+          display: true,
+          text: 'test'
+        }
+      }
+    }
+  },
+  options: {
+    spriteText: true,
+    canvas: {
+      height: 185,
+      width: 185
+    }
+  }
+};
diff --git a/test/fixtures/core.layouts/refit-vertical-boxes.png b/test/fixtures/core.layouts/refit-vertical-boxes.png
new file mode 100644 (file)
index 0000000..f45a406
Binary files /dev/null and b/test/fixtures/core.layouts/refit-vertical-boxes.png differ
index 1cdd9b95d9512bb71206a4171ed28c6a728f3b45..2257064a27162c6ef086e44f2907673b0492c3e4 100644 (file)
Binary files a/test/fixtures/scale.time/invalid-data.png and b/test/fixtures/scale.time/invalid-data.png differ
index 2fb13eb4bae2040339d7d5416a6b5ca2a7943d48..c0f1b2ba8502a34555d86c947ddf85d8843c3459 100644 (file)
@@ -1430,14 +1430,16 @@ describe('Chart', function() {
       update: [
         'beforeUpdate',
         'beforeLayout',
-        'beforeDataLimits',
+        'beforeDataLimits', // y-axis fit
         'afterDataLimits',
         'beforeBuildTicks',
         'afterBuildTicks',
-        'beforeDataLimits',
+        'beforeDataLimits', // x-axis fit
         'afterDataLimits',
         'beforeBuildTicks',
         'afterBuildTicks',
+        'beforeBuildTicks', // y-axis re-fit
+        'afterBuildTicks',
         'afterLayout',
         'beforeDatasetsUpdate',
         'beforeDatasetUpdate',