diff --git a/src/elements/element.bar.js b/src/elements/element.bar.js index 6b0cfc70bc1..4435046ffc3 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(); diff --git a/test/specs/element.bar.tests.js b/test/specs/element.bar.tests.js index 128bdddc12e..08e43008f84 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'], true); + + 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', () => { + 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'], true); + + 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); + }); + }); });