chartjs-plugin-datalabels icon indicating copy to clipboard operation
chartjs-plugin-datalabels copied to clipboard

Arc labels around "pie" or "doughnut" charts

Open Jonibigoud opened this issue 6 years ago • 5 comments

Thank you for the last release enabling multiple labels. It works nicely. Nevertheless, it would be great to be able to arc labels positionned outside "pie" or "doughnut" charts. This option (arc: boolean) exists on a similar library: https://emn178.github.io/chartjs-plugin-labels/samples/demo/

This is the "renderArcLabel" function (see line 141 of js file) that allows labels to arc.

Do you think such a function could be easily added to this plugin? I would be happy to help more but i'm afraid i'm not skilled enough for that.

Jonibigoud avatar Sep 26 '19 09:09 Jonibigoud

Thanks for this nice plugin! I would also appreciate this functionality

rada-it avatar Mar 10 '21 18:03 rada-it

+1. Looking for label arcing as well. Would be nice to have the option to arc either within the chart or outside the chart.

lnjustin avatar Feb 27 '23 14:02 lnjustin

Is this future going to be added one day? Someone found a solution to this?

Tolfx avatar Jun 06 '23 19:06 Tolfx

Similar to #93

On Chart.js v4.4 with plugin chartjs/chartjs-plugin-datalabels v2.2

image

On Chart.js v2.9 with plugin emn178/chartjs-plugin-labels v1.1

image

noraj avatar Mar 19 '24 01:03 noraj

I solved this issue by adding a variable checking if we should fix is upside down

-          for (let i = 0; i < line.length; i++) {
-            const char = line.charAt(i);
-            mertrics = ctx.measureText(char);
-            ctx.save();
-            ctx.translate(0, -1 * radius);
-            ctx.fillText(char, 0, y);
-            ctx.restore();
-            ctx.rotate(mertrics.width / radius);
-          }
+          for (let i = 0; i < reversedLine.length; i++) {
+            const char = reversedLine.charAt(i);
+           const metrics = ctx.measureText(char);
+            const charWidth = metrics.width;
+
+            ctx.save();
+            // Translate to the correct radius and rotate the canvas to the start angle
+            ctx.translate(0, -1 * radius);
+            ctx.save();
+            ctx.translate(charWidth / 8, 0);
+            ctx.rotate(Math.PI); // This rotation flips the character
+
+            // The y position is adjusted by the offset and the height of the characters
+            ctx.fillText(char, 0, y);
+
+            // Rotate the context back for the next character
+            ctx.restore();
+            ctx.restore();
+            // Adjust the rotation for each character
+            ctx.rotate(charWidth / radius);
+          }

Here's the whole code

      if (shouldReverseArc) {
        ctx.rotate(renderInfo.startAngle);
        ctx.textBaseline = "middle";
        ctx.textAlign = "right";
        const lines = label.split("\n");
        let max = 0;
        const widths = [];
        let offset = 0;
        if (this.options.position === "border") {
          offset = ((lines.length - 1) * this.getFontSize(label)) / 2;
        }
        let mertrics;
        for (let j = 0; j < lines.length; ++j) {
          mertrics = ctx.measureText(lines[j]);
          if (mertrics.width > max) {
            max = mertrics.width;
          }
          widths.push(mertrics.width);
        }

        for (let j = 0; j < lines.length; ++j) {
          const line = lines[j];
          // Reverse the characters in the line to draw in reverse
          const reversedLine = line.split("").reverse().join("");
          const y =
            (lines.length - 1 - j) *
              (this.getFontSize(label) + this.getFontSize(label) * 0.8) +
            offset;

          const padding = (max - widths[j]) / 2;
          ctx.rotate(padding / radius);

          for (let i = 0; i < reversedLine.length; i++) {
            const char = reversedLine.charAt(i);
            const metrics = ctx.measureText(char);
            const charWidth = metrics.width;

            ctx.save();
            // Translate to the correct radius and rotate the canvas to the start angle
            ctx.translate(0, -1 * radius);
            ctx.save();
            ctx.translate(charWidth / 8, 0);
            ctx.rotate(Math.PI); // This rotation flips the character

            // The y position is adjusted by the offset and the height of the characters
            ctx.fillText(char, 0, y);

            // Rotate the context back for the next character
            ctx.restore();
            ctx.restore();
            // Adjust the rotation for each character
            ctx.rotate(charWidth / radius);
          }
        }
      }

This helped me with fixing the issue. Change depending usage I guess.

Tolfx avatar Mar 22 '24 11:03 Tolfx