From 795e957196edb6cd4d1cc0c8c4f1a27d83e60d60 Mon Sep 17 00:00:00 2001 From: minjibak Date: Thu, 4 Sep 2025 15:01:49 +0900 Subject: [PATCH 1/3] fix: Unwanted white gap between bar border and bar fill (chartjs#12094) --- src/elements/element.bar.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/elements/element.bar.js b/src/elements/element.bar.js index 6b0cfc70bc1..40ad5cdfe99 100644 --- a/src/elements/element.bar.js +++ b/src/elements/element.bar.js @@ -124,18 +124,29 @@ function addNormalRectPath(ctx, rect) { ctx.rect(rect.x, rect.y, rect.w, rect.h); } -function inflateRect(rect, amount, refRect = {}) { +function inflateRect(rect, amount, refRect = {}, snap = false) { const x = rect.x !== refRect.x ? -amount : 0; const y = rect.y !== refRect.y ? -amount : 0; const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x; const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y; - return { + + const result = { x: rect.x + x, y: rect.y + y, w: rect.w + w, h: rect.h + h, radius: rect.radius }; + + if (snap) { + const dpr = (typeof window !== 'undefined' && window.devicePixelRatio) ? window.devicePixelRatio : 1; + result.x = Math.round(result.x * dpr) / dpr; + result.y = Math.round(result.y * dpr) / dpr; + result.w = Math.round(result.w * dpr) / dpr; + result.h = Math.round(result.h * dpr) / dpr; + } + + return result; } export default class BarElement extends Element { @@ -185,15 +196,15 @@ export default class BarElement extends Element { if (outer.w !== inner.w || outer.h !== inner.h) { ctx.beginPath(); - addRectPath(ctx, inflateRect(outer, inflateAmount, inner)); + addRectPath(ctx, inflateRect(outer, inflateAmount, inner, true)); ctx.clip(); - addRectPath(ctx, inflateRect(inner, -inflateAmount, outer)); + addRectPath(ctx, inflateRect(inner, -inflateAmount, outer, true)); ctx.fillStyle = borderColor; ctx.fill('evenodd'); } ctx.beginPath(); - addRectPath(ctx, inflateRect(inner, inflateAmount)); + addRectPath(ctx, inflateRect(inner, inflateAmount, undefined, true)); ctx.fillStyle = backgroundColor; ctx.fill(); From c00bb8383bfef24474ba450da827620d576bbc64 Mon Sep 17 00:00:00 2001 From: minjibak Date: Wed, 17 Sep 2025 13:58:38 +0900 Subject: [PATCH 2/3] fix: Unwanted white gap between bar border and bar fill (chartjs#12094) - Added tests in element.bar.tests.js to verify pixel snapping fix --- src/elements/element.bar.js | 2 +- test/specs/element.bar.tests.js | 66 +++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/elements/element.bar.js b/src/elements/element.bar.js index 40ad5cdfe99..4435046ffc3 100644 --- a/src/elements/element.bar.js +++ b/src/elements/element.bar.js @@ -145,7 +145,7 @@ function inflateRect(rect, amount, refRect = {}, snap = false) { result.w = Math.round(result.w * dpr) / dpr; result.h = Math.round(result.h * dpr) / dpr; } - + return result; } diff --git a/test/specs/element.bar.tests.js b/test/specs/element.bar.tests.js index 128bdddc12e..f00d30c86e8 100644 --- a/test/specs/element.bar.tests.js +++ b/test/specs/element.bar.tests.js @@ -64,4 +64,70 @@ describe('Bar element tests', function() { expect(bar.getCenterPoint()).toEqual({x: 10, y: 7.5}); }); + + describe('unwanted white gap fix', () => { + it('should not produce white gap when borderWidth > 0', () => { + var chart = window.acquireChart({ + type: 'bar', + data: { + datasets: [{ + data: [50], + backgroundColor: 'pink', + borderColor: 'pink', + borderWidth: 15, + borderSkipped: false, + }], + labels: [] + }, + options: { + elements: { + bar: { + inflateAmount: 0, + } + }, + } + }); + + const meta = chart.getDatasetMeta(0); + const bar = meta.data[0]; + const props = bar.getProps(['width', 'height', 'borderWidth'], true); + + expect(props.borderWidth).toBe(15); + expect(props.width).toBeGreaterThan(props.borderWidth * 2); + expect(props.height).toBeGreaterThan(props.borderWidth * 2); + }); + + it('should handle borderRadius without creating a gap', () => { + var chart = window.acquireChart({ + type: 'bar', + data: { + datasets: [{ + data: [30], + backgroundColor: 'black', + borderColor: 'black', + borderWidth: 10, + borderRadius: 8, + borderSkipped: false, + }], + labels: [] + }, + options: { + elements: { + bar: { + inflateAmount: 0, + } + }, + } + }); + + const meta = chart.getDatasetMeta(0); + const bar = meta.data[0]; + const props = bar.getProps(['width', 'height', 'borderWidth', 'borderRadius'], true); + + expect(props.borderWidth).toBe(10); + expect(props.borderRadius).toBe(8); + expect(props.width).toBeGreaterThan(props.borderRadius + props.borderWidth); + expect(props.height).toBeGreaterThan(props.borderRadius + props.borderWidth); + }); + }); }); From 2d1ea458957697dce0eae643a88bc26685610d3a Mon Sep 17 00:00:00 2001 From: minjibak Date: Fri, 19 Sep 2025 14:17:41 +0900 Subject: [PATCH 3/3] fix: Unwanted white gap between bar border and bar fill (chartjs#12094) test: fix Bar element tests to use options.borderWidth (chartjs#12094) --- test/specs/element.bar.tests.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/specs/element.bar.tests.js b/test/specs/element.bar.tests.js index f00d30c86e8..08e43008f84 100644 --- a/test/specs/element.bar.tests.js +++ b/test/specs/element.bar.tests.js @@ -90,11 +90,11 @@ describe('Bar element tests', function() { const meta = chart.getDatasetMeta(0); const bar = meta.data[0]; - const props = bar.getProps(['width', 'height', 'borderWidth'], true); + const props = bar.getProps(['width', 'height'], true); - expect(props.borderWidth).toBe(15); - expect(props.width).toBeGreaterThan(props.borderWidth * 2); - expect(props.height).toBeGreaterThan(props.borderWidth * 2); + expect(bar.options.borderWidth).toBe(15); + expect(props.width).toBeGreaterThan(bar.options.borderWidth * 2); + expect(props.height).toBeGreaterThan(bar.options.borderWidth * 2); }); it('should handle borderRadius without creating a gap', () => { @@ -122,12 +122,12 @@ describe('Bar element tests', function() { const meta = chart.getDatasetMeta(0); const bar = meta.data[0]; - const props = bar.getProps(['width', 'height', 'borderWidth', 'borderRadius'], true); + const props = bar.getProps(['width', 'height'], true); - expect(props.borderWidth).toBe(10); - expect(props.borderRadius).toBe(8); - expect(props.width).toBeGreaterThan(props.borderRadius + props.borderWidth); - expect(props.height).toBeGreaterThan(props.borderRadius + props.borderWidth); + expect(bar.options.borderWidth).toBe(10); + expect(bar.options.borderRadius).toBe(8); + expect(props.width).toBeGreaterThan(bar.options.borderRadius + bar.options.borderWidth); + expect(props.height).toBeGreaterThan(bar.options.borderRadius + bar.options.borderWidth); }); }); });