]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Refactor radialLinear scale and renderText helper (#9276)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Fri, 18 Jun 2021 18:12:27 +0000 (21:12 +0300)
committerGitHub <noreply@github.com>
Fri, 18 Jun 2021 18:12:27 +0000 (14:12 -0400)
* Refactor radialLinear scale and renderText helper
* Undo the big move to make review possible

.codeclimate.yml
src/helpers/helpers.canvas.js
src/scales/scale.radialLinear.js
test/specs/helpers.canvas.tests.js
test/specs/plugin.title.tests.js

index b7f9662a17ee31450a864d4e6d3ada7aed080277..81a86a8b1ccb2b2f72793424ab35a8f35e9e9edc 100644 (file)
@@ -8,9 +8,12 @@ plugins:
   fixme:
     enabled: true
 checks:
+  argument-count:
+    config:
+      threshold: 5
   method-complexity:
     config:
-      threshold: 6
+      threshold: 7
 exclude_patterns:
   - "dist/"
   - "docs/"
index a23dce715a510ac71c564fdeaad4bca38313d626..faae4e5db348782afa144cb0c56945d4e20e1c7e 100644 (file)
@@ -310,28 +310,8 @@ export function renderText(ctx, text, x, y, font, opts = {}) {
   let i, line;
 
   ctx.save();
-
-  if (opts.translation) {
-    ctx.translate(opts.translation[0], opts.translation[1]);
-  }
-
-  if (!isNullOrUndef(opts.rotation)) {
-    ctx.rotate(opts.rotation);
-  }
-
   ctx.font = font.string;
-
-  if (opts.color) {
-    ctx.fillStyle = opts.color;
-  }
-
-  if (opts.textAlign) {
-    ctx.textAlign = opts.textAlign;
-  }
-
-  if (opts.textBaseline) {
-    ctx.textBaseline = opts.textBaseline;
-  }
+  setRenderOpts(ctx, opts);
 
   for (i = 0; i < lines.length; ++i) {
     line = lines[i];
@@ -349,35 +329,61 @@ export function renderText(ctx, text, x, y, font, opts = {}) {
     }
 
     ctx.fillText(line, x, y, opts.maxWidth);
+    decorateText(ctx, x, y, line, opts);
 
-    if (opts.strikethrough || opts.underline) {
-      /**
-                        * Now that IE11 support has been dropped, we can use more
-                        * of the TextMetrics object. The actual bounding boxes
-                        * are unflagged in Chrome, Firefox, Edge, and Safari so they
-                        * can be safely used.
-                        * See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility
-                        */
-      const metrics = ctx.measureText(line);
-      const left = x - metrics.actualBoundingBoxLeft;
-      const right = x + metrics.actualBoundingBoxRight;
-      const top = y - metrics.actualBoundingBoxAscent;
-      const bottom = y + metrics.actualBoundingBoxDescent;
-      const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
-
-      ctx.strokeStyle = ctx.fillStyle;
-      ctx.beginPath();
-      ctx.lineWidth = opts.decorationWidth || 2;
-      ctx.moveTo(left, yDecoration);
-      ctx.lineTo(right, yDecoration);
-      ctx.stroke();
-    }
     y += font.lineHeight;
   }
 
   ctx.restore();
 }
 
+function setRenderOpts(ctx, opts) {
+  if (opts.translation) {
+    ctx.translate(opts.translation[0], opts.translation[1]);
+  }
+
+  if (!isNullOrUndef(opts.rotation)) {
+    ctx.rotate(opts.rotation);
+  }
+
+  if (opts.color) {
+    ctx.fillStyle = opts.color;
+  }
+
+  if (opts.textAlign) {
+    ctx.textAlign = opts.textAlign;
+  }
+
+  if (opts.textBaseline) {
+    ctx.textBaseline = opts.textBaseline;
+  }
+}
+
+function decorateText(ctx, x, y, line, opts) {
+  if (opts.strikethrough || opts.underline) {
+    /**
+     * Now that IE11 support has been dropped, we can use more
+     * of the TextMetrics object. The actual bounding boxes
+     * are unflagged in Chrome, Firefox, Edge, and Safari so they
+     * can be safely used.
+     * See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility
+     */
+    const metrics = ctx.measureText(line);
+    const left = x - metrics.actualBoundingBoxLeft;
+    const right = x + metrics.actualBoundingBoxRight;
+    const top = y - metrics.actualBoundingBoxAscent;
+    const bottom = y + metrics.actualBoundingBoxDescent;
+    const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
+
+    ctx.strokeStyle = ctx.fillStyle;
+    ctx.beginPath();
+    ctx.lineWidth = opts.decorationWidth || 2;
+    ctx.moveTo(left, yDecoration);
+    ctx.lineTo(right, yDecoration);
+    ctx.stroke();
+  }
+}
+
 /**
  * Add a path of a rectangle with rounded corners to the current sub-path
  * @param {CanvasRenderingContext2D} ctx Context
index 7be1d220c35978f0290105b02a77ec04b8f23349..e1702975a04e6bde32deeee0c07355fa46c11cfb 100644 (file)
@@ -16,17 +16,11 @@ function getTickBackdropHeight(opts) {
   return 0;
 }
 
-function measureLabelSize(ctx, lineHeight, label) {
-  if (isArray(label)) {
-    return {
-      w: _longestText(ctx, ctx.font, label),
-      h: label.length * lineHeight
-    };
-  }
-
+function measureLabelSize(ctx, font, label) {
+  label = isArray(label) ? label : [label];
   return {
-    w: ctx.measureText(label).width,
-    h: lineHeight
+    w: _longestText(ctx, font.string, label),
+    h: label.length * font.lineHeight
   };
 }
 
@@ -89,22 +83,18 @@ function fitWithPointLabels(scale) {
     b: scale.height - scale.paddingTop
   };
   const furthestAngles = {};
-  let i, textSize, pointPosition;
-
   const labelSizes = [];
   const padding = [];
 
   const valueCount = scale.getLabels().length;
-  for (i = 0; i < valueCount; i++) {
+  for (let i = 0; i < valueCount; i++) {
     const opts = scale.options.pointLabels.setContext(scale.getContext(i));
     padding[i] = opts.padding;
-    pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i]);
+    const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i]);
     const plFont = toFont(opts.font);
-    scale.ctx.font = plFont.string;
-    textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale._pointLabels[i]);
+    const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]);
     labelSizes[i] = textSize;
 
-    // Add quarter circle to make degree 0 mean top of circle
     const angleRadians = scale.getIndexAngle(i);
     const angle = toDegrees(angleRadians);
     const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
@@ -133,50 +123,43 @@ function fitWithPointLabels(scale) {
 
   scale._setReductions(scale.drawingArea, furthestLimits, furthestAngles);
 
-  scale._pointLabelItems = [];
-
   // Now that text size is determined, compute the full positions
+  scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding);
+}
+
+function buildPointLabelItems(scale, labelSizes, padding) {
+  const items = [];
+  const valueCount = scale.getLabels().length;
   const opts = scale.options;
   const tickBackdropHeight = getTickBackdropHeight(opts);
   const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
 
-  for (i = 0; i < valueCount; i++) {
+  for (let i = 0; i < valueCount; i++) {
     // Extra pixels out for some label spacing
     const extra = (i === 0 ? tickBackdropHeight / 2 : 0);
     const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i]);
-
     const angle = toDegrees(scale.getIndexAngle(i));
     const size = labelSizes[i];
-    adjustPointPositionForLabelHeight(angle, size, pointLabelPosition);
-
+    const y = yForAngle(pointLabelPosition.y, size.h, angle);
     const textAlign = getTextAlignForAngle(angle);
-    let left;
+    const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign);
 
-    if (textAlign === 'left') {
-      left = pointLabelPosition.x;
-    } else if (textAlign === 'center') {
-      left = pointLabelPosition.x - (size.w / 2);
-    } else {
-      left = pointLabelPosition.x - size.w;
-    }
-
-    const right = left + size.w;
-
-    scale._pointLabelItems[i] = {
+    items.push({
       // Text position
       x: pointLabelPosition.x,
-      y: pointLabelPosition.y,
+      y,
 
       // Text rendering data
       textAlign,
 
       // Bounding box
       left,
-      top: pointLabelPosition.y,
-      right,
-      bottom: pointLabelPosition.y + size.h,
-    };
+      top: y,
+      right: left + size.w,
+      bottom: y + size.h
+    });
   }
+  return items;
 }
 
 function getTextAlignForAngle(angle) {
@@ -189,12 +172,22 @@ function getTextAlignForAngle(angle) {
   return 'right';
 }
 
-function adjustPointPositionForLabelHeight(angle, textSize, position) {
+function leftForTextAlign(x, w, align) {
+  if (align === 'right') {
+    x -= w;
+  } else if (align === 'center') {
+    x -= (w / 2);
+  }
+  return x;
+}
+
+function yForAngle(y, h, angle) {
   if (angle === 90 || angle === 270) {
-    position.y -= (textSize.h / 2);
+    y -= (h / 2);
   } else if (angle > 270 || angle < 90) {
-    position.y -= textSize.h;
+    y -= h;
   }
+  return y;
 }
 
 function drawPointLabels(scale, labelCount) {
@@ -543,6 +536,7 @@ export default class RadialLinearScale extends LinearScaleBase {
       offset = me.getDistanceFromCenterForValue(me.ticks[index].value);
 
       if (optsAtIndex.showLabelBackdrop) {
+        ctx.font = tickFont.string;
         width = ctx.measureText(tick.label).width;
         ctx.fillStyle = optsAtIndex.backdropColor;
 
index e451d7f13314c99778ba090294ef91e6bc036bd9..297ff03ef2e80a1931e4ec96f9492d49f0d15a86 100644 (file)
@@ -316,15 +316,15 @@ describe('Chart.helpers.canvas', function() {
       expect(context.getCalls()).toEqual([{
         name: 'save',
         args: [],
+      }, {
+        name: 'setFont',
+        args: ['12px arial'],
       }, {
         name: 'translate',
         args: [10, 20],
       }, {
         name: 'rotate',
         args: [90],
-      }, {
-        name: 'setFont',
-        args: ['12px arial'],
       }, {
         name: 'fillText',
         args: ['foo', 0, 0, undefined],
index 564c509278bf7987dd2d4c28dec4b1575f40dd9e..c08b9d4c9021a930a18a5bd9bf6211a4512826f7 100644 (file)
@@ -131,15 +131,15 @@ describe('Plugin.title', function() {
     expect(context.getCalls()).toEqual([{
       name: 'save',
       args: []
+    }, {
+      name: 'setFont',
+      args: ["normal bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"],
     }, {
       name: 'translate',
       args: [300, 67.2]
     }, {
       name: 'rotate',
       args: [0]
-    }, {
-      name: 'setFont',
-      args: ["normal bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"],
     }, {
       name: 'setFillStyle',
       args: ['#666']
@@ -192,15 +192,15 @@ describe('Plugin.title', function() {
     expect(context.getCalls()).toEqual([{
       name: 'save',
       args: []
+    }, {
+      name: 'setFont',
+      args: ["normal bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"],
     }, {
       name: 'translate',
       args: [117.2, 250]
     }, {
       name: 'rotate',
       args: [-0.5 * Math.PI]
-    }, {
-      name: 'setFont',
-      args: ["normal bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"],
     }, {
       name: 'setFillStyle',
       args: ['#666']
@@ -234,15 +234,15 @@ describe('Plugin.title', function() {
     expect(context.getCalls()).toEqual([{
       name: 'save',
       args: []
+    }, {
+      name: 'setFont',
+      args: ["normal bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"],
     }, {
       name: 'translate',
       args: [117.2, 250]
     }, {
       name: 'rotate',
       args: [0.5 * Math.PI]
-    }, {
-      name: 'setFont',
-      args: ["normal bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"],
     }, {
       name: 'setFillStyle',
       args: ['#666']