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.
This commit is contained in:
Toni Dietze 2021-05-08 07:11:52 +02:00 committed by GitHub
parent a87175f119
commit c955ffad64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 15 deletions

View File

@ -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});

View File

@ -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);
canvas.height = height * pixelRatio;
canvas.width = width * pixelRatio;
chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
chart.height = deviceHeight / pixelRatio;
chart.width = deviceWidth / pixelRatio;
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;
}
/**