mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Resize on devicePixelRatio changes (#7717)
* Resize on devicePixelRatio changes
This commit is contained in:
parent
886a465c18
commit
ae214d9fbd
4
package-lock.json
generated
4
package-lock.json
generated
@ -2399,7 +2399,7 @@
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@ -2418,7 +2418,7 @@
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
}
|
||||
|
||||
@ -176,3 +176,42 @@ export function retinaScale(chart, forceRatio) {
|
||||
canvas.style.width = width + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects support for options object argument in addEventListener.
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
|
||||
* @private
|
||||
*/
|
||||
export const supportsEventListenerOptions = (function() {
|
||||
let passiveSupported = false;
|
||||
try {
|
||||
const options = {
|
||||
get passive() { // This function will be called when the browser attempts to access the passive property.
|
||||
passiveSupported = true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// @ts-ignore
|
||||
window.addEventListener('test', null, options);
|
||||
// @ts-ignore
|
||||
window.removeEventListener('test', null, options);
|
||||
} catch (e) {
|
||||
// continue regardless of error
|
||||
}
|
||||
return passiveSupported;
|
||||
}());
|
||||
|
||||
/**
|
||||
* The "used" size is the final value of a dimension property after all calculations have
|
||||
* been performed. This method uses the computed style of `element` but returns undefined
|
||||
* if the computed style is not expressed in pixels. That can happen in some cases where
|
||||
* `element` has a size relative to its parent and this last one is not yet displayed,
|
||||
* for example because of `display: none` on a parent node.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
|
||||
* @returns {number=} Size in pixels or undefined if unknown.
|
||||
*/
|
||||
export function readUsedSize(element, property) {
|
||||
const value = getStyle(element, property);
|
||||
const matches = value && value.match(/^(\d+)(\.\d+)?px$/);
|
||||
return matches ? +matches[1] : undefined;
|
||||
}
|
||||
|
||||
@ -13,3 +13,26 @@ export const requestAnimFrame = (function() {
|
||||
}
|
||||
return window.requestAnimationFrame;
|
||||
}());
|
||||
|
||||
/**
|
||||
* Throttles calling `fn` once per animation frame
|
||||
* Latest argments are used on the actual call
|
||||
* @param {function} fn
|
||||
* @param {*} thisArg
|
||||
*/
|
||||
export function throttled(fn, thisArg) {
|
||||
let ticking = false;
|
||||
let args = [];
|
||||
|
||||
return function(...rest) {
|
||||
args = Array.prototype.slice.call(rest);
|
||||
|
||||
if (!ticking) {
|
||||
ticking = true;
|
||||
requestAnimFrame.call(window, () => {
|
||||
ticking = false;
|
||||
fn.apply(thisArg, args);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import BasePlatform from './platform.base';
|
||||
import {_getParentNode, getStyle, getRelativePosition} from '../helpers/helpers.dom';
|
||||
import {requestAnimFrame} from '../helpers/helpers.extras';
|
||||
import {_getParentNode, getRelativePosition, supportsEventListenerOptions, readUsedSize} from '../helpers/helpers.dom';
|
||||
import {throttled} from '../helpers/helpers.extras';
|
||||
import {isNullOrUndef} from '../helpers/helpers.core';
|
||||
|
||||
/**
|
||||
@ -30,21 +30,7 @@ const EVENT_TYPES = {
|
||||
pointerout: 'mouseout'
|
||||
};
|
||||
|
||||
/**
|
||||
* The "used" size is the final value of a dimension property after all calculations have
|
||||
* been performed. This method uses the computed style of `element` but returns undefined
|
||||
* if the computed style is not expressed in pixels. That can happen in some cases where
|
||||
* `element` has a size relative to its parent and this last one is not yet displayed,
|
||||
* for example because of `display: none` on a parent node.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
|
||||
* @returns {number=} Size in pixels or undefined if unknown.
|
||||
*/
|
||||
function readUsedSize(element, property) {
|
||||
const value = getStyle(element, property);
|
||||
const matches = value && value.match(/^(\d+)(\.\d+)?px$/);
|
||||
return matches ? +matches[1] : undefined;
|
||||
}
|
||||
|
||||
const isNullOrEmpty = value => value === null || value === '';
|
||||
/**
|
||||
* Initializes the canvas style and render size without modifying the canvas display size,
|
||||
* since responsiveness is handled by the controller.resize() method. The config is used
|
||||
@ -80,14 +66,14 @@ function initCanvas(canvas, config) {
|
||||
// Include possible borders in the size
|
||||
style.boxSizing = style.boxSizing || 'border-box';
|
||||
|
||||
if (renderWidth === null || renderWidth === '') {
|
||||
if (isNullOrEmpty(renderWidth)) {
|
||||
const displayWidth = readUsedSize(canvas, 'width');
|
||||
if (displayWidth !== undefined) {
|
||||
canvas.width = displayWidth;
|
||||
}
|
||||
}
|
||||
|
||||
if (renderHeight === null || renderHeight === '') {
|
||||
if (isNullOrEmpty(renderHeight)) {
|
||||
if (canvas.style.height === '') {
|
||||
// If no explicit render height and style height, let's apply the aspect ratio,
|
||||
// which one can be specified by the user but also by charts as default option
|
||||
@ -104,30 +90,6 @@ function initCanvas(canvas, config) {
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects support for options object argument in addEventListener.
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
|
||||
* @private
|
||||
*/
|
||||
const supportsEventListenerOptions = (function() {
|
||||
let passiveSupported = false;
|
||||
try {
|
||||
const options = {
|
||||
get passive() { // This function will be called when the browser attempts to access the passive property.
|
||||
passiveSupported = true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// @ts-ignore
|
||||
window.addEventListener('test', null, options);
|
||||
// @ts-ignore
|
||||
window.removeEventListener('test', null, options);
|
||||
} catch (e) {
|
||||
// continue regardless of error
|
||||
}
|
||||
return passiveSupported;
|
||||
}());
|
||||
|
||||
// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
|
||||
// https://github.com/chartjs/Chart.js/issues/4287
|
||||
const eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
|
||||
@ -136,8 +98,8 @@ function addListener(node, type, listener) {
|
||||
node.addEventListener(type, listener, eventListenerOptions);
|
||||
}
|
||||
|
||||
function removeListener(node, type, listener) {
|
||||
node.removeEventListener(type, listener, eventListenerOptions);
|
||||
function removeListener(chart, type, listener) {
|
||||
chart.canvas.removeEventListener(type, listener, eventListenerOptions);
|
||||
}
|
||||
|
||||
function createEvent(type, chart, x, y, nativeEvent) {
|
||||
@ -156,23 +118,6 @@ function fromNativeEvent(event, chart) {
|
||||
return createEvent(type, chart, pos.x, pos.y, event);
|
||||
}
|
||||
|
||||
function throttled(fn, thisArg) {
|
||||
let ticking = false;
|
||||
let args = [];
|
||||
|
||||
return function(...rest) {
|
||||
args = Array.prototype.slice.call(rest);
|
||||
|
||||
if (!ticking) {
|
||||
ticking = true;
|
||||
requestAnimFrame.call(window, () => {
|
||||
ticking = false;
|
||||
fn.apply(thisArg, args);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createAttachObserver(chart, type, listener) {
|
||||
const canvas = chart.canvas;
|
||||
const container = canvas && _getParentNode(canvas);
|
||||
@ -212,6 +157,36 @@ function createDetachObserver(chart, type, listener) {
|
||||
return observer;
|
||||
}
|
||||
|
||||
const drpListeningCharts = new Map();
|
||||
let oldDevicePixelRatio = 0;
|
||||
|
||||
function onWindowResize() {
|
||||
const dpr = window.devicePixelRatio;
|
||||
if (dpr === oldDevicePixelRatio) {
|
||||
return;
|
||||
}
|
||||
oldDevicePixelRatio = dpr;
|
||||
drpListeningCharts.forEach((resize, chart) => {
|
||||
if (chart.currentDevicePixelRatio !== dpr) {
|
||||
resize();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function listenDevicePixelRatioChanges(chart, resize) {
|
||||
if (!drpListeningCharts.size) {
|
||||
window.addEventListener('resize', onWindowResize);
|
||||
}
|
||||
drpListeningCharts.set(chart, resize);
|
||||
}
|
||||
|
||||
function unlistenDevicePixelRatioChanges(chart) {
|
||||
drpListeningCharts.delete(chart);
|
||||
if (!drpListeningCharts.size) {
|
||||
window.removeEventListener('resize', onWindowResize);
|
||||
}
|
||||
}
|
||||
|
||||
function createResizeObserver(chart, type, listener) {
|
||||
const canvas = chart.canvas;
|
||||
const container = canvas && _getParentNode(canvas);
|
||||
@ -247,13 +222,18 @@ function createResizeObserver(chart, type, listener) {
|
||||
resize(width, height);
|
||||
});
|
||||
observer.observe(container);
|
||||
listenDevicePixelRatioChanges(chart, resize);
|
||||
|
||||
return observer;
|
||||
}
|
||||
|
||||
function releaseObserver(canvas, type, observer) {
|
||||
function releaseObserver(chart, type, observer) {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
}
|
||||
if (type === 'resize') {
|
||||
unlistenDevicePixelRatioChanges(chart);
|
||||
}
|
||||
}
|
||||
|
||||
function createProxyAndListen(chart, type, listener) {
|
||||
@ -367,7 +347,6 @@ export default class DomPlatform extends BasePlatform {
|
||||
* @param {string} type
|
||||
*/
|
||||
removeEventListener(chart, type) {
|
||||
const canvas = chart.canvas;
|
||||
const proxies = chart.$proxies || (chart.$proxies = {});
|
||||
const proxy = proxies[type];
|
||||
|
||||
@ -381,7 +360,7 @@ export default class DomPlatform extends BasePlatform {
|
||||
resize: releaseObserver
|
||||
};
|
||||
const handler = handlers[type] || removeListener;
|
||||
handler(canvas, type, proxy);
|
||||
handler(chart, type, proxy);
|
||||
proxies[type] = undefined;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user