From: Toni Dietze Date: Sat, 8 May 2021 05:11:52 +0000 (+0200) Subject: Round canvas.style dimensions to avoid blurring (#9015) X-Git-Tag: v3.3.0~24 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=c955ffad64195fb17f214eae6be522287eadc586;p=thirdparty%2FChart.js.git Round canvas.style dimensions to avoid blurring (#9015) When canvas.height and canvas.width are set, the numbers are rounded to integers. The same rounding must be applied to canvas.style.height and canvas.style.width to avoid scaling of the canvas which would lead to blurring. Acknowledging that canvas.height and canvas.width are integers, the framebuffer is now only redrawn if those integer values change. --- diff --git a/src/core/core.controller.js b/src/core/core.controller.js index e1ce13e30..89071b55c 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -246,19 +246,14 @@ class Chart { const canvas = me.canvas; const aspectRatio = options.maintainAspectRatio && me.aspectRatio; const newSize = me.platform.getMaximumSize(canvas, width, height, aspectRatio); - - // detect devicePixelRation changes - const oldRatio = me.currentDevicePixelRatio; const newRatio = options.devicePixelRatio || me.platform.getDevicePixelRatio(); - if (me.width === newSize.width && me.height === newSize.height && oldRatio === newRatio) { - return; - } - me.width = newSize.width; me.height = newSize.height; me._aspectRatio = me.aspectRatio; - retinaScale(me, newRatio, true); + if (!retinaScale(me, newRatio, true)) { + return; + } me.notifyPlugins('resize', {size: newSize}); diff --git a/src/helpers/helpers.dom.js b/src/helpers/helpers.dom.js index db0fe3fc8..750af3907 100644 --- a/src/helpers/helpers.dom.js +++ b/src/helpers/helpers.dom.js @@ -150,21 +150,40 @@ export function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) { }; } +/** + * @param {import('../core/core.controller').default} chart + * @param {number} [forceRatio] + * @param {boolean} [forceStyle] + * @returns {boolean} True if the canvas context size or transformation has changed. + */ export function retinaScale(chart, forceRatio, forceStyle) { - const pixelRatio = chart.currentDevicePixelRatio = forceRatio || 1; - const {canvas, width, height} = chart; + const pixelRatio = forceRatio || 1; + const deviceHeight = Math.floor(chart.height * pixelRatio); + const deviceWidth = Math.floor(chart.width * pixelRatio); + + chart.height = deviceHeight / pixelRatio; + chart.width = deviceWidth / pixelRatio; - canvas.height = height * pixelRatio; - canvas.width = width * pixelRatio; - chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); + const canvas = chart.canvas; // If no style has been set on the canvas, the render size is used as display size, // making the chart visually bigger, so let's enforce it to the "correct" values. // See https://github.com/chartjs/Chart.js/issues/3575 if (canvas.style && (forceStyle || (!canvas.style.height && !canvas.style.width))) { - canvas.style.height = height + 'px'; - canvas.style.width = width + 'px'; + canvas.style.height = `${chart.height}px`; + canvas.style.width = `${chart.width}px`; + } + + if (chart.currentDevicePixelRatio !== pixelRatio + || canvas.height !== deviceHeight + || canvas.width !== deviceWidth) { + chart.currentDevicePixelRatio = pixelRatio; + canvas.height = deviceHeight; + canvas.width = deviceWidth; + chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); + return true; } + return false; } /**