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