@thewhitehaven04/chartjs-plugin-zoom 2.2.9 → 2.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/dist/@thewhitehaven04/chartjs-plugin-zoom.esm.js +0 -1172
- package/dist/@thewhitehaven04/chartjs-plugin-zoom.esm.js.map +0 -1
- package/dist/@thewhitehaven04/chartjs-plugin-zoom.js +0 -1179
- package/dist/@thewhitehaven04/chartjs-plugin-zoom.js.map +0 -1
- package/dist/@thewhitehaven04/chartjs-plugin-zoom.min.js +0 -8
- package/dist/@thewhitehaven04/chartjs-plugin-zoom.min.js.map +0 -1
- package/dist/src/core.d.ts +0 -24
- package/dist/src/core.d.ts.map +0 -1
- package/dist/src/defaults.d.ts +0 -3
- package/dist/src/defaults.d.ts.map +0 -1
- package/dist/src/hammer.d.ts +0 -6
- package/dist/src/hammer.d.ts.map +0 -1
- package/dist/src/handlers.d.ts +0 -24
- package/dist/src/handlers.d.ts.map +0 -1
- package/dist/src/index.d.ts +0 -31
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.umd.d.ts +0 -3
- package/dist/src/index.umd.d.ts.map +0 -1
- package/dist/src/options.d.ts +0 -196
- package/dist/src/options.d.ts.map +0 -1
- package/dist/src/plugin.d.ts +0 -25
- package/dist/src/plugin.d.ts.map +0 -1
- package/dist/src/scale.types.d.ts +0 -12
- package/dist/src/scale.types.d.ts.map +0 -1
- package/dist/src/scale.types.test.d.ts +0 -2
- package/dist/src/scale.types.test.d.ts.map +0 -1
- package/dist/src/state.d.ts +0 -53
- package/dist/src/state.d.ts.map +0 -1
- package/dist/src/types.d.ts +0 -11
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/utils.d.ts +0 -14
- package/dist/src/utils.d.ts.map +0 -1
- package/dist/src/utils.test.d.ts +0 -2
- package/dist/src/utils.test.d.ts.map +0 -1
|
@@ -1,1172 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* @thewhitehaven04/chartjs-plugin-zoom v2.2.7
|
|
3
|
-
* https://www.chartjs.org/chartjs-plugin-zoom/2.2.7/
|
|
4
|
-
* (c) 2016-2026 chartjs-plugin-zoom Contributors
|
|
5
|
-
* Released under the MIT License
|
|
6
|
-
*/
|
|
7
|
-
import Hammer from 'hammerjs';
|
|
8
|
-
import { isNumber, isNullOrUndef, valueOrDefault, almostEquals, sign, getRelativePosition, _isPointInArea } from 'chart.js/helpers';
|
|
9
|
-
|
|
10
|
-
const eventKey = (key)=>`${key}Key`;
|
|
11
|
-
const getModifierKey = (opts)=>opts?.enabled && opts.modifierKey ? opts.modifierKey : undefined;
|
|
12
|
-
const keyPressed = (key, event)=>key && event[eventKey(key)];
|
|
13
|
-
const keyNotPressed = (key, event)=>key && !event[eventKey(key)];
|
|
14
|
-
function directionEnabled(mode, dir, chart) {
|
|
15
|
-
if (mode === undefined) {
|
|
16
|
-
return true;
|
|
17
|
-
} else if (typeof mode === 'string') {
|
|
18
|
-
return mode.indexOf(dir) !== -1;
|
|
19
|
-
} else if (typeof mode === 'function') {
|
|
20
|
-
return mode({
|
|
21
|
-
chart
|
|
22
|
-
}).indexOf(dir) !== -1;
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
function directionsEnabled(mode, chart) {
|
|
27
|
-
if (typeof mode === 'function') {
|
|
28
|
-
mode = mode({
|
|
29
|
-
chart
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
if (typeof mode === 'string') {
|
|
33
|
-
return {
|
|
34
|
-
x: mode.indexOf('x') !== -1,
|
|
35
|
-
y: mode.indexOf('y') !== -1
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
return {
|
|
39
|
-
x: false,
|
|
40
|
-
y: false
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function debounce(fn, delay) {
|
|
44
|
-
let timeout;
|
|
45
|
-
return function() {
|
|
46
|
-
clearTimeout(timeout);
|
|
47
|
-
timeout = setTimeout(fn, delay);
|
|
48
|
-
return delay;
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
function getScaleUnderPoint({ x, y }, chart) {
|
|
52
|
-
const scales = chart.scales;
|
|
53
|
-
const scaleIds = Object.keys(scales);
|
|
54
|
-
for(let i = 0; i < scaleIds.length; i++){
|
|
55
|
-
const scale = scales[scaleIds[i]];
|
|
56
|
-
if (y >= scale.top && y <= scale.bottom && x >= scale.left && x <= scale.right) {
|
|
57
|
-
return scale;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
const convertOverScaleMode = (chart, overScaleMode, scaleEnabled, enabled)=>{
|
|
63
|
-
if (!overScaleMode) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const overScaleEnabled = directionsEnabled(overScaleMode, chart);
|
|
67
|
-
for (const axis of [
|
|
68
|
-
'x',
|
|
69
|
-
'y'
|
|
70
|
-
]){
|
|
71
|
-
if (overScaleEnabled[axis]) {
|
|
72
|
-
scaleEnabled[axis] = enabled[axis];
|
|
73
|
-
enabled[axis] = false;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
const getEnabledScales = (chart, enabled)=>{
|
|
78
|
-
const enabledScales = [];
|
|
79
|
-
for (const scaleItem of Object.values(chart.scales)){
|
|
80
|
-
if (enabled[scaleItem.axis]) {
|
|
81
|
-
enabledScales.push(scaleItem);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return enabledScales || Object.values(chart.scales);
|
|
85
|
-
};
|
|
86
|
-
function getEnabledScalesByPoint(options, point, chart) {
|
|
87
|
-
const { mode = 'xy', scaleMode, overScaleMode } = options || {};
|
|
88
|
-
const scale = getScaleUnderPoint(point, chart);
|
|
89
|
-
const enabled = directionsEnabled(mode, chart);
|
|
90
|
-
const scaleEnabled = directionsEnabled(scaleMode, chart);
|
|
91
|
-
convertOverScaleMode(chart, overScaleMode, scaleEnabled, enabled);
|
|
92
|
-
if (scale && scaleEnabled[scale.axis]) {
|
|
93
|
-
return [
|
|
94
|
-
scale
|
|
95
|
-
];
|
|
96
|
-
}
|
|
97
|
-
return getEnabledScales(chart, enabled);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const chartStates = new WeakMap();
|
|
101
|
-
function getState(chart) {
|
|
102
|
-
let state = chartStates.get(chart);
|
|
103
|
-
if (!state) {
|
|
104
|
-
state = {
|
|
105
|
-
originalScaleLimits: {},
|
|
106
|
-
updatedScaleLimits: {},
|
|
107
|
-
handlers: {},
|
|
108
|
-
options: {},
|
|
109
|
-
targets: {},
|
|
110
|
-
panDelta: {},
|
|
111
|
-
dragging: false,
|
|
112
|
-
panning: false
|
|
113
|
-
};
|
|
114
|
-
chartStates.set(chart, state);
|
|
115
|
-
}
|
|
116
|
-
return state;
|
|
117
|
-
}
|
|
118
|
-
function removeState(chart) {
|
|
119
|
-
chartStates.delete(chart);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const isTimeScale = (scale)=>scale.type === 'time';
|
|
123
|
-
const isNotNumber = (value)=>value === undefined || isNaN(value);
|
|
124
|
-
function zoomDelta(val, min, range, newRange) {
|
|
125
|
-
const minPercent = range && isNumber(val) && isNumber(min) ? Math.max(0, Math.min(1, (val - min) / range)) : 0;
|
|
126
|
-
const maxPercent = 1 - minPercent;
|
|
127
|
-
return {
|
|
128
|
-
min: newRange * minPercent,
|
|
129
|
-
max: newRange * maxPercent
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
function getValueAtPoint(scale, point) {
|
|
133
|
-
const pixel = scale.isHorizontal() ? point.x : point.y;
|
|
134
|
-
return scale.getValueForPixel(pixel);
|
|
135
|
-
}
|
|
136
|
-
function linearZoomDelta(scale, zoom, center) {
|
|
137
|
-
const range = scale.max - scale.min;
|
|
138
|
-
const newRange = range * (zoom - 1);
|
|
139
|
-
const centerValue = getValueAtPoint(scale, center);
|
|
140
|
-
return zoomDelta(centerValue, scale.min, range, newRange);
|
|
141
|
-
}
|
|
142
|
-
function logarithmicZoomRange(scale, zoom, center) {
|
|
143
|
-
const centerValue = getValueAtPoint(scale, center);
|
|
144
|
-
if (centerValue === undefined) {
|
|
145
|
-
return {
|
|
146
|
-
min: scale.min,
|
|
147
|
-
max: scale.max
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
const logMin = Math.log10(scale.min);
|
|
151
|
-
const logMax = Math.log10(scale.max);
|
|
152
|
-
const logCenter = Math.log10(centerValue);
|
|
153
|
-
const logRange = logMax - logMin;
|
|
154
|
-
const newLogRange = logRange * (zoom - 1);
|
|
155
|
-
const delta = zoomDelta(logCenter, logMin, logRange, newLogRange);
|
|
156
|
-
return {
|
|
157
|
-
min: Math.pow(10, logMin + delta.min),
|
|
158
|
-
max: Math.pow(10, logMax - delta.max)
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
function getScaleLimits(scale, limits) {
|
|
162
|
-
return limits?.[scale.id] || limits?.[scale.axis] || {};
|
|
163
|
-
}
|
|
164
|
-
function getLimit(state, scale, scaleLimits, prop, fallback) {
|
|
165
|
-
let limit = scaleLimits[prop];
|
|
166
|
-
if (limit === 'original') {
|
|
167
|
-
const original = state.originalScaleLimits[scale.id][prop];
|
|
168
|
-
if (isNumber(original.options)) {
|
|
169
|
-
return original.options;
|
|
170
|
-
}
|
|
171
|
-
if (!isNullOrUndef(original.options)) {
|
|
172
|
-
const parsed = scale.parse(original.options);
|
|
173
|
-
if (isNumber(parsed)) {
|
|
174
|
-
return parsed;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
limit = original.scale;
|
|
178
|
-
}
|
|
179
|
-
return valueOrDefault(limit, fallback);
|
|
180
|
-
}
|
|
181
|
-
function linearRange(scale, pixel0, pixel1) {
|
|
182
|
-
const v0 = scale.getValueForPixel(pixel0) ?? scale.min;
|
|
183
|
-
const v1 = scale.getValueForPixel(pixel1) ?? scale.max;
|
|
184
|
-
return {
|
|
185
|
-
min: Math.min(v0, v1),
|
|
186
|
-
max: Math.max(v0, v1)
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
function fixRange(range, { min, max, minLimit, maxLimit }, state, scale) {
|
|
190
|
-
const offset = (range - max + min) / 2;
|
|
191
|
-
min -= offset;
|
|
192
|
-
max += offset;
|
|
193
|
-
const origLimits = {
|
|
194
|
-
min: 'original',
|
|
195
|
-
max: 'original'
|
|
196
|
-
};
|
|
197
|
-
const origMin = getLimit(state, scale, origLimits, 'min', -Infinity);
|
|
198
|
-
const origMax = getLimit(state, scale, origLimits, 'max', Infinity);
|
|
199
|
-
const epsilon = range / 1e6;
|
|
200
|
-
if (almostEquals(min, origMin, epsilon)) {
|
|
201
|
-
min = origMin;
|
|
202
|
-
}
|
|
203
|
-
if (almostEquals(max, origMax, epsilon)) {
|
|
204
|
-
max = origMax;
|
|
205
|
-
}
|
|
206
|
-
if (min < minLimit) {
|
|
207
|
-
min = minLimit;
|
|
208
|
-
max = Math.min(minLimit + range, maxLimit);
|
|
209
|
-
} else if (max > maxLimit) {
|
|
210
|
-
max = maxLimit;
|
|
211
|
-
min = Math.max(maxLimit - range, minLimit);
|
|
212
|
-
}
|
|
213
|
-
return {
|
|
214
|
-
min,
|
|
215
|
-
max
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
function updateRange(scale, { min, max }, limits, zoom = false, pan = false) {
|
|
219
|
-
const state = getState(scale.chart);
|
|
220
|
-
const { options: scaleOpts } = scale;
|
|
221
|
-
const scaleLimits = getScaleLimits(scale, limits);
|
|
222
|
-
const { minRange = 0 } = scaleLimits;
|
|
223
|
-
const minLimit = getLimit(state, scale, scaleLimits, 'min', -Infinity);
|
|
224
|
-
const maxLimit = getLimit(state, scale, scaleLimits, 'max', Infinity);
|
|
225
|
-
if (pan && (min < minLimit || max > maxLimit)) {
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
228
|
-
const scaleRange = scale.max - scale.min;
|
|
229
|
-
const range = zoom ? Math.max(max - min, minRange) : scaleRange;
|
|
230
|
-
if (zoom && range === minRange && scaleRange <= minRange) {
|
|
231
|
-
return true;
|
|
232
|
-
}
|
|
233
|
-
const newRange = fixRange(range, {
|
|
234
|
-
min,
|
|
235
|
-
max,
|
|
236
|
-
minLimit,
|
|
237
|
-
maxLimit
|
|
238
|
-
}, state, scale);
|
|
239
|
-
scaleOpts.min = newRange.min;
|
|
240
|
-
scaleOpts.max = newRange.max;
|
|
241
|
-
state.updatedScaleLimits[scale.id] = newRange;
|
|
242
|
-
return scale.parse(newRange.min) !== scale.min || scale.parse(newRange.max) !== scale.max;
|
|
243
|
-
}
|
|
244
|
-
function zoomNumericalScale(scale, zoom, center, limits) {
|
|
245
|
-
const delta = linearZoomDelta(scale, zoom, center);
|
|
246
|
-
const newRange = {
|
|
247
|
-
min: scale.min + delta.min,
|
|
248
|
-
max: scale.max - delta.max
|
|
249
|
-
};
|
|
250
|
-
return updateRange(scale, newRange, limits, true);
|
|
251
|
-
}
|
|
252
|
-
function zoomLogarithmicScale(scale, zoom, center, limits) {
|
|
253
|
-
const newRange = logarithmicZoomRange(scale, zoom, center);
|
|
254
|
-
return updateRange(scale, newRange, limits, true);
|
|
255
|
-
}
|
|
256
|
-
function zoomRectNumericalScale(scale, from, to, limits) {
|
|
257
|
-
return updateRange(scale, linearRange(scale, from, to), limits, true);
|
|
258
|
-
}
|
|
259
|
-
const integerChange = (v)=>v === 0 || isNaN(v) ? 0 : v < 0 ? Math.min(Math.round(v), -1) : Math.max(Math.round(v), 1);
|
|
260
|
-
function existCategoryFromMaxZoom(scale) {
|
|
261
|
-
const labels = scale.getLabels();
|
|
262
|
-
const maxIndex = labels.length - 1;
|
|
263
|
-
if (scale.min > 0) {
|
|
264
|
-
scale.min -= 1;
|
|
265
|
-
}
|
|
266
|
-
if (scale.max < maxIndex) {
|
|
267
|
-
scale.max += 1;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
function zoomCategoryScale(scale, zoom, center, limits) {
|
|
271
|
-
const delta = linearZoomDelta(scale, zoom, center);
|
|
272
|
-
if (scale.min === scale.max && zoom < 1) {
|
|
273
|
-
existCategoryFromMaxZoom(scale);
|
|
274
|
-
}
|
|
275
|
-
const newRange = {
|
|
276
|
-
min: scale.min + integerChange(delta.min),
|
|
277
|
-
max: scale.max - integerChange(delta.max)
|
|
278
|
-
};
|
|
279
|
-
return updateRange(scale, newRange, limits, true);
|
|
280
|
-
}
|
|
281
|
-
function scaleLength(scale) {
|
|
282
|
-
return scale.isHorizontal() ? scale.width : scale.height;
|
|
283
|
-
}
|
|
284
|
-
function panCategoryScale(scale, delta, limits) {
|
|
285
|
-
const labels = scale.getLabels();
|
|
286
|
-
const lastLabelIndex = labels.length - 1;
|
|
287
|
-
let { min, max } = scale;
|
|
288
|
-
const range = Math.max(max - min, 1);
|
|
289
|
-
const stepDelta = Math.round(scaleLength(scale) / Math.max(range, 10));
|
|
290
|
-
const stepSize = Math.round(Math.abs(delta / stepDelta));
|
|
291
|
-
let applied;
|
|
292
|
-
if (delta < -stepDelta) {
|
|
293
|
-
max = Math.min(max + stepSize, lastLabelIndex);
|
|
294
|
-
min = range === 1 ? max : max - range;
|
|
295
|
-
applied = max === lastLabelIndex;
|
|
296
|
-
} else if (delta > stepDelta) {
|
|
297
|
-
min = Math.max(0, min - stepSize);
|
|
298
|
-
max = range === 1 ? min : min + range;
|
|
299
|
-
applied = min === 0;
|
|
300
|
-
}
|
|
301
|
-
return updateRange(scale, {
|
|
302
|
-
min,
|
|
303
|
-
max
|
|
304
|
-
}, limits) || Boolean(applied);
|
|
305
|
-
}
|
|
306
|
-
const OFFSETS = {
|
|
307
|
-
millisecond: 0,
|
|
308
|
-
second: 500,
|
|
309
|
-
minute: 30 * 1000,
|
|
310
|
-
hour: 30 * 60 * 1000,
|
|
311
|
-
day: 12 * 60 * 60 * 1000,
|
|
312
|
-
week: 3.5 * 24 * 60 * 60 * 1000,
|
|
313
|
-
month: 15 * 24 * 60 * 60 * 1000,
|
|
314
|
-
quarter: 60 * 24 * 60 * 60 * 1000,
|
|
315
|
-
year: 182 * 24 * 60 * 60 * 1000
|
|
316
|
-
};
|
|
317
|
-
function panNumericalScale(scale, delta, limits, canZoom = false) {
|
|
318
|
-
const { min: prevStart, max: prevEnd } = scale;
|
|
319
|
-
let offset = 0;
|
|
320
|
-
if (isTimeScale(scale)) {
|
|
321
|
-
const round = scale.options.time?.round;
|
|
322
|
-
offset = round ? OFFSETS[round] : 0;
|
|
323
|
-
}
|
|
324
|
-
const newMin = scale.getValueForPixel(scale.getPixelForValue(prevStart + offset) - delta);
|
|
325
|
-
const newMax = scale.getValueForPixel(scale.getPixelForValue(prevEnd + offset) - delta);
|
|
326
|
-
if (isNotNumber(newMin) || isNotNumber(newMax)) {
|
|
327
|
-
return true;
|
|
328
|
-
}
|
|
329
|
-
return updateRange(scale, {
|
|
330
|
-
min: newMin,
|
|
331
|
-
max: newMax
|
|
332
|
-
}, limits, canZoom, true);
|
|
333
|
-
}
|
|
334
|
-
function panNonLinearScale(scale, delta, limits) {
|
|
335
|
-
return panNumericalScale(scale, delta, limits, true);
|
|
336
|
-
}
|
|
337
|
-
const zoomFunctions = {
|
|
338
|
-
category: zoomCategoryScale,
|
|
339
|
-
default: zoomNumericalScale,
|
|
340
|
-
logarithmic: zoomLogarithmicScale
|
|
341
|
-
};
|
|
342
|
-
const zoomRectFunctions = {
|
|
343
|
-
default: zoomRectNumericalScale
|
|
344
|
-
};
|
|
345
|
-
const panFunctions = {
|
|
346
|
-
category: panCategoryScale,
|
|
347
|
-
default: panNumericalScale,
|
|
348
|
-
logarithmic: panNonLinearScale,
|
|
349
|
-
timeseries: panNonLinearScale
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
function shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits) {
|
|
353
|
-
const { id, options: { min, max } } = scale;
|
|
354
|
-
if (!originalScaleLimits[id] || !updatedScaleLimits[id]) {
|
|
355
|
-
return true;
|
|
356
|
-
}
|
|
357
|
-
const previous = updatedScaleLimits[id];
|
|
358
|
-
return previous.min !== min || previous.max !== max;
|
|
359
|
-
}
|
|
360
|
-
function removeMissingScales(limits, scales) {
|
|
361
|
-
for (const key of Object.keys(limits)){
|
|
362
|
-
if (!scales[key]) {
|
|
363
|
-
delete limits[key];
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
function storeOriginalScaleLimits(chart, state) {
|
|
368
|
-
const { scales } = chart;
|
|
369
|
-
const { originalScaleLimits, updatedScaleLimits } = state;
|
|
370
|
-
for (const scale of Object.values(scales)){
|
|
371
|
-
if (shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits)) {
|
|
372
|
-
originalScaleLimits[scale.id] = {
|
|
373
|
-
min: {
|
|
374
|
-
scale: scale.min,
|
|
375
|
-
options: scale.options.min
|
|
376
|
-
},
|
|
377
|
-
max: {
|
|
378
|
-
scale: scale.max,
|
|
379
|
-
options: scale.options.max
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
removeMissingScales(originalScaleLimits, scales);
|
|
385
|
-
removeMissingScales(updatedScaleLimits, scales);
|
|
386
|
-
return originalScaleLimits;
|
|
387
|
-
}
|
|
388
|
-
function doZoom(scale, amount, center, limits) {
|
|
389
|
-
const fn = zoomFunctions[scale.type] || zoomFunctions.default;
|
|
390
|
-
fn?.(scale, amount, center, limits);
|
|
391
|
-
}
|
|
392
|
-
function doZoomRect(scale, from, to, limits) {
|
|
393
|
-
const fn = zoomRectFunctions[scale.type] || zoomRectFunctions.default;
|
|
394
|
-
fn?.(scale, from, to, limits);
|
|
395
|
-
}
|
|
396
|
-
function getCenter(chart) {
|
|
397
|
-
const ca = chart.chartArea;
|
|
398
|
-
return {
|
|
399
|
-
x: (ca.left + ca.right) / 2,
|
|
400
|
-
y: (ca.top + ca.bottom) / 2
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
function zoom(chart, amount, transition = 'none', trigger = 'api') {
|
|
404
|
-
const { x = 1, y = 1, focalPoint = getCenter(chart) } = typeof amount === 'number' ? {
|
|
405
|
-
x: amount,
|
|
406
|
-
y: amount
|
|
407
|
-
} : amount;
|
|
408
|
-
const state = getState(chart);
|
|
409
|
-
const { options: { limits = {}, zoom: zoomOptions } } = state;
|
|
410
|
-
storeOriginalScaleLimits(chart, state);
|
|
411
|
-
const xEnabled = x !== 1;
|
|
412
|
-
const yEnabled = y !== 1;
|
|
413
|
-
const enabledScales = getEnabledScalesByPoint(zoomOptions, focalPoint, chart);
|
|
414
|
-
for (const scale of enabledScales){
|
|
415
|
-
if (scale.isHorizontal() && xEnabled) {
|
|
416
|
-
doZoom(scale, x, focalPoint, limits);
|
|
417
|
-
} else if (!scale.isHorizontal() && yEnabled) {
|
|
418
|
-
doZoom(scale, y, focalPoint, limits);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
chart.update(transition);
|
|
422
|
-
zoomOptions?.onZoom?.({
|
|
423
|
-
chart,
|
|
424
|
-
trigger,
|
|
425
|
-
amount: {
|
|
426
|
-
x,
|
|
427
|
-
y,
|
|
428
|
-
focalPoint
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
function zoomRect(chart, p0, p1, transition = 'none', trigger = 'api') {
|
|
433
|
-
const state = getState(chart);
|
|
434
|
-
const { options: { limits = {}, zoom: zoomOptions = {} } } = state;
|
|
435
|
-
const { mode = 'xy' } = zoomOptions;
|
|
436
|
-
storeOriginalScaleLimits(chart, state);
|
|
437
|
-
const xEnabled = directionEnabled(mode, 'x', chart);
|
|
438
|
-
const yEnabled = directionEnabled(mode, 'y', chart);
|
|
439
|
-
for (const scale of Object.values(chart.scales)){
|
|
440
|
-
if (scale.isHorizontal() && xEnabled) {
|
|
441
|
-
doZoomRect(scale, p0.x, p1.x, limits);
|
|
442
|
-
} else if (!scale.isHorizontal() && yEnabled) {
|
|
443
|
-
doZoomRect(scale, p0.y, p1.y, limits);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
chart.update(transition);
|
|
447
|
-
zoomOptions.onZoom?.({
|
|
448
|
-
chart,
|
|
449
|
-
trigger
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
function zoomScale(chart, scaleId, range, transition = 'none', trigger = 'api') {
|
|
453
|
-
const state = getState(chart);
|
|
454
|
-
storeOriginalScaleLimits(chart, state);
|
|
455
|
-
const scale = chart.scales[scaleId];
|
|
456
|
-
updateRange(scale, range, undefined, true);
|
|
457
|
-
chart.update(transition);
|
|
458
|
-
state.options.zoom?.onZoom?.({
|
|
459
|
-
chart,
|
|
460
|
-
trigger
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
function resetZoom(chart, transition = 'default') {
|
|
464
|
-
const state = getState(chart);
|
|
465
|
-
const originalScaleLimits = storeOriginalScaleLimits(chart, state);
|
|
466
|
-
for (const scale of Object.values(chart.scales)){
|
|
467
|
-
const scaleOptions = scale.options;
|
|
468
|
-
if (originalScaleLimits[scale.id]) {
|
|
469
|
-
scaleOptions.min = originalScaleLimits[scale.id].min.options;
|
|
470
|
-
scaleOptions.max = originalScaleLimits[scale.id].max.options;
|
|
471
|
-
} else {
|
|
472
|
-
delete scaleOptions.min;
|
|
473
|
-
delete scaleOptions.max;
|
|
474
|
-
}
|
|
475
|
-
delete state.updatedScaleLimits[scale.id];
|
|
476
|
-
}
|
|
477
|
-
chart.update(transition);
|
|
478
|
-
state.options.zoom?.onZoomComplete?.({
|
|
479
|
-
chart
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
function getOriginalRange(state, scaleId) {
|
|
483
|
-
const original = state.originalScaleLimits[scaleId];
|
|
484
|
-
if (!original) {
|
|
485
|
-
return undefined;
|
|
486
|
-
}
|
|
487
|
-
const { min, max } = original;
|
|
488
|
-
if (isNumber(max.options) && isNumber(min.options)) {
|
|
489
|
-
return max.options - min.options;
|
|
490
|
-
}
|
|
491
|
-
if (isNumber(max.scale) && isNumber(min.scale)) {
|
|
492
|
-
return max.scale - min.scale;
|
|
493
|
-
}
|
|
494
|
-
return undefined;
|
|
495
|
-
}
|
|
496
|
-
function getZoomLevel(chart) {
|
|
497
|
-
const state = getState(chart);
|
|
498
|
-
let min = 1;
|
|
499
|
-
let max = 1;
|
|
500
|
-
for (const scale of Object.values(chart.scales)){
|
|
501
|
-
const origRange = getOriginalRange(state, scale.id);
|
|
502
|
-
if (origRange) {
|
|
503
|
-
const level = Math.round(origRange / (scale.max - scale.min) * 100) / 100;
|
|
504
|
-
min = Math.min(min, level);
|
|
505
|
-
max = Math.max(max, level);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return min < 1 ? min : max;
|
|
509
|
-
}
|
|
510
|
-
function panScale(scale, delta, limits, state) {
|
|
511
|
-
const { panDelta } = state;
|
|
512
|
-
const storedDelta = panDelta[scale.id] || 0;
|
|
513
|
-
if (sign(storedDelta) === sign(delta)) {
|
|
514
|
-
delta += storedDelta;
|
|
515
|
-
}
|
|
516
|
-
const fn = panFunctions[scale.type] || panFunctions.default;
|
|
517
|
-
if (fn?.(scale, delta, limits)) {
|
|
518
|
-
panDelta[scale.id] = 0;
|
|
519
|
-
} else {
|
|
520
|
-
panDelta[scale.id] = delta;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
function pan(chart, delta, enabledScales, transition = 'none', trigger = 'other') {
|
|
524
|
-
const { x = 0, y = 0 } = typeof delta === 'number' ? {
|
|
525
|
-
x: delta,
|
|
526
|
-
y: delta
|
|
527
|
-
} : delta;
|
|
528
|
-
const state = getState(chart);
|
|
529
|
-
const { options: { pan: panOptions, limits = {} } } = state;
|
|
530
|
-
const { onPan } = panOptions || {};
|
|
531
|
-
storeOriginalScaleLimits(chart, state);
|
|
532
|
-
const xEnabled = x !== 0;
|
|
533
|
-
const yEnabled = y !== 0;
|
|
534
|
-
const scales = enabledScales || Object.values(chart.scales);
|
|
535
|
-
for (const scale of scales){
|
|
536
|
-
if (scale.isHorizontal() && xEnabled) {
|
|
537
|
-
panScale(scale, x, limits, state);
|
|
538
|
-
} else if (!scale.isHorizontal() && yEnabled) {
|
|
539
|
-
panScale(scale, y, limits, state);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
chart.update(transition);
|
|
543
|
-
onPan?.({
|
|
544
|
-
chart,
|
|
545
|
-
trigger,
|
|
546
|
-
delta: {
|
|
547
|
-
x,
|
|
548
|
-
y
|
|
549
|
-
}
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
function getInitialScaleBounds(chart) {
|
|
553
|
-
const state = getState(chart);
|
|
554
|
-
storeOriginalScaleLimits(chart, state);
|
|
555
|
-
const scaleBounds = {};
|
|
556
|
-
for (const scaleId of Object.keys(chart.scales)){
|
|
557
|
-
const { min, max } = state.originalScaleLimits[scaleId] || {
|
|
558
|
-
min: {},
|
|
559
|
-
max: {}
|
|
560
|
-
};
|
|
561
|
-
scaleBounds[scaleId] = {
|
|
562
|
-
min: min.scale,
|
|
563
|
-
max: max.scale
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
return scaleBounds;
|
|
567
|
-
}
|
|
568
|
-
function getZoomedScaleBounds(chart) {
|
|
569
|
-
const state = getState(chart);
|
|
570
|
-
const scaleBounds = {};
|
|
571
|
-
for (const scaleId of Object.keys(chart.scales)){
|
|
572
|
-
scaleBounds[scaleId] = state.updatedScaleLimits[scaleId];
|
|
573
|
-
}
|
|
574
|
-
return scaleBounds;
|
|
575
|
-
}
|
|
576
|
-
function isZoomedOrPanned(chart) {
|
|
577
|
-
const scaleBounds = getInitialScaleBounds(chart);
|
|
578
|
-
for (const scaleId of Object.keys(chart.scales)){
|
|
579
|
-
const { min: originalMin, max: originalMax } = scaleBounds[scaleId];
|
|
580
|
-
if (originalMin !== undefined && chart.scales[scaleId].min !== originalMin) {
|
|
581
|
-
return true;
|
|
582
|
-
}
|
|
583
|
-
if (originalMax !== undefined && chart.scales[scaleId].max !== originalMax) {
|
|
584
|
-
return true;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
return false;
|
|
588
|
-
}
|
|
589
|
-
function isZoomingOrPanningState(state) {
|
|
590
|
-
return state.panning || state.dragging;
|
|
591
|
-
}
|
|
592
|
-
function isZoomingOrPanning(chart) {
|
|
593
|
-
const state = getState(chart);
|
|
594
|
-
return !!(isZoomingOrPanningState(state) || state.filterNextClick);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
const clamp = (x, from, to)=>Math.min(to, Math.max(from, x));
|
|
598
|
-
function removeHandler(chart, type) {
|
|
599
|
-
const { handlers, targets } = getState(chart);
|
|
600
|
-
const handler = handlers[type];
|
|
601
|
-
const target = targets[type];
|
|
602
|
-
if (handler && target) {
|
|
603
|
-
target.removeEventListener(type, handler);
|
|
604
|
-
delete handlers[type];
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
function addHandler(chart, target, type, handler) {
|
|
608
|
-
const { handlers, options, targets } = getState(chart);
|
|
609
|
-
const oldHandler = handlers[type];
|
|
610
|
-
if (oldHandler && targets[type] === target) {
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
removeHandler(chart, type);
|
|
614
|
-
const listener = handlers[type] = (event)=>handler(chart, event, options);
|
|
615
|
-
targets[type] = target;
|
|
616
|
-
const passive = type === 'wheel' ? false : undefined;
|
|
617
|
-
target.addEventListener(type, listener, {
|
|
618
|
-
passive
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
function mouseMove(chart, event) {
|
|
622
|
-
const state = getState(chart);
|
|
623
|
-
if (state.dragStart) {
|
|
624
|
-
state.dragging = true;
|
|
625
|
-
state.dragEnd = event;
|
|
626
|
-
chart.draw();
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
function keyDown(chart, event) {
|
|
630
|
-
const state = getState(chart);
|
|
631
|
-
if (!state.dragStart || event.key !== 'Escape') {
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
removeHandler(chart, 'keydown');
|
|
635
|
-
state.dragging = false;
|
|
636
|
-
state.dragStart = state.dragEnd = undefined;
|
|
637
|
-
chart.draw();
|
|
638
|
-
}
|
|
639
|
-
function getPointPosition(event, chart) {
|
|
640
|
-
if (event.target !== chart.canvas) {
|
|
641
|
-
const canvasArea = chart.canvas.getBoundingClientRect();
|
|
642
|
-
return {
|
|
643
|
-
x: event.clientX - canvasArea.left,
|
|
644
|
-
y: event.clientY - canvasArea.top
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
return getRelativePosition(event, chart)
|
|
648
|
-
;
|
|
649
|
-
}
|
|
650
|
-
function zoomStart(chart, event, zoomOptions) {
|
|
651
|
-
const { onZoomStart, onZoomRejected } = zoomOptions;
|
|
652
|
-
if (onZoomStart) {
|
|
653
|
-
const point = getPointPosition(event, chart);
|
|
654
|
-
if (onZoomStart?.({
|
|
655
|
-
chart,
|
|
656
|
-
event,
|
|
657
|
-
point
|
|
658
|
-
}) === false) {
|
|
659
|
-
onZoomRejected?.({
|
|
660
|
-
chart,
|
|
661
|
-
event
|
|
662
|
-
});
|
|
663
|
-
return false;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
function mouseDown(chart, event) {
|
|
668
|
-
if (chart.legend) {
|
|
669
|
-
const point = getRelativePosition(event, chart)
|
|
670
|
-
;
|
|
671
|
-
if (_isPointInArea(point, chart.legend)) {
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
const state = getState(chart);
|
|
676
|
-
const { pan: panOptions, zoom: zoomOptions = {} } = state.options;
|
|
677
|
-
if (event.button !== 0 || keyPressed(getModifierKey(panOptions), event) || keyNotPressed(getModifierKey(zoomOptions.drag), event)) {
|
|
678
|
-
return zoomOptions.onZoomRejected?.({
|
|
679
|
-
chart,
|
|
680
|
-
event
|
|
681
|
-
});
|
|
682
|
-
}
|
|
683
|
-
if (zoomStart(chart, event, zoomOptions) === false) {
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
state.dragStart = event;
|
|
687
|
-
addHandler(chart, chart.canvas.ownerDocument, 'mousemove', mouseMove);
|
|
688
|
-
addHandler(chart, window.document, 'keydown', keyDown);
|
|
689
|
-
}
|
|
690
|
-
function applyAspectRatio({ begin, end }, aspectRatio) {
|
|
691
|
-
let width = end.x - begin.x;
|
|
692
|
-
let height = end.y - begin.y;
|
|
693
|
-
const ratio = Math.abs(width / height);
|
|
694
|
-
if (ratio > aspectRatio) {
|
|
695
|
-
width = Math.sign(width) * Math.abs(height * aspectRatio);
|
|
696
|
-
} else if (ratio < aspectRatio) {
|
|
697
|
-
height = Math.sign(height) * Math.abs(width / aspectRatio);
|
|
698
|
-
}
|
|
699
|
-
end.x = begin.x + width;
|
|
700
|
-
end.y = begin.y + height;
|
|
701
|
-
}
|
|
702
|
-
function applyMinMaxProps(rect, chartArea, points, { min, max, prop }) {
|
|
703
|
-
rect[min] = clamp(Math.min(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
|
|
704
|
-
rect[max] = clamp(Math.max(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
|
|
705
|
-
}
|
|
706
|
-
function getRelativePoints(chart, pointEvents, maintainAspectRatio) {
|
|
707
|
-
const points = {
|
|
708
|
-
begin: getPointPosition(pointEvents.dragStart, chart),
|
|
709
|
-
end: getPointPosition(pointEvents.dragEnd, chart)
|
|
710
|
-
};
|
|
711
|
-
if (maintainAspectRatio) {
|
|
712
|
-
const aspectRatio = chart.chartArea.width / chart.chartArea.height;
|
|
713
|
-
applyAspectRatio(points, aspectRatio);
|
|
714
|
-
}
|
|
715
|
-
return points;
|
|
716
|
-
}
|
|
717
|
-
function computeDragRect(chart, mode, pointEvents, maintainAspectRatio) {
|
|
718
|
-
const xEnabled = directionEnabled(mode, 'x', chart);
|
|
719
|
-
const yEnabled = directionEnabled(mode, 'y', chart);
|
|
720
|
-
const { top, left, right, bottom, width: chartWidth, height: chartHeight } = chart.chartArea;
|
|
721
|
-
const rect = {
|
|
722
|
-
top,
|
|
723
|
-
left,
|
|
724
|
-
right,
|
|
725
|
-
bottom
|
|
726
|
-
};
|
|
727
|
-
const points = getRelativePoints(chart, pointEvents, maintainAspectRatio && xEnabled && yEnabled);
|
|
728
|
-
if (xEnabled) {
|
|
729
|
-
applyMinMaxProps(rect, chart.chartArea, points, {
|
|
730
|
-
min: 'left',
|
|
731
|
-
max: 'right',
|
|
732
|
-
prop: 'x'
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
if (yEnabled) {
|
|
736
|
-
applyMinMaxProps(rect, chart.chartArea, points, {
|
|
737
|
-
min: 'top',
|
|
738
|
-
max: 'bottom',
|
|
739
|
-
prop: 'y'
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
const width = rect.right - rect.left;
|
|
743
|
-
const height = rect.bottom - rect.top;
|
|
744
|
-
return {
|
|
745
|
-
...rect,
|
|
746
|
-
width,
|
|
747
|
-
height,
|
|
748
|
-
zoomX: xEnabled && width ? 1 + (chartWidth - width) / chartWidth : 1,
|
|
749
|
-
zoomY: yEnabled && height ? 1 + (chartHeight - height) / chartHeight : 1
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
function mouseUp(chart, event) {
|
|
753
|
-
const state = getState(chart);
|
|
754
|
-
if (!state.dragStart) {
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
removeHandler(chart, 'mousemove');
|
|
758
|
-
const { mode, onZoomComplete, drag } = state.options.zoom ?? {};
|
|
759
|
-
const { threshold = 0, maintainAspectRatio } = drag ?? {};
|
|
760
|
-
const rect = computeDragRect(chart, mode, {
|
|
761
|
-
dragStart: state.dragStart,
|
|
762
|
-
dragEnd: event
|
|
763
|
-
}, maintainAspectRatio);
|
|
764
|
-
const distanceX = directionEnabled(mode, 'x', chart) ? rect.width : 0;
|
|
765
|
-
const distanceY = directionEnabled(mode, 'y', chart) ? rect.height : 0;
|
|
766
|
-
const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
|
|
767
|
-
state.dragStart = state.dragEnd = undefined;
|
|
768
|
-
if (distance <= threshold) {
|
|
769
|
-
state.dragging = false;
|
|
770
|
-
chart.draw();
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
zoomRect(chart, {
|
|
774
|
-
x: rect.left,
|
|
775
|
-
y: rect.top
|
|
776
|
-
}, {
|
|
777
|
-
x: rect.right,
|
|
778
|
-
y: rect.bottom
|
|
779
|
-
}, 'zoom', 'drag');
|
|
780
|
-
state.dragging = false;
|
|
781
|
-
state.filterNextClick = true;
|
|
782
|
-
onZoomComplete?.({
|
|
783
|
-
chart
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
function wheelPreconditions(chart, event, zoomOptions) {
|
|
787
|
-
if (keyNotPressed(getModifierKey(zoomOptions.wheel), event)) {
|
|
788
|
-
zoomOptions.onZoomRejected?.({
|
|
789
|
-
chart,
|
|
790
|
-
event
|
|
791
|
-
});
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
794
|
-
if (zoomStart(chart, event, zoomOptions) === false) {
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
if (event.cancelable) {
|
|
798
|
-
event.preventDefault();
|
|
799
|
-
}
|
|
800
|
-
if (event.deltaY === undefined) {
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
return true;
|
|
804
|
-
}
|
|
805
|
-
function wheel(chart, event) {
|
|
806
|
-
const { handlers: { onZoomComplete }, options: { zoom: zoomOptions = {} } } = getState(chart);
|
|
807
|
-
if (!wheelPreconditions(chart, event, zoomOptions)) {
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
const rect = event.target?.getBoundingClientRect();
|
|
811
|
-
const speed = zoomOptions?.wheel?.speed ?? 0.1;
|
|
812
|
-
const percentage = event.deltaY >= 0 ? 2 - 1 / (1 - speed) : 1 + speed;
|
|
813
|
-
const amount = {
|
|
814
|
-
x: percentage,
|
|
815
|
-
y: percentage,
|
|
816
|
-
focalPoint: {
|
|
817
|
-
x: event.clientX - rect.left,
|
|
818
|
-
y: event.clientY - rect.top
|
|
819
|
-
}
|
|
820
|
-
};
|
|
821
|
-
zoom(chart, amount, 'zoom', 'wheel');
|
|
822
|
-
onZoomComplete?.(event);
|
|
823
|
-
}
|
|
824
|
-
function addDebouncedHandler(chart, name, handler, delay) {
|
|
825
|
-
if (handler) {
|
|
826
|
-
getState(chart).handlers[name] = debounce(()=>handler?.({
|
|
827
|
-
chart
|
|
828
|
-
}), delay);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
function addListeners(chart, options) {
|
|
832
|
-
const canvas = chart.canvas;
|
|
833
|
-
const { wheel: wheelOptions, drag: dragOptions, onZoomComplete } = options.zoom ?? {};
|
|
834
|
-
if (wheelOptions?.enabled) {
|
|
835
|
-
addHandler(chart, canvas, 'wheel', wheel);
|
|
836
|
-
addDebouncedHandler(chart, 'onZoomComplete', onZoomComplete, 250);
|
|
837
|
-
} else {
|
|
838
|
-
removeHandler(chart, 'wheel');
|
|
839
|
-
}
|
|
840
|
-
if (dragOptions?.enabled) {
|
|
841
|
-
addHandler(chart, canvas, 'mousedown', mouseDown);
|
|
842
|
-
addHandler(chart, canvas.ownerDocument, 'mouseup', mouseUp);
|
|
843
|
-
} else {
|
|
844
|
-
removeHandler(chart, 'mousedown');
|
|
845
|
-
removeHandler(chart, 'mousemove');
|
|
846
|
-
removeHandler(chart, 'mouseup');
|
|
847
|
-
removeHandler(chart, 'keydown');
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
function removeListeners(chart) {
|
|
851
|
-
removeHandler(chart, 'mousedown');
|
|
852
|
-
removeHandler(chart, 'mousemove');
|
|
853
|
-
removeHandler(chart, 'mouseup');
|
|
854
|
-
removeHandler(chart, 'wheel');
|
|
855
|
-
removeHandler(chart, 'click');
|
|
856
|
-
removeHandler(chart, 'keydown');
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
function createEnabler(chart, state) {
|
|
860
|
-
return function(_recognizer, event) {
|
|
861
|
-
const { pan: panOptions, zoom: zoomOptions = {} } = state.options;
|
|
862
|
-
if (!panOptions || !panOptions.enabled) {
|
|
863
|
-
return false;
|
|
864
|
-
}
|
|
865
|
-
const srcEvent = event && event.srcEvent;
|
|
866
|
-
if (!srcEvent) {
|
|
867
|
-
return true;
|
|
868
|
-
}
|
|
869
|
-
if (!state.panning && event.pointerType === 'mouse' && (keyNotPressed(getModifierKey(panOptions), srcEvent) || keyPressed(getModifierKey(zoomOptions.drag), srcEvent))) {
|
|
870
|
-
panOptions.onPanRejected?.({
|
|
871
|
-
chart,
|
|
872
|
-
event
|
|
873
|
-
});
|
|
874
|
-
return false;
|
|
875
|
-
}
|
|
876
|
-
return true;
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
function pinchAxes(p0, p1) {
|
|
880
|
-
const pinchX = Math.abs(p0.clientX - p1.clientX);
|
|
881
|
-
const pinchY = Math.abs(p0.clientY - p1.clientY);
|
|
882
|
-
const p = pinchX / pinchY;
|
|
883
|
-
let x, y;
|
|
884
|
-
if (p > 0.3 && p < 1.7) {
|
|
885
|
-
x = y = true;
|
|
886
|
-
} else if (pinchX > pinchY) {
|
|
887
|
-
x = true;
|
|
888
|
-
} else {
|
|
889
|
-
y = true;
|
|
890
|
-
}
|
|
891
|
-
return {
|
|
892
|
-
x,
|
|
893
|
-
y
|
|
894
|
-
};
|
|
895
|
-
}
|
|
896
|
-
function handlePinch(chart, state, e) {
|
|
897
|
-
if (state.scale) {
|
|
898
|
-
const { center, pointers } = e;
|
|
899
|
-
const zoomPercent = 1 / state.scale * e.scale;
|
|
900
|
-
const rect = e.target.getBoundingClientRect();
|
|
901
|
-
const pinch = pinchAxes(pointers[0], pointers[1]);
|
|
902
|
-
const mode = state.options.zoom?.mode;
|
|
903
|
-
const amount = {
|
|
904
|
-
x: pinch.x && directionEnabled(mode, 'x', chart) ? zoomPercent : 1,
|
|
905
|
-
y: pinch.y && directionEnabled(mode, 'y', chart) ? zoomPercent : 1,
|
|
906
|
-
focalPoint: {
|
|
907
|
-
x: center.x - rect.left,
|
|
908
|
-
y: center.y - rect.top
|
|
909
|
-
}
|
|
910
|
-
};
|
|
911
|
-
zoom(chart, amount, 'zoom', 'pinch');
|
|
912
|
-
state.scale = e.scale;
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
function startPinch(chart, state, e) {
|
|
916
|
-
if (state.options.zoom?.pinch?.enabled) {
|
|
917
|
-
const point = getRelativePosition(e.srcEvent, chart)
|
|
918
|
-
;
|
|
919
|
-
if (state.options.zoom?.onZoomStart?.({
|
|
920
|
-
chart,
|
|
921
|
-
event: e.srcEvent,
|
|
922
|
-
point
|
|
923
|
-
}) === false) {
|
|
924
|
-
state.scale = null;
|
|
925
|
-
state.options.zoom?.onZoomRejected?.({
|
|
926
|
-
chart,
|
|
927
|
-
event: e.srcEvent
|
|
928
|
-
});
|
|
929
|
-
} else {
|
|
930
|
-
state.scale = 1;
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
function endPinch(chart, state, e) {
|
|
935
|
-
if (state.scale) {
|
|
936
|
-
handlePinch(chart, state, e);
|
|
937
|
-
state.scale = null
|
|
938
|
-
;
|
|
939
|
-
state.options.zoom?.onZoomComplete?.({
|
|
940
|
-
chart
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
function handlePan(chart, state, e) {
|
|
945
|
-
const delta = state.delta;
|
|
946
|
-
if (delta) {
|
|
947
|
-
state.panning = true;
|
|
948
|
-
pan(chart, {
|
|
949
|
-
x: e.deltaX - delta.x,
|
|
950
|
-
y: e.deltaY - delta.y
|
|
951
|
-
}, state.panScales && state.panScales.map((i)=>chart.scales[i]).filter(Boolean));
|
|
952
|
-
state.delta = {
|
|
953
|
-
x: e.deltaX,
|
|
954
|
-
y: e.deltaY
|
|
955
|
-
};
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
function startPan(chart, state, event) {
|
|
959
|
-
const { enabled, onPanStart, onPanRejected } = state.options.pan ?? {};
|
|
960
|
-
if (!enabled) {
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
|
-
const rect = event.target.getBoundingClientRect();
|
|
964
|
-
const point = {
|
|
965
|
-
x: event.center.x - rect.left,
|
|
966
|
-
y: event.center.y - rect.top
|
|
967
|
-
};
|
|
968
|
-
if (onPanStart?.({
|
|
969
|
-
chart,
|
|
970
|
-
event,
|
|
971
|
-
point
|
|
972
|
-
}) === false) {
|
|
973
|
-
return onPanRejected?.({
|
|
974
|
-
chart,
|
|
975
|
-
event
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
state.panScales = getEnabledScalesByPoint(state.options.pan, point, chart).map((i)=>i.id);
|
|
979
|
-
state.delta = {
|
|
980
|
-
x: 0,
|
|
981
|
-
y: 0
|
|
982
|
-
};
|
|
983
|
-
handlePan(chart, state, event);
|
|
984
|
-
}
|
|
985
|
-
function endPan(chart, state) {
|
|
986
|
-
state.delta = null;
|
|
987
|
-
if (state.panning) {
|
|
988
|
-
state.panning = false;
|
|
989
|
-
state.filterNextClick = true;
|
|
990
|
-
state.options.pan?.onPanComplete?.({
|
|
991
|
-
chart
|
|
992
|
-
});
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
const hammers = new WeakMap();
|
|
996
|
-
function startHammer(chart, options) {
|
|
997
|
-
const state = getState(chart);
|
|
998
|
-
const canvas = chart.canvas;
|
|
999
|
-
const { pan: panOptions, zoom: zoomOptions } = options;
|
|
1000
|
-
const mc = new Hammer.Manager(canvas);
|
|
1001
|
-
if (zoomOptions?.pinch?.enabled) {
|
|
1002
|
-
mc.add(new Hammer.Pinch());
|
|
1003
|
-
mc.on('pinchstart', (e)=>startPinch(chart, state, e));
|
|
1004
|
-
mc.on('pinch', (e)=>handlePinch(chart, state, e));
|
|
1005
|
-
mc.on('pinchend', (e)=>endPinch(chart, state, e));
|
|
1006
|
-
}
|
|
1007
|
-
if (panOptions && panOptions.enabled) {
|
|
1008
|
-
mc.add(new Hammer.Pan({
|
|
1009
|
-
threshold: panOptions.threshold,
|
|
1010
|
-
enable: createEnabler(chart, state)
|
|
1011
|
-
}));
|
|
1012
|
-
mc.on('panstart', (e)=>startPan(chart, state, e));
|
|
1013
|
-
mc.on('panmove', (e)=>handlePan(chart, state, e));
|
|
1014
|
-
mc.on('panend', ()=>endPan(chart, state));
|
|
1015
|
-
}
|
|
1016
|
-
hammers.set(chart, mc);
|
|
1017
|
-
}
|
|
1018
|
-
function stopHammer(chart) {
|
|
1019
|
-
const mc = hammers.get(chart);
|
|
1020
|
-
if (mc) {
|
|
1021
|
-
mc.remove('pinchstart');
|
|
1022
|
-
mc.remove('pinch');
|
|
1023
|
-
mc.remove('pinchend');
|
|
1024
|
-
mc.remove('panstart');
|
|
1025
|
-
mc.remove('pan');
|
|
1026
|
-
mc.remove('panend');
|
|
1027
|
-
mc.destroy();
|
|
1028
|
-
hammers.delete(chart);
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
function hammerOptionsChanged(oldOptions, newOptions) {
|
|
1032
|
-
const { pan: oldPan, zoom: oldZoom } = oldOptions;
|
|
1033
|
-
const { pan: newPan, zoom: newZoom } = newOptions;
|
|
1034
|
-
if (oldZoom?.pinch?.enabled !== newZoom?.pinch?.enabled) {
|
|
1035
|
-
return true;
|
|
1036
|
-
}
|
|
1037
|
-
if (oldPan?.enabled !== newPan?.enabled) {
|
|
1038
|
-
return true;
|
|
1039
|
-
}
|
|
1040
|
-
if (oldPan?.threshold !== newPan?.threshold) {
|
|
1041
|
-
return true;
|
|
1042
|
-
}
|
|
1043
|
-
return false;
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
var version = "2.2.7";
|
|
1047
|
-
|
|
1048
|
-
const defaults = {
|
|
1049
|
-
pan: {
|
|
1050
|
-
enabled: false,
|
|
1051
|
-
mode: 'xy',
|
|
1052
|
-
threshold: 10,
|
|
1053
|
-
modifierKey: null
|
|
1054
|
-
},
|
|
1055
|
-
zoom: {
|
|
1056
|
-
wheel: {
|
|
1057
|
-
enabled: false,
|
|
1058
|
-
speed: 0.1,
|
|
1059
|
-
modifierKey: null
|
|
1060
|
-
},
|
|
1061
|
-
drag: {
|
|
1062
|
-
enabled: false,
|
|
1063
|
-
drawTime: 'beforeDatasetsDraw',
|
|
1064
|
-
modifierKey: null
|
|
1065
|
-
},
|
|
1066
|
-
pinch: {
|
|
1067
|
-
enabled: false
|
|
1068
|
-
},
|
|
1069
|
-
mode: 'xy'
|
|
1070
|
-
}
|
|
1071
|
-
};
|
|
1072
|
-
|
|
1073
|
-
function draw(chart, caller, options) {
|
|
1074
|
-
const dragOptions = options.zoom?.drag;
|
|
1075
|
-
const { dragStart, dragEnd } = getState(chart);
|
|
1076
|
-
if (dragOptions?.drawTime !== caller || !dragStart || !dragEnd) {
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
const { left, top, width, height } = computeDragRect(chart, options.zoom?.mode, {
|
|
1080
|
-
dragStart,
|
|
1081
|
-
dragEnd
|
|
1082
|
-
}, dragOptions.maintainAspectRatio);
|
|
1083
|
-
const ctx = chart.ctx;
|
|
1084
|
-
ctx.save();
|
|
1085
|
-
ctx.beginPath();
|
|
1086
|
-
ctx.fillStyle = dragOptions.backgroundColor || 'rgba(225,225,225,0.3)';
|
|
1087
|
-
ctx.fillRect(left, top, width, height);
|
|
1088
|
-
if (dragOptions.borderWidth) {
|
|
1089
|
-
ctx.lineWidth = dragOptions.borderWidth;
|
|
1090
|
-
ctx.strokeStyle = dragOptions.borderColor || 'rgba(225,225,225)';
|
|
1091
|
-
ctx.strokeRect(left, top, width, height);
|
|
1092
|
-
}
|
|
1093
|
-
ctx.restore();
|
|
1094
|
-
}
|
|
1095
|
-
const bindApi = (chart)=>{
|
|
1096
|
-
chart.pan = (delta, panScales, transition)=>pan(chart, delta, panScales, transition, 'api');
|
|
1097
|
-
chart.zoom = (args, transition)=>zoom(chart, args, transition);
|
|
1098
|
-
chart.zoomRect = (p0, p1, transition)=>zoomRect(chart, p0, p1, transition);
|
|
1099
|
-
chart.zoomScale = (id, range, transition)=>zoomScale(chart, id, range, transition);
|
|
1100
|
-
chart.resetZoom = (transition)=>resetZoom(chart, transition);
|
|
1101
|
-
chart.getZoomLevel = ()=>getZoomLevel(chart);
|
|
1102
|
-
chart.getInitialScaleBounds = ()=>getInitialScaleBounds(chart);
|
|
1103
|
-
chart.getZoomedScaleBounds = ()=>getZoomedScaleBounds(chart);
|
|
1104
|
-
chart.isZoomedOrPanned = ()=>isZoomedOrPanned(chart);
|
|
1105
|
-
chart.isZoomingOrPanning = ()=>isZoomingOrPanning(chart);
|
|
1106
|
-
};
|
|
1107
|
-
var plugin = {
|
|
1108
|
-
id: 'zoom',
|
|
1109
|
-
version,
|
|
1110
|
-
defaults,
|
|
1111
|
-
start (chart, _args, options) {
|
|
1112
|
-
const state = getState(chart);
|
|
1113
|
-
state.options = options;
|
|
1114
|
-
if (Object.prototype.hasOwnProperty.call(options.zoom, 'enabled')) {
|
|
1115
|
-
console.warn('The option `zoom.enabled` is no longer supported. Please use `zoom.wheel.enabled`, `zoom.drag.enabled`, or `zoom.pinch.enabled`.');
|
|
1116
|
-
}
|
|
1117
|
-
if (Object.prototype.hasOwnProperty.call(options.zoom, 'overScaleMode') || Object.prototype.hasOwnProperty.call(options.pan, 'overScaleMode')) {
|
|
1118
|
-
console.warn('The option `overScaleMode` is deprecated. Please use `scaleMode` instead (and update `mode` as desired).');
|
|
1119
|
-
}
|
|
1120
|
-
if (Hammer) {
|
|
1121
|
-
startHammer(chart, options);
|
|
1122
|
-
}
|
|
1123
|
-
bindApi(chart);
|
|
1124
|
-
},
|
|
1125
|
-
beforeEvent (chart, { event }) {
|
|
1126
|
-
const state = getState(chart);
|
|
1127
|
-
if (isZoomingOrPanningState(state)) {
|
|
1128
|
-
return false;
|
|
1129
|
-
}
|
|
1130
|
-
if (event.type === 'click' || event.type === 'mouseup') {
|
|
1131
|
-
if (state.filterNextClick) {
|
|
1132
|
-
state.filterNextClick = false;
|
|
1133
|
-
return false;
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
},
|
|
1137
|
-
beforeUpdate (chart, _args, options) {
|
|
1138
|
-
const state = getState(chart);
|
|
1139
|
-
const previousOptions = state.options;
|
|
1140
|
-
state.options = options;
|
|
1141
|
-
if (hammerOptionsChanged(previousOptions, options)) {
|
|
1142
|
-
stopHammer(chart);
|
|
1143
|
-
startHammer(chart, options);
|
|
1144
|
-
}
|
|
1145
|
-
addListeners(chart, options);
|
|
1146
|
-
},
|
|
1147
|
-
beforeDatasetsDraw (chart, _args, options) {
|
|
1148
|
-
draw(chart, 'beforeDatasetsDraw', options);
|
|
1149
|
-
},
|
|
1150
|
-
afterDatasetsDraw (chart, _args, options) {
|
|
1151
|
-
draw(chart, 'afterDatasetsDraw', options);
|
|
1152
|
-
},
|
|
1153
|
-
beforeDraw (chart, _args, options) {
|
|
1154
|
-
draw(chart, 'beforeDraw', options);
|
|
1155
|
-
},
|
|
1156
|
-
afterDraw (chart, _args, options) {
|
|
1157
|
-
draw(chart, 'afterDraw', options);
|
|
1158
|
-
},
|
|
1159
|
-
stop (chart) {
|
|
1160
|
-
removeListeners(chart);
|
|
1161
|
-
if (Hammer) {
|
|
1162
|
-
stopHammer(chart);
|
|
1163
|
-
}
|
|
1164
|
-
removeState(chart);
|
|
1165
|
-
},
|
|
1166
|
-
panFunctions,
|
|
1167
|
-
zoomFunctions,
|
|
1168
|
-
zoomRectFunctions
|
|
1169
|
-
};
|
|
1170
|
-
|
|
1171
|
-
export { plugin as default, pan, resetZoom, zoom, zoomRect, zoomScale };
|
|
1172
|
-
//# sourceMappingURL=chartjs-plugin-zoom.esm.js.map
|