[BUG] Weird behavior with labels
wolflu05 opened this issue · 1 comments
wolflu05 commented
Hello,
I experienced a really weird behavior with my labels and don't know why. Im using this package in combination with chartjs-node-canvas
.
Description
The provided example code has three different for loop heads which work/don't work.
// run only for top=60 => perfect result
for (let top = 60; top <= 60; top += 5) {
// run a few values for top more => only a few bad
for (let top = 40; top < 70; top += 5) {
// run from 0 to 100 in 5er steps => everything bad
for (let top = 0; top < 100; top += 5) {
The weird part is, that it works if the for loop only runs one iteration. If it runs only a few iterations, it's sometimes correct and if its runs 20 iterations nothing is correct.
Screenshots
Look at the placement of the labels. In the correct one, the labels are placed ontop of the bars, in the wrong one they are wrapped somehow.
Steps to reproduce
- Create a folder and add the example code provided below
- Create a folder next to the
index.ts
file calleda
. - Run the file with
npx ts-node index.ts
and try all 3 for heads. (Remember to remove all files ina/
first to have no conflicts) - Compare the results
Example code
package.json
:
{
"name": "typescript-node",
"version": "1.0.0",
"scripts": {},
"dependencies": {
"@types/node": "14.14.29",
"ts-node": "9.1.1",
"typescript": "4.1.5",
"chart.js": "^3.9.1",
"chartjs-node-canvas": "^4.1.6",
"chartjs-plugin-datalabels": "^2.2.0"
}
}
import { ChartJSNodeCanvas } from "chartjs-node-canvas";
import { ChartConfiguration } from "chart.js";
import "chartjs-plugin-datalabels"; // to fix types
import { writeFileSync } from "fs";
const abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const srcData = new Array(100)
.fill(0)
.map((_, i) => ({ x: abc[i % (abc.length - 1)], y: i }));
(async () => {
// run only for top=60 => perfect result
// for (let top = 60; top <= 60; top += 5) {
// run a few values for top more => only a few bad
for (let top = 40; top < 70; top += 5) {
// run from 0 to 100 in 5er steps => everything bad
// for (let top = 0; top < 100; top += 5) {
const sliced = srcData.slice(0, top);
// import via require as it doesnt work otherwise
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ChartDataLabels = require("chartjs-plugin-datalabels");
const scale = 2;
const chartRenderer = new ChartJSNodeCanvas({
width: scale * Math.max(400, 40 * sliced.length),
height: scale * 400,
chartCallback: async (ChartJS) => {
ChartJS.register(ChartDataLabels);
},
});
const chart: ChartConfiguration = {
type: "bar",
data: {
labels: sliced.map((x) => x.x),
datasets: [
{
label: "ABC",
data: sliced.map((x) => x.y * 2),
backgroundColor: "#f43543",
datalabels: {
labels: {
relative: {
align: "end",
anchor: "end",
color: "#ffffff",
offset: scale * 12,
font: {
size: scale * 10,
weight: "bold",
},
formatter: (_value, ctx) =>
`${~~(sliced[ctx.dataIndex].y * 10 ** 3) / 10}%`,
},
},
},
},
],
},
};
const pictureBuffer = await chartRenderer.renderToBuffer(chart);
writeFileSync(`./a/chart${top}.png`, pictureBuffer);
}
})();
wolflu05 commented
This must be related to this library, because doing the require in the for loop with some tweeks to fresh require it, everything is working fine.
// https://github.com/hughsk/fresh-require
export const freshRequire: /*NodeJS.Require*/ (id: string) => any = (file) => {
const resolvedFile = require.resolve(file);
const temp = require.cache[resolvedFile];
delete require.cache[resolvedFile];
// eslint-disable-next-line @typescript-eslint/no-var-requires
const modified = require(resolvedFile);
require.cache[resolvedFile] = temp;
return modified;
};
// run only for top=60 => perfect result
//for (let top = 45; top <= 45; top += 5) {
// run a few values for top more => only a few bad
for (let top = 40; top < 70; top += 5) {
// run from 0 to 100 in 5er steps => everything bad
// for (let top = 0; top < 100; top += 5) {
// import via require as it doesnt work otherwise
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ChartDataLabels = freshRequire('chartjs-plugin-datalabels');
[...]