squarified 0.6.2 → 1.1.0
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/dist/{dom-event-ClwTQnot.js → dom-event-BkRCiIWB.js} +279 -97
- package/dist/{dom-event-DrYYfglv.mjs → dom-event-CBrOg7CX.mjs} +266 -98
- package/dist/{index-Bks7tGiV.d.ts → index-C0Hd4N0X.d.ts} +85 -28
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +19 -2
- package/dist/index.mjs +3 -4
- package/dist/plugin.d.mts +33 -18
- package/dist/plugin.d.ts +33 -18
- package/dist/plugin.js +295 -140
- package/dist/plugin.mjs +294 -139
- package/package.json +3 -3
package/dist/plugin.js
CHANGED
|
@@ -1,51 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var domEvent = require('./dom-event-
|
|
3
|
+
var domEvent = require('./dom-event-BkRCiIWB.js');
|
|
4
4
|
|
|
5
|
-
// Currently, etoile is an internal module, so we won't need too much easing functions.
|
|
6
|
-
// And the animation logic is implemented by user code.
|
|
7
|
-
const easing = {
|
|
8
|
-
linear: (k)=>k,
|
|
9
|
-
quadraticIn: (k)=>k * k,
|
|
10
|
-
quadraticOut: (k)=>k * (2 - k),
|
|
11
|
-
quadraticInOut: (k)=>{
|
|
12
|
-
if ((k *= 2) < 1) {
|
|
13
|
-
return 0.5 * k * k;
|
|
14
|
-
}
|
|
15
|
-
return -0.5 * (--k * (k - 2) - 1);
|
|
16
|
-
},
|
|
17
|
-
cubicIn: (k)=>k * k * k,
|
|
18
|
-
cubicOut: (k)=>{
|
|
19
|
-
if ((k *= 2) < 1) {
|
|
20
|
-
return 0.5 * k * k * k;
|
|
21
|
-
}
|
|
22
|
-
return 0.5 * ((k -= 2) * k * k + 2);
|
|
23
|
-
},
|
|
24
|
-
cubicInOut: (k)=>{
|
|
25
|
-
if ((k *= 2) < 1) {
|
|
26
|
-
return 0.5 * k * k * k;
|
|
27
|
-
}
|
|
28
|
-
return 0.5 * ((k -= 2) * k * k + 2);
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
class Highlight extends domEvent.Schedule {
|
|
33
|
-
reset() {
|
|
34
|
-
this.destory();
|
|
35
|
-
this.update();
|
|
36
|
-
}
|
|
37
|
-
get canvas() {
|
|
38
|
-
return this.render.canvas;
|
|
39
|
-
}
|
|
40
|
-
setZIndexForHighlight(zIndex = '-1') {
|
|
41
|
-
this.canvas.style.zIndex = zIndex;
|
|
42
|
-
}
|
|
43
|
-
init() {
|
|
44
|
-
this.setZIndexForHighlight();
|
|
45
|
-
this.canvas.style.position = 'absolute';
|
|
46
|
-
this.canvas.style.pointerEvents = 'none';
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
5
|
const ANIMATION_DURATION = 300;
|
|
50
6
|
const HIGH_LIGHT_OPACITY = 0.3;
|
|
51
7
|
const fill = {
|
|
@@ -58,43 +14,80 @@ const fill = {
|
|
|
58
14
|
};
|
|
59
15
|
const presetHighlightPlugin = domEvent.definePlugin({
|
|
60
16
|
name: 'treemap:preset-highlight',
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
17
|
+
onDOMEventTriggered (name, _, graphic, { stateManager: state, matrix, component }) {
|
|
18
|
+
// Any interaction that isn't a pure hover must reset the overlay so we never
|
|
19
|
+
// show a stale highlight after zoom / drag / pan transitions.
|
|
20
|
+
if (name !== 'mousemove') {
|
|
21
|
+
const meta = this.getPluginMetadata('treemap:preset-highlight');
|
|
22
|
+
if (meta && meta.lastDirtyRect) {
|
|
23
|
+
component.clearOverlay();
|
|
24
|
+
meta.overlayGraphic = null;
|
|
25
|
+
meta.lastDirtyRect = null;
|
|
26
|
+
}
|
|
64
27
|
return;
|
|
65
28
|
}
|
|
66
|
-
if (!meta.highlight) {
|
|
67
|
-
meta.highlight = new Highlight(this.instance.to);
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
onDOMEventTriggered (name, _, module, { stateManager: state, matrix }) {
|
|
71
29
|
if (name === 'mousemove') {
|
|
72
30
|
if (state.canTransition('MOVE')) {
|
|
73
31
|
const meta = this.getPluginMetadata('treemap:preset-highlight');
|
|
74
|
-
if (!
|
|
75
|
-
meta.highlight?.reset();
|
|
76
|
-
meta.highlight?.update();
|
|
77
|
-
meta.highlight?.setZIndexForHighlight();
|
|
32
|
+
if (!meta) {
|
|
78
33
|
return;
|
|
79
34
|
}
|
|
35
|
+
const oldDirtyRect = meta.lastDirtyRect;
|
|
36
|
+
if (!graphic) {
|
|
37
|
+
if (oldDirtyRect) {
|
|
38
|
+
component.clearOverlay();
|
|
39
|
+
component.updateDirty([
|
|
40
|
+
oldDirtyRect
|
|
41
|
+
]);
|
|
42
|
+
meta.overlayGraphic = null;
|
|
43
|
+
meta.lastDirtyRect = null;
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const module = graphic.__widget__;
|
|
80
48
|
const [x, y, w, h] = module.layout;
|
|
81
|
-
const
|
|
82
|
-
domEvent.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
49
|
+
const rect = graphic.elements[0];
|
|
50
|
+
if (!rect || !domEvent.asserts.isRoundRect(rect)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const effectiveRadius = rect.style.radius;
|
|
54
|
+
// Layout coordinates are already in visual (zoomed) space; matrix.e/f
|
|
55
|
+
// is the pan translation that gets added to every element position.
|
|
56
|
+
const visualX = x + matrix.e;
|
|
57
|
+
const visualY = y + matrix.f;
|
|
58
|
+
// Expand dirty rect by 1 CSS px on each side to cover anti-aliased edges.
|
|
59
|
+
const pad = 1;
|
|
60
|
+
const newDirtyRect = {
|
|
61
|
+
x: visualX - pad,
|
|
62
|
+
y: visualY - pad,
|
|
63
|
+
width: w + pad * 2,
|
|
64
|
+
height: h + pad * 2
|
|
65
|
+
};
|
|
66
|
+
const mask = domEvent.createRoundBlock(visualX, visualY, w, h, {
|
|
67
|
+
fill,
|
|
68
|
+
opacity: HIGH_LIGHT_OPACITY,
|
|
69
|
+
radius: effectiveRadius,
|
|
70
|
+
padding: 0
|
|
97
71
|
});
|
|
72
|
+
component.clearOverlay();
|
|
73
|
+
component.addOverlay(mask);
|
|
74
|
+
meta.overlayGraphic = mask;
|
|
75
|
+
meta.lastDirtyRect = newDirtyRect;
|
|
76
|
+
const dirtyRects = oldDirtyRect ? [
|
|
77
|
+
newDirtyRect,
|
|
78
|
+
oldDirtyRect
|
|
79
|
+
] : [
|
|
80
|
+
newDirtyRect
|
|
81
|
+
];
|
|
82
|
+
component.updateDirty(dirtyRects);
|
|
83
|
+
} else {
|
|
84
|
+
// State changed away from hoverable (e.g. dragging / zooming) — clear overlay.
|
|
85
|
+
const meta = this.getPluginMetadata('treemap:preset-highlight');
|
|
86
|
+
if (meta && meta.lastDirtyRect) {
|
|
87
|
+
component.clearOverlay();
|
|
88
|
+
meta.overlayGraphic = null;
|
|
89
|
+
meta.lastDirtyRect = null;
|
|
90
|
+
}
|
|
98
91
|
}
|
|
99
92
|
}
|
|
100
93
|
},
|
|
@@ -103,21 +96,21 @@ const presetHighlightPlugin = domEvent.definePlugin({
|
|
|
103
96
|
if (!meta) {
|
|
104
97
|
return;
|
|
105
98
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
meta.highlight?.reset();
|
|
110
|
-
meta.highlight?.init();
|
|
99
|
+
this.instance.clearOverlay();
|
|
100
|
+
meta.overlayGraphic = null;
|
|
101
|
+
meta.lastDirtyRect = null;
|
|
111
102
|
},
|
|
112
103
|
onDispose () {
|
|
113
104
|
const meta = this.getPluginMetadata('treemap:preset-highlight');
|
|
114
|
-
if (meta
|
|
115
|
-
|
|
116
|
-
meta.
|
|
105
|
+
if (meta) {
|
|
106
|
+
this.instance.clearOverlay();
|
|
107
|
+
meta.overlayGraphic = null;
|
|
108
|
+
meta.lastDirtyRect = null;
|
|
117
109
|
}
|
|
118
110
|
},
|
|
119
111
|
meta: {
|
|
120
|
-
|
|
112
|
+
overlayGraphic: null,
|
|
113
|
+
lastDirtyRect: null
|
|
121
114
|
}
|
|
122
115
|
});
|
|
123
116
|
|
|
@@ -143,7 +136,6 @@ const presetDragElementPlugin = domEvent.definePlugin({
|
|
|
143
136
|
}
|
|
144
137
|
state.transition('DRAGGING');
|
|
145
138
|
if (state.isInState('DRAGGING')) {
|
|
146
|
-
const highlight = getHighlightInstance.call(this);
|
|
147
139
|
domEvent.smoothFrame((_, cleanup)=>{
|
|
148
140
|
cleanup();
|
|
149
141
|
const { offsetX, offsetY } = event.native;
|
|
@@ -151,18 +143,30 @@ const presetDragElementPlugin = domEvent.definePlugin({
|
|
|
151
143
|
const drawY = offsetY - meta.dragOptions.y;
|
|
152
144
|
const lastX = meta.dragOptions.x;
|
|
153
145
|
const lastY = meta.dragOptions.y;
|
|
154
|
-
|
|
155
|
-
highlight.highlight.reset();
|
|
156
|
-
highlight.highlight.setZIndexForHighlight();
|
|
157
|
-
}
|
|
146
|
+
component.clearOverlay();
|
|
158
147
|
matrix.translation(drawX, drawY);
|
|
159
148
|
meta.dragOptions.x = offsetX;
|
|
160
149
|
meta.dragOptions.y = offsetY;
|
|
161
150
|
meta.dragOptions.lastX = lastX;
|
|
162
151
|
meta.dragOptions.lastY = lastY;
|
|
152
|
+
const cloned = component.elements.map((el)=>domEvent.isBox(el) ? el.clone() : el);
|
|
163
153
|
component.cleanup();
|
|
164
|
-
component.
|
|
165
|
-
domEvent.
|
|
154
|
+
component.add(...cloned);
|
|
155
|
+
domEvent.traverse(component.elements, (graph)=>{
|
|
156
|
+
if (domEvent.isText(graph)) {
|
|
157
|
+
const { textX, textY } = graph.__widget__;
|
|
158
|
+
graph.x = textX;
|
|
159
|
+
graph.y = textY;
|
|
160
|
+
}
|
|
161
|
+
if (domEvent.isRoundRect(graph)) {
|
|
162
|
+
const { x, y, w, h } = graph.__widget__;
|
|
163
|
+
graph.x = x;
|
|
164
|
+
graph.y = y;
|
|
165
|
+
graph.width = w;
|
|
166
|
+
graph.height = h;
|
|
167
|
+
}
|
|
168
|
+
domEvent.stackMatrixTransform(graph, matrix.e, matrix.f, 1);
|
|
169
|
+
});
|
|
166
170
|
component.update();
|
|
167
171
|
return true;
|
|
168
172
|
}, {
|
|
@@ -186,11 +190,7 @@ const presetDragElementPlugin = domEvent.definePlugin({
|
|
|
186
190
|
}
|
|
187
191
|
}
|
|
188
192
|
if (state.isInState('DRAGGING') && state.canTransition('IDLE')) {
|
|
189
|
-
|
|
190
|
-
if (highlight && highlight.highlight) {
|
|
191
|
-
highlight.highlight.reset();
|
|
192
|
-
highlight.highlight.setZIndexForHighlight();
|
|
193
|
-
}
|
|
193
|
+
component.clearOverlay();
|
|
194
194
|
const meta = getDragOptions.call(this);
|
|
195
195
|
if (meta && meta.dragOptions) {
|
|
196
196
|
meta.dragOptions.x = 0;
|
|
@@ -233,9 +233,6 @@ const presetDragElementPlugin = domEvent.definePlugin({
|
|
|
233
233
|
state.reset();
|
|
234
234
|
}
|
|
235
235
|
});
|
|
236
|
-
function getHighlightInstance() {
|
|
237
|
-
return this.getPluginMetadata('treemap:preset-highlight');
|
|
238
|
-
}
|
|
239
236
|
function getDragOptions() {
|
|
240
237
|
const meta = this.getPluginMetadata('treemap:preset-drag-element');
|
|
241
238
|
return meta;
|
|
@@ -259,9 +256,9 @@ function presetMenuPlugin(options) {
|
|
|
259
256
|
return;
|
|
260
257
|
}
|
|
261
258
|
if (options?.onClick) {
|
|
262
|
-
options.onClick(action, domEvent$1.
|
|
263
|
-
|
|
264
|
-
|
|
259
|
+
options.onClick(action, domEvent$1.findRelativeGraphicNode({
|
|
260
|
+
kind: 'click',
|
|
261
|
+
native: e
|
|
265
262
|
}));
|
|
266
263
|
}
|
|
267
264
|
}
|
|
@@ -363,6 +360,15 @@ function adjustColorToComfortableForHumanEye(hue, saturation, lightness) {
|
|
|
363
360
|
};
|
|
364
361
|
}
|
|
365
362
|
|
|
363
|
+
// refer https://developer.mozilla.org/en-US/docs/Web/API/Element/mousewheel_event
|
|
364
|
+
// we shouldn't use wheelDelta property anymore.
|
|
365
|
+
function getScaleOptions() {
|
|
366
|
+
const meta = this.getPluginMetadata('treemap:preset-scale');
|
|
367
|
+
if (!meta) {
|
|
368
|
+
throw new Error('treemap:preset-scale metadata missing; ensure presetScalePlugin is registered');
|
|
369
|
+
}
|
|
370
|
+
return meta;
|
|
371
|
+
}
|
|
366
372
|
function presetScalePlugin(options) {
|
|
367
373
|
return domEvent.definePlugin({
|
|
368
374
|
name: 'treemap:preset-scale',
|
|
@@ -374,9 +380,19 @@ function presetScalePlugin(options) {
|
|
|
374
380
|
meta: {
|
|
375
381
|
scaleOptions: {
|
|
376
382
|
scale: 1,
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
383
|
+
virtualScale: 1,
|
|
384
|
+
minScale: options?.min ?? 0.1,
|
|
385
|
+
maxScale: options?.max ?? Infinity,
|
|
386
|
+
scaleFactor: 0.05,
|
|
387
|
+
springStiffness: options?.springStiffness ?? 300,
|
|
388
|
+
springDamping: options?.springDamping ?? 35,
|
|
389
|
+
overshootResistance: options?.overshootResistance ?? 0.35,
|
|
390
|
+
overshootLimitFactor: options?.overshootLimitFactor ?? 0.05,
|
|
391
|
+
lastAnchorX: undefined,
|
|
392
|
+
lastAnchorY: undefined,
|
|
393
|
+
springRafId: null,
|
|
394
|
+
animationsEnabled: options?.animationsEnabled ?? true,
|
|
395
|
+
wheelDebounce: options?.wheelDebounce ?? 200
|
|
380
396
|
},
|
|
381
397
|
gestureState: {
|
|
382
398
|
isTrackingGesture: false,
|
|
@@ -386,23 +402,19 @@ function presetScalePlugin(options) {
|
|
|
386
402
|
totalDeltaX: 0,
|
|
387
403
|
consecutivePinchEvents: 0,
|
|
388
404
|
gestureType: 'unknown',
|
|
389
|
-
lockGestureType: false
|
|
405
|
+
lockGestureType: false,
|
|
406
|
+
wheelEndTimeoutId: null
|
|
390
407
|
}
|
|
391
408
|
},
|
|
392
409
|
onResize ({ matrix, stateManager: state }) {
|
|
393
410
|
const meta = getScaleOptions.call(this);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
411
|
+
meta.scaleOptions.scale = 1;
|
|
412
|
+
meta.scaleOptions.virtualScale = 1;
|
|
397
413
|
matrix.create(domEvent.DEFAULT_MATRIX_LOC);
|
|
398
414
|
state.reset();
|
|
399
415
|
}
|
|
400
416
|
});
|
|
401
417
|
}
|
|
402
|
-
function getScaleOptions() {
|
|
403
|
-
const meta = this.getPluginMetadata('treemap:preset-scale');
|
|
404
|
-
return meta;
|
|
405
|
-
}
|
|
406
418
|
function determineGestureType(event, gestureState) {
|
|
407
419
|
const now = Date.now();
|
|
408
420
|
const timeDiff = now - gestureState.lastEventTime;
|
|
@@ -429,7 +441,7 @@ function determineGestureType(event, gestureState) {
|
|
|
429
441
|
return 'zoom';
|
|
430
442
|
}
|
|
431
443
|
// windows/macos mouse wheel
|
|
432
|
-
// Usually the
|
|
444
|
+
// Usually the deltaY is large and deltaX maybe 0 or small number.
|
|
433
445
|
const isMouseWheel = Math.abs(event.deltaX) >= 100 && Math.abs(event.deltaX) <= 10 || Math.abs(event.deltaY) > 50 && Math.abs(event.deltaX) < Math.abs(event.deltaY) * 0.1;
|
|
434
446
|
if (isMouseWheel) {
|
|
435
447
|
gestureState.gestureType = 'zoom';
|
|
@@ -460,9 +472,6 @@ function determineGestureType(event, gestureState) {
|
|
|
460
472
|
function onWheel(pluginContext, event, domEvent) {
|
|
461
473
|
event.native.preventDefault();
|
|
462
474
|
const meta = getScaleOptions.call(pluginContext);
|
|
463
|
-
if (!meta) {
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
475
|
const gestureType = determineGestureType(event.native, meta.gestureState);
|
|
467
476
|
if (gestureType === 'zoom') {
|
|
468
477
|
handleZoom(pluginContext, event, domEvent);
|
|
@@ -471,12 +480,8 @@ function onWheel(pluginContext, event, domEvent) {
|
|
|
471
480
|
}
|
|
472
481
|
}
|
|
473
482
|
function updateViewport(pluginContext, { stateManager: state, component, matrix }, useAnimation = false) {
|
|
474
|
-
const highlight = getHighlightInstance.apply(pluginContext);
|
|
475
483
|
const doUpdate = ()=>{
|
|
476
|
-
|
|
477
|
-
highlight.highlight.reset();
|
|
478
|
-
highlight.highlight.setZIndexForHighlight();
|
|
479
|
-
}
|
|
484
|
+
component.clearOverlay();
|
|
480
485
|
component.cleanup();
|
|
481
486
|
const { width, height } = component.render.options;
|
|
482
487
|
component.layoutNodes = component.calculateLayoutNodes(component.data, {
|
|
@@ -504,43 +509,197 @@ function updateViewport(pluginContext, { stateManager: state, component, matrix
|
|
|
504
509
|
doUpdate();
|
|
505
510
|
}
|
|
506
511
|
}
|
|
507
|
-
function
|
|
508
|
-
const
|
|
512
|
+
function cancelSpringAnimationIfAny(meta) {
|
|
513
|
+
const rafId = meta.scaleOptions.springRafId;
|
|
514
|
+
if (typeof rafId === 'number' && rafId !== null) {
|
|
515
|
+
cancelAnimationFrame(rafId);
|
|
516
|
+
meta.scaleOptions.springRafId = null;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function springAnimateToScale(pluginContext, domEvent, targetScale, anchorX, anchorY) {
|
|
509
520
|
const meta = getScaleOptions.call(pluginContext);
|
|
510
|
-
|
|
521
|
+
const { matrix, component } = domEvent;
|
|
522
|
+
// if animations disabled, snap immediately and return
|
|
523
|
+
if (!meta.scaleOptions.animationsEnabled) {
|
|
524
|
+
const oldMatrix = {
|
|
525
|
+
e: matrix.e,
|
|
526
|
+
f: matrix.f
|
|
527
|
+
};
|
|
528
|
+
const finalScaleDiff = targetScale / meta.scaleOptions.scale;
|
|
529
|
+
if (isFinite(finalScaleDiff) && finalScaleDiff > 0 && Math.abs(finalScaleDiff - 1) > 1e-12) {
|
|
530
|
+
matrix.scale(finalScaleDiff, finalScaleDiff);
|
|
531
|
+
matrix.e = anchorX - (anchorX - matrix.e) * finalScaleDiff;
|
|
532
|
+
matrix.f = anchorY - (anchorY - matrix.f) * finalScaleDiff;
|
|
533
|
+
}
|
|
534
|
+
meta.scaleOptions.scale = targetScale;
|
|
535
|
+
meta.scaleOptions.virtualScale = targetScale;
|
|
536
|
+
try {
|
|
537
|
+
component.handleTransformCacheInvalidation(oldMatrix, {
|
|
538
|
+
e: matrix.e,
|
|
539
|
+
f: matrix.f
|
|
540
|
+
});
|
|
541
|
+
} catch {}
|
|
542
|
+
updateViewport(pluginContext, domEvent, false);
|
|
511
543
|
return;
|
|
512
544
|
}
|
|
513
|
-
|
|
545
|
+
cancelSpringAnimationIfAny(meta);
|
|
546
|
+
const stiffness = meta.scaleOptions.springStiffness ?? 300;
|
|
547
|
+
const damping = meta.scaleOptions.springDamping ?? 35;
|
|
548
|
+
let position = meta.scaleOptions.scale;
|
|
549
|
+
let velocity = 0;
|
|
550
|
+
let lastTime = performance.now();
|
|
551
|
+
const thresholdPos = Math.max(1e-4, Math.abs(targetScale) * 1e-3);
|
|
552
|
+
const thresholdVel = 1e-3;
|
|
553
|
+
const oldMatrix = {
|
|
554
|
+
e: matrix.e,
|
|
555
|
+
f: matrix.f
|
|
556
|
+
};
|
|
557
|
+
function step(now) {
|
|
558
|
+
const dt = Math.min((now - lastTime) / 1000, 0.033);
|
|
559
|
+
lastTime = now;
|
|
560
|
+
const force = stiffness * (targetScale - position);
|
|
561
|
+
const accel = force - damping * velocity;
|
|
562
|
+
velocity += accel * dt;
|
|
563
|
+
const prev = position;
|
|
564
|
+
position += velocity * dt;
|
|
565
|
+
const scaleDiff = position / prev;
|
|
566
|
+
if (isFinite(scaleDiff) && scaleDiff > 0) {
|
|
567
|
+
matrix.scale(scaleDiff, scaleDiff);
|
|
568
|
+
matrix.e = anchorX - (anchorX - matrix.e) * scaleDiff;
|
|
569
|
+
matrix.f = anchorY - (anchorY - matrix.f) * scaleDiff;
|
|
570
|
+
meta.scaleOptions.scale = position;
|
|
571
|
+
meta.scaleOptions.virtualScale = position;
|
|
572
|
+
updateViewport(pluginContext, domEvent, false);
|
|
573
|
+
}
|
|
574
|
+
const isSettled = Math.abs(targetScale - position) <= thresholdPos && Math.abs(velocity) <= thresholdVel;
|
|
575
|
+
if (isSettled) {
|
|
576
|
+
const finalScaleDiff = targetScale / meta.scaleOptions.scale;
|
|
577
|
+
if (isFinite(finalScaleDiff) && finalScaleDiff > 0 && Math.abs(finalScaleDiff - 1) > 1e-12) {
|
|
578
|
+
matrix.scale(finalScaleDiff, finalScaleDiff);
|
|
579
|
+
matrix.e = anchorX - (anchorX - matrix.e) * finalScaleDiff;
|
|
580
|
+
matrix.f = anchorY - (anchorY - matrix.f) * finalScaleDiff;
|
|
581
|
+
}
|
|
582
|
+
meta.scaleOptions.scale = targetScale;
|
|
583
|
+
meta.scaleOptions.virtualScale = targetScale;
|
|
584
|
+
try {
|
|
585
|
+
component.handleTransformCacheInvalidation(oldMatrix, {
|
|
586
|
+
e: matrix.e,
|
|
587
|
+
f: matrix.f
|
|
588
|
+
});
|
|
589
|
+
} catch {}
|
|
590
|
+
updateViewport(pluginContext, domEvent, false);
|
|
591
|
+
meta.scaleOptions.springRafId = null;
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
meta.scaleOptions.springRafId = requestAnimationFrame(step);
|
|
595
|
+
}
|
|
596
|
+
meta.scaleOptions.springRafId = requestAnimationFrame(step);
|
|
597
|
+
}
|
|
598
|
+
function handleWheelEnd(pluginContext, domEvent) {
|
|
599
|
+
const meta = getScaleOptions.call(pluginContext);
|
|
600
|
+
const { scale, minScale, maxScale } = meta.scaleOptions;
|
|
601
|
+
const eps = 1e-6;
|
|
602
|
+
if (scale + eps < minScale) {
|
|
603
|
+
const target = minScale;
|
|
604
|
+
const anchorX = meta.scaleOptions.lastAnchorX ?? domEvent.component.render.options.width / 2;
|
|
605
|
+
const anchorY = meta.scaleOptions.lastAnchorY ?? domEvent.component.render.options.height / 2;
|
|
606
|
+
springAnimateToScale(pluginContext, domEvent, target, anchorX, anchorY);
|
|
607
|
+
} else if (scale - eps > maxScale) {
|
|
608
|
+
const target = maxScale;
|
|
609
|
+
const anchorX = meta.scaleOptions.lastAnchorX ?? domEvent.component.render.options.width / 2;
|
|
610
|
+
const anchorY = meta.scaleOptions.lastAnchorY ?? domEvent.component.render.options.height / 2;
|
|
611
|
+
springAnimateToScale(pluginContext, domEvent, target, anchorX, anchorY);
|
|
612
|
+
} else {
|
|
613
|
+
// inside bounds: sync virtualScale to visible scale to avoid sudden jumps later
|
|
614
|
+
meta.scaleOptions.virtualScale = meta.scaleOptions.scale;
|
|
615
|
+
if (Math.abs(scale - minScale) < 1e-8) {
|
|
616
|
+
meta.scaleOptions.scale = minScale;
|
|
617
|
+
}
|
|
618
|
+
if (Math.abs(scale - maxScale) < 1e-8) {
|
|
619
|
+
meta.scaleOptions.scale = maxScale;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function handleZoom(pluginContext, event, domEvent) {
|
|
624
|
+
const { stateManager: state, matrix, component } = domEvent;
|
|
625
|
+
const meta = getScaleOptions.call(pluginContext);
|
|
626
|
+
// read currentVisible and currentVirtual separately to avoid destructuring-default warnings
|
|
627
|
+
const currentVisible = meta.scaleOptions.scale;
|
|
628
|
+
const prevVirtualRaw = meta.scaleOptions.virtualScale ?? currentVisible;
|
|
629
|
+
const minScale = meta.scaleOptions.minScale;
|
|
630
|
+
const maxScale = meta.scaleOptions.maxScale;
|
|
631
|
+
const scaleFactor = meta.scaleOptions.scaleFactor;
|
|
632
|
+
const overshootResistance = meta.scaleOptions.overshootResistance ?? 0.35;
|
|
633
|
+
const overshootLimitFactor = meta.scaleOptions.overshootLimitFactor ?? 0.05;
|
|
634
|
+
cancelSpringAnimationIfAny(meta);
|
|
514
635
|
const oldMatrix = {
|
|
515
636
|
e: matrix.e,
|
|
516
637
|
f: matrix.f
|
|
517
638
|
};
|
|
518
|
-
const dynamicScaleFactor = Math.max(scaleFactor,
|
|
639
|
+
const dynamicScaleFactor = Math.max(scaleFactor, currentVisible * 0.1);
|
|
519
640
|
const delta = event.native.deltaY < 0 ? dynamicScaleFactor : -dynamicScaleFactor;
|
|
520
|
-
|
|
521
|
-
|
|
641
|
+
let newVirtual = prevVirtualRaw + delta;
|
|
642
|
+
let newVisible;
|
|
643
|
+
if (newVirtual >= minScale && newVirtual <= maxScale) {
|
|
644
|
+
newVisible = newVirtual;
|
|
645
|
+
} else if (newVirtual < minScale) {
|
|
646
|
+
newVisible = minScale + (newVirtual - minScale) * overshootResistance;
|
|
647
|
+
const lowerBound = Math.max(0, minScale * overshootLimitFactor);
|
|
648
|
+
if (newVisible < lowerBound) {
|
|
649
|
+
newVisible = lowerBound;
|
|
650
|
+
// sync virtual so further moves are consistent with clamped visible value
|
|
651
|
+
newVirtual = minScale + (newVisible - minScale) / Math.max(1e-6, overshootResistance);
|
|
652
|
+
}
|
|
653
|
+
} else {
|
|
654
|
+
newVisible = maxScale + (newVirtual - maxScale) * overshootResistance;
|
|
655
|
+
const upperBound = maxScale * (1 + Math.max(overshootLimitFactor, 0.05));
|
|
656
|
+
if (newVisible > upperBound) {
|
|
657
|
+
newVisible = upperBound;
|
|
658
|
+
newVirtual = maxScale + (newVisible - maxScale) / Math.max(1e-6, overshootResistance);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
const prevVisible = currentVisible;
|
|
662
|
+
if (newVisible === prevVisible) {
|
|
663
|
+
meta.scaleOptions.virtualScale = newVirtual;
|
|
522
664
|
return;
|
|
523
665
|
}
|
|
524
666
|
state.transition('SCALING');
|
|
525
667
|
const mouseX = event.native.offsetX;
|
|
526
668
|
const mouseY = event.native.offsetY;
|
|
527
|
-
|
|
528
|
-
meta.scaleOptions.
|
|
669
|
+
// remember anchor for later spring animation
|
|
670
|
+
meta.scaleOptions.lastAnchorX = mouseX;
|
|
671
|
+
meta.scaleOptions.lastAnchorY = mouseY;
|
|
672
|
+
const scaleDiff = newVisible / prevVisible;
|
|
673
|
+
meta.scaleOptions.virtualScale = newVirtual;
|
|
674
|
+
meta.scaleOptions.scale = newVisible;
|
|
529
675
|
matrix.scale(scaleDiff, scaleDiff);
|
|
530
676
|
matrix.e = mouseX - (mouseX - matrix.e) * scaleDiff;
|
|
531
677
|
matrix.f = mouseY - (mouseY - matrix.f) * scaleDiff;
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
678
|
+
try {
|
|
679
|
+
component.handleTransformCacheInvalidation(oldMatrix, {
|
|
680
|
+
e: matrix.e,
|
|
681
|
+
f: matrix.f
|
|
682
|
+
});
|
|
683
|
+
} catch {}
|
|
537
684
|
updateViewport(pluginContext, domEvent, false);
|
|
685
|
+
const g = meta.gestureState;
|
|
686
|
+
if (g.wheelEndTimeoutId) {
|
|
687
|
+
clearTimeout(g.wheelEndTimeoutId);
|
|
688
|
+
g.wheelEndTimeoutId = null;
|
|
689
|
+
}
|
|
690
|
+
const debounceMs = meta.scaleOptions.wheelDebounce ?? 200;
|
|
691
|
+
g.wheelEndTimeoutId = window.setTimeout(()=>{
|
|
692
|
+
g.wheelEndTimeoutId = null;
|
|
693
|
+
handleWheelEnd(pluginContext, domEvent);
|
|
694
|
+
}, debounceMs);
|
|
538
695
|
}
|
|
539
696
|
function handlePan(pluginContext, event, domEvent) {
|
|
540
697
|
const { stateManager: state, matrix } = domEvent;
|
|
541
698
|
const panSpeed = 0.8;
|
|
542
699
|
const deltaX = event.native.deltaX * panSpeed;
|
|
543
700
|
const deltaY = event.native.deltaY * panSpeed;
|
|
701
|
+
const meta = getScaleOptions.call(pluginContext);
|
|
702
|
+
cancelSpringAnimationIfAny(meta);
|
|
544
703
|
state.transition('PANNING');
|
|
545
704
|
matrix.e -= deltaX;
|
|
546
705
|
matrix.f -= deltaY;
|
|
@@ -597,7 +756,6 @@ const presetZoomablePlugin = domEvent.definePlugin({
|
|
|
597
756
|
if (scaleMeta) {
|
|
598
757
|
scaleMeta.scaleOptions.scale = targetScale;
|
|
599
758
|
}
|
|
600
|
-
const highlight = getHighlightInstance.call(this);
|
|
601
759
|
const dragMeta = getDragOptions.call(this);
|
|
602
760
|
if (dragMeta) {
|
|
603
761
|
Object.assign(dragMeta.dragOptions, {
|
|
@@ -619,16 +777,13 @@ const presetZoomablePlugin = domEvent.definePlugin({
|
|
|
619
777
|
};
|
|
620
778
|
component.handleTransformCacheInvalidation(oldMatrix, finalMatrix);
|
|
621
779
|
domEvent.smoothFrame((progress)=>{
|
|
622
|
-
const easedProgress = easing.cubicInOut(progress);
|
|
780
|
+
const easedProgress = domEvent.easing.cubicInOut(progress);
|
|
623
781
|
matrix.create(domEvent.DEFAULT_MATRIX_LOC);
|
|
624
782
|
matrix.e = startMatrix.e + (targetE - startMatrix.e) * easedProgress;
|
|
625
783
|
matrix.f = startMatrix.f + (targetF - startMatrix.f) * easedProgress;
|
|
626
784
|
matrix.a = startMatrix.a + (targetScale - startMatrix.a) * easedProgress;
|
|
627
785
|
matrix.d = startMatrix.d + (targetScale - startMatrix.d) * easedProgress;
|
|
628
|
-
|
|
629
|
-
highlight.highlight.reset();
|
|
630
|
-
highlight.highlight.setZIndexForHighlight();
|
|
631
|
-
}
|
|
786
|
+
component.clearOverlay();
|
|
632
787
|
component.cleanup();
|
|
633
788
|
component.layoutNodes = component.calculateLayoutNodes(component.data, {
|
|
634
789
|
w: width * matrix.a,
|