marquee-selection 0.0.11

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.
@@ -0,0 +1,215 @@
1
+ /**
2
+ * InfiniteCanvas
3
+ *
4
+ * 在指定 HTML 容器内启用双指缩放与双指平移(触摸手势)。
5
+ * 会将容器现有子节点搬运到一个内部 content 容器中,并对该 content 施加 CSS 变换。
6
+ */
7
+ type InfiniteCanvasOptions = {
8
+ container: HTMLElement;
9
+ initialScale?: number;
10
+ minScale?: number;
11
+ maxScale?: number;
12
+ initialTranslate?: {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ clampOverflowHidden?: boolean;
17
+ contentClassName?: string;
18
+ autoFit?: boolean | {
19
+ mode?: "contain" | "width" | "height" | "cover";
20
+ alignX?: "left" | "center" | "right";
21
+ alignY?: "top" | "center" | "bottom";
22
+ padding?: number;
23
+ };
24
+ autoRefitOnResize?: boolean;
25
+ onReady?: (content: HTMLDivElement) => void;
26
+ onTransform?: (state: {
27
+ scale: number;
28
+ x: number;
29
+ y: number;
30
+ content: HTMLDivElement;
31
+ }) => void;
32
+ onTransformStart?: (state: {
33
+ scale: number;
34
+ x: number;
35
+ y: number;
36
+ content: HTMLDivElement;
37
+ }) => void;
38
+ onTransformEnd?: (state: {
39
+ scale: number;
40
+ x: number;
41
+ y: number;
42
+ content: HTMLDivElement;
43
+ }) => void;
44
+ };
45
+ declare class InfiniteCanvas {
46
+ private container;
47
+ private content;
48
+ private scale;
49
+ private tx;
50
+ private ty;
51
+ private minScale;
52
+ private maxScale;
53
+ private destroyed;
54
+ private onTransformCb?;
55
+ private onTransformStartCb?;
56
+ private onTransformEndCb?;
57
+ private transforming;
58
+ private transformEndTimer;
59
+ private refitTimer;
60
+ private autoFitPreset;
61
+ private resizeObserver?;
62
+ private resizeHandler?;
63
+ private hitLayer;
64
+ private gesture;
65
+ private inContainerGesture;
66
+ private onWindowTouchStart?;
67
+ private onWindowTouchMove?;
68
+ private onWindowTouchEnd?;
69
+ private onWindowWheel?;
70
+ constructor(options: InfiniteCanvasOptions);
71
+ destroy(): void;
72
+ getTransform(): {
73
+ scale: number;
74
+ x: number;
75
+ y: number;
76
+ };
77
+ setTransform(t: {
78
+ scale?: number;
79
+ x?: number;
80
+ y?: number;
81
+ }): void;
82
+ reset(): void;
83
+ private clampScale;
84
+ private normalizeFitParams;
85
+ computeFitTransform(params?: {
86
+ mode?: "contain" | "width" | "height" | "cover";
87
+ alignX?: "left" | "center" | "right";
88
+ alignY?: "top" | "center" | "bottom";
89
+ padding?: number;
90
+ }): {
91
+ scale: number;
92
+ x: number;
93
+ y: number;
94
+ } | null;
95
+ fitToView(params?: {
96
+ mode?: "contain" | "width" | "height" | "cover";
97
+ alignX?: "left" | "center" | "right";
98
+ alignY?: "top" | "center" | "bottom";
99
+ padding?: number;
100
+ }): void;
101
+ private _noop;
102
+ private _isPointInContainer;
103
+ private _windowTouchStart;
104
+ private _windowTouchMove;
105
+ private _windowTouchEnd;
106
+ private _windowWheel;
107
+ private applyTransform;
108
+ private getTouches;
109
+ private midpoint;
110
+ private distance;
111
+ private onTouchStart;
112
+ private onTouchMove;
113
+ private onTouchEnd;
114
+ private onWheel;
115
+ private onGesture;
116
+ }
117
+
118
+ type MarqueeSelectionOptions = {
119
+ container: HTMLElement;
120
+ selectable?: string;
121
+ exclude?: string | string[];
122
+ selectionMode?: "intersects" | "contains" | "center";
123
+ minOverlapRatio?: number;
124
+ overlapMetric?: "element" | "iou";
125
+ selectedClass?: string;
126
+ onChange?: (payload: {
127
+ type: "single";
128
+ selected: Element[];
129
+ } | {
130
+ type: "groups";
131
+ groups: Element[][];
132
+ flat: Element[];
133
+ }) => void;
134
+ preventAncestorSelection?: boolean;
135
+ conflictStrategy?: "none" | "leaf" | "best";
136
+ multi?: boolean;
137
+ combineMode?: "replace" | "add" | "subtract" | "toggle" | "auto";
138
+ groupMode?: boolean;
139
+ groupOverlayClass?: string;
140
+ groupColor?: string;
141
+ groupRandomColor?: boolean;
142
+ groupColorPalette?: string[];
143
+ hoverHighlight?: boolean;
144
+ hoverClass?: string;
145
+ onSelectionEnd?: (snapshot: MarqueeSelectionSnapshot) => void;
146
+ quickGroupOnDblClick?: boolean;
147
+ quickGroupSelector?: string;
148
+ allowIntersectionSelection?: boolean;
149
+ allowContainmentSelection?: boolean;
150
+ allowUnionSelection?: boolean;
151
+ toolbarButtons?: Array<{
152
+ label: string;
153
+ title?: string;
154
+ className?: string;
155
+ onClick?: (ctx: {
156
+ index: number;
157
+ group: Element[];
158
+ controller: MarqueeSelectionController;
159
+ getSnapshot: () => MarqueeSelectionSnapshot;
160
+ refresh: () => void;
161
+ mouseX?: number;
162
+ mouseY?: number;
163
+ anchorRect?: DOMRect;
164
+ anchorEl?: HTMLElement;
165
+ overlayEl?: HTMLElement;
166
+ }) => void;
167
+ }>;
168
+ };
169
+ type MarqueeSelectionSnapshot = {
170
+ type: "single";
171
+ selected: Element[];
172
+ } | {
173
+ type: "groups";
174
+ groups: Element[][];
175
+ flat: Element[];
176
+ hidden: boolean[];
177
+ groupRects: ({
178
+ left: number;
179
+ top: number;
180
+ width: number;
181
+ height: number;
182
+ } | null)[];
183
+ groupNesting: {
184
+ parents: (number | null)[];
185
+ children: number[][];
186
+ roots: number[];
187
+ };
188
+ };
189
+ type MarqueeSelectionController = {
190
+ destroy: () => void;
191
+ getGroups: () => Element[][];
192
+ clearGroups: () => void;
193
+ removeGroup: (index: number) => boolean;
194
+ addGroup: (elements: Element[]) => boolean;
195
+ setGroupVisibility: (index: number, hidden: boolean) => boolean;
196
+ toggleGroupVisibility: (index: number) => boolean;
197
+ getSelectionResult: () => MarqueeSelectionSnapshot;
198
+ refresh: () => void;
199
+ hideOverlays: () => void;
200
+ showAndRefreshOverlays: () => void;
201
+ onSelectionEnd: (handler: (snapshot: MarqueeSelectionSnapshot) => void) => () => void;
202
+ waitForSelectionEnd: () => Promise<MarqueeSelectionSnapshot>;
203
+ getGroupNesting: () => {
204
+ parents: (number | null)[];
205
+ children: number[][];
206
+ roots: number[];
207
+ } | null;
208
+ };
209
+ /**
210
+ * 启用容器内矩形拖拽圈选
211
+ */
212
+ declare function marqueeSelection(options: MarqueeSelectionOptions): MarqueeSelectionController;
213
+
214
+ export { InfiniteCanvas, marqueeSelection };
215
+ export type { InfiniteCanvasOptions, MarqueeSelectionOptions };
@@ -0,0 +1,475 @@
1
+ class G {
2
+ constructor(t) {
3
+ this.scale = 1, this.tx = 0, this.ty = 0, this.destroyed = !1, this.transforming = !1, this.transformEndTimer = null, this.refitTimer = null, this.autoFitPreset = null, this.gesture = null, this.inContainerGesture = !1;
4
+ const {
5
+ container: e,
6
+ initialScale: i = 1,
7
+ minScale: n = 0.2,
8
+ maxScale: s = 6,
9
+ initialTranslate: r = { x: 0, y: 0 },
10
+ clampOverflowHidden: h = !0,
11
+ contentClassName: o,
12
+ autoFit: a,
13
+ autoRefitOnResize: f = !1,
14
+ onReady: y,
15
+ onTransform: p,
16
+ onTransformStart: w,
17
+ onTransformEnd: x
18
+ } = t;
19
+ if (!e) throw new Error("InfiniteCanvas: container is required");
20
+ this.container = e, this.scale = i, this.tx = r.x, this.ty = r.y, this.minScale = Math.min(n, s), this.maxScale = Math.max(n, s), this.onTransformCb = p, this.onTransformStartCb = w, this.onTransformEndCb = x;
21
+ const c = document.createElement("div");
22
+ c.style.position = "relative", c.style.transformOrigin = "0 0", c.style.willChange = "transform", o && (c.className = o);
23
+ const T = [];
24
+ for (; e.firstChild; )
25
+ T.push(e.firstChild), e.removeChild(e.firstChild);
26
+ if (T.forEach((d) => c.appendChild(d)), e.appendChild(c), this.content = c, e.style.touchAction = "none", e.style.overscrollBehavior = e.style.overscrollBehavior || "none", h && (e.style.overflow || (e.style.overflow = "hidden")), a) {
27
+ const d = this.normalizeFitParams(a), l = this.computeFitTransform(d);
28
+ l && (this.scale = this.clampScale(l.scale), this.tx = l.x, this.ty = l.y, this.autoFitPreset = d);
29
+ }
30
+ if (this.applyTransform(), this.autoFitPreset && f) {
31
+ const d = () => {
32
+ this.refitTimer && clearTimeout(this.refitTimer), this.refitTimer = setTimeout(() => {
33
+ this.fitToView(this.autoFitPreset || void 0);
34
+ }, 100);
35
+ };
36
+ try {
37
+ this.resizeObserver = new ResizeObserver(() => d()), this.resizeObserver.observe(this.container);
38
+ } catch {
39
+ }
40
+ this.resizeHandler = d, window.addEventListener("resize", this.resizeHandler);
41
+ }
42
+ this.onTouchStart = this.onTouchStart.bind(this), this.onTouchMove = this.onTouchMove.bind(this), this.onTouchEnd = this.onTouchEnd.bind(this), this.onWheel = this.onWheel.bind(this), this.onGesture = this.onGesture.bind(this), this.onWindowTouchStart = this._windowTouchStart.bind(this), this.onWindowTouchMove = this._windowTouchMove.bind(this), this.onWindowTouchEnd = this._windowTouchEnd.bind(this), this.onWindowWheel = this._windowWheel.bind(this);
43
+ try {
44
+ getComputedStyle(e).position === "static" && (e.style.position = "relative");
45
+ } catch {
46
+ }
47
+ if (window.addEventListener(
48
+ "touchstart",
49
+ this.onWindowTouchStart,
50
+ {
51
+ passive: !1,
52
+ capture: !0
53
+ }
54
+ ), window.addEventListener(
55
+ "touchmove",
56
+ this.onWindowTouchMove,
57
+ {
58
+ passive: !1,
59
+ capture: !0
60
+ }
61
+ ), window.addEventListener(
62
+ "touchend",
63
+ this.onWindowTouchEnd,
64
+ {
65
+ passive: !1,
66
+ capture: !0
67
+ }
68
+ ), window.addEventListener(
69
+ "touchcancel",
70
+ this.onWindowTouchEnd,
71
+ {
72
+ passive: !1,
73
+ capture: !0
74
+ }
75
+ ), window.addEventListener(
76
+ "wheel",
77
+ this.onWindowWheel,
78
+ {
79
+ passive: !1,
80
+ capture: !0
81
+ }
82
+ ), window.addEventListener(
83
+ "gesturestart",
84
+ this.onGesture,
85
+ { passive: !1, capture: !0 }
86
+ ), window.addEventListener(
87
+ "gesturechange",
88
+ this.onGesture,
89
+ { passive: !1, capture: !0 }
90
+ ), window.addEventListener(
91
+ "gestureend",
92
+ this.onGesture,
93
+ { passive: !1, capture: !0 }
94
+ ), typeof y == "function")
95
+ try {
96
+ y(this.content);
97
+ } catch {
98
+ }
99
+ }
100
+ // 对外 API
101
+ destroy() {
102
+ if (!this.destroyed) {
103
+ if (this.destroyed = !0, this.container, window.removeEventListener(
104
+ "touchstart",
105
+ this.onWindowTouchStart,
106
+ !0
107
+ ), window.removeEventListener(
108
+ "touchmove",
109
+ this.onWindowTouchMove,
110
+ !0
111
+ ), window.removeEventListener(
112
+ "touchend",
113
+ this.onWindowTouchEnd,
114
+ !0
115
+ ), window.removeEventListener(
116
+ "touchcancel",
117
+ this.onWindowTouchEnd,
118
+ !0
119
+ ), window.removeEventListener(
120
+ "wheel",
121
+ this.onWindowWheel,
122
+ !0
123
+ ), window.removeEventListener(
124
+ "gesturestart",
125
+ this.onGesture,
126
+ !0
127
+ ), window.removeEventListener(
128
+ "gesturechange",
129
+ this.onGesture,
130
+ !0
131
+ ), window.removeEventListener(
132
+ "gestureend",
133
+ this.onGesture,
134
+ !0
135
+ ), this.resizeObserver) {
136
+ try {
137
+ this.resizeObserver.disconnect();
138
+ } catch {
139
+ }
140
+ this.resizeObserver = void 0;
141
+ }
142
+ this.resizeHandler && (window.removeEventListener("resize", this.resizeHandler), this.resizeHandler = void 0);
143
+ }
144
+ }
145
+ getTransform() {
146
+ return { scale: this.scale, x: this.tx, y: this.ty };
147
+ }
148
+ setTransform(t) {
149
+ typeof t.scale == "number" && (this.scale = this.clampScale(t.scale)), typeof t.x == "number" && (this.tx = t.x), typeof t.y == "number" && (this.ty = t.y), this.applyTransform();
150
+ }
151
+ reset() {
152
+ this.scale = 1, this.tx = 0, this.ty = 0, this.applyTransform();
153
+ }
154
+ // 内部工具
155
+ clampScale(t) {
156
+ return Math.min(this.maxScale, Math.max(this.minScale, t));
157
+ }
158
+ normalizeFitParams(t) {
159
+ const e = typeof t == "object" ? t : {
160
+ mode: "contain",
161
+ alignX: "center",
162
+ alignY: "center",
163
+ padding: 0
164
+ };
165
+ return {
166
+ mode: e.mode || "contain",
167
+ alignX: e.alignX || "center",
168
+ alignY: e.alignY || "center",
169
+ padding: Math.max(0, e.padding ?? 0)
170
+ };
171
+ }
172
+ // 计算使内容按给定策略适配到容器可见区域的变换(不应用)
173
+ computeFitTransform(t) {
174
+ const e = this.container, i = this.content, n = Math.max(0, e.clientWidth), s = Math.max(0, e.clientHeight);
175
+ if (n === 0 || s === 0) return null;
176
+ const r = i.getBoundingClientRect(), h = this.scale || 1;
177
+ let o = Number.POSITIVE_INFINITY, a = Number.POSITIVE_INFINITY, f = Number.NEGATIVE_INFINITY, y = Number.NEGATIVE_INFINITY;
178
+ const p = Array.from(i.querySelectorAll("*"));
179
+ for (const g of p) {
180
+ const m = g.getBoundingClientRect();
181
+ if (m.width <= 0 || m.height <= 0) continue;
182
+ const W = (m.left - r.left) / h, I = (m.top - r.top) / h, Y = (m.right - r.left) / h, F = (m.bottom - r.top) / h;
183
+ W < o && (o = W), I < a && (a = I), Y > f && (f = Y), F > y && (y = F);
184
+ }
185
+ if (!isFinite(o) || !isFinite(a) || !isFinite(f) || !isFinite(y)) {
186
+ const g = Math.max(
187
+ i.scrollWidth,
188
+ i.offsetWidth,
189
+ i.clientWidth
190
+ ), m = Math.max(
191
+ i.scrollHeight,
192
+ i.offsetHeight,
193
+ i.clientHeight
194
+ );
195
+ if (g === 0 || m === 0) return null;
196
+ o = 0, a = 0, f = g, y = m;
197
+ }
198
+ const w = Math.max(0, f - o), x = Math.max(0, y - a), c = (t == null ? void 0 : t.mode) ?? "contain", T = (t == null ? void 0 : t.alignX) ?? "center", d = (t == null ? void 0 : t.alignY) ?? "center", l = Math.max(0, (t == null ? void 0 : t.padding) ?? 0), L = Math.max(0, n - l * 2), X = Math.max(0, s - l * 2), v = L / w, E = X / x;
199
+ let u = 1;
200
+ c === "width" ? u = v : c === "height" ? u = E : c === "cover" ? u = Math.max(v, E) : u = Math.min(v, E), u = this.clampScale(u);
201
+ const S = w * u, M = x * u;
202
+ let b = l, C = l;
203
+ T === "center" ? b = Math.round((n - S) / 2) : T === "right" && (b = Math.round(n - S - l)), d === "center" ? C = Math.round((s - M) / 2) : d === "bottom" && (C = Math.round(s - M - l));
204
+ const _ = b - Math.round(o * u), z = C - Math.round(a * u);
205
+ return { scale: u, x: _, y: z };
206
+ }
207
+ // 应用适配(基于 computeFitTransform 计算结果)
208
+ fitToView(t) {
209
+ const e = this.normalizeFitParams(t || { mode: "contain" }), i = this.computeFitTransform(e);
210
+ i && (this.scale = this.clampScale(i.scale), this.tx = i.x, this.ty = i.y, this.autoFitPreset = e, this.applyTransform());
211
+ }
212
+ // 仅用于移除 window.resize 监听占位(便于彻底清理)
213
+ _noop() {
214
+ }
215
+ // 命中检测:点是否在容器可见区域内
216
+ _isPointInContainer(t, e) {
217
+ const i = this.container.getBoundingClientRect();
218
+ return t >= i.left && t <= i.right && e >= i.top && e <= i.bottom;
219
+ }
220
+ // 窗口级触摸/滚轮捕获:仅当命中容器时转发
221
+ _windowTouchStart(t) {
222
+ if (t.touches.length === 0) return;
223
+ let e = !1;
224
+ for (let i = 0; i < t.touches.length; i++) {
225
+ const n = t.touches.item ? t.touches.item(i) : t.touches[i];
226
+ if (n && this._isPointInContainer(n.clientX, n.clientY)) {
227
+ e = !0;
228
+ break;
229
+ }
230
+ }
231
+ e && this.onTouchStart(t);
232
+ }
233
+ _windowTouchMove(t) {
234
+ if (t.touches.length === 0) return;
235
+ let e = !1;
236
+ for (let i = 0; i < t.touches.length; i++) {
237
+ const n = t.touches.item ? t.touches.item(i) : t.touches[i];
238
+ if (n && this._isPointInContainer(n.clientX, n.clientY)) {
239
+ e = !0;
240
+ break;
241
+ }
242
+ }
243
+ e && this.onTouchMove(t);
244
+ }
245
+ _windowTouchEnd(t) {
246
+ const e = t.changedTouches;
247
+ if (e && e.length > 0) {
248
+ const i = e.item ? e.item(0) : e[0];
249
+ if (i && !this._isPointInContainer(i.clientX, i.clientY)) return;
250
+ }
251
+ this.onTouchEnd(t);
252
+ }
253
+ _windowWheel(t) {
254
+ this._isPointInContainer(t.clientX, t.clientY) && this.onWheel(t);
255
+ }
256
+ applyTransform() {
257
+ if (this.content.style.transform = `translate(${this.tx}px, ${this.ty}px) scale(${this.scale})`, this.onTransformCb)
258
+ try {
259
+ this.onTransformCb({
260
+ scale: this.scale,
261
+ x: this.tx,
262
+ y: this.ty,
263
+ content: this.content
264
+ });
265
+ } catch {
266
+ }
267
+ try {
268
+ const t = new CustomEvent("infiniteCanvas:transform", {
269
+ detail: {
270
+ scale: this.scale,
271
+ x: this.tx,
272
+ y: this.ty,
273
+ content: this.content
274
+ },
275
+ bubbles: !0
276
+ });
277
+ this.container.dispatchEvent(t);
278
+ } catch {
279
+ }
280
+ this.transforming && (this.transformEndTimer && clearTimeout(this.transformEndTimer), this.transformEndTimer = setTimeout(() => {
281
+ this.transforming = !1;
282
+ try {
283
+ const t = new CustomEvent("infiniteCanvas:transformEnd", {
284
+ detail: {
285
+ scale: this.scale,
286
+ x: this.tx,
287
+ y: this.ty,
288
+ content: this.content
289
+ },
290
+ bubbles: !0
291
+ });
292
+ this.container.dispatchEvent(t);
293
+ } catch {
294
+ }
295
+ if (this.onTransformEndCb)
296
+ try {
297
+ this.onTransformEndCb({
298
+ scale: this.scale,
299
+ x: this.tx,
300
+ y: this.ty,
301
+ content: this.content
302
+ });
303
+ } catch {
304
+ }
305
+ }, 120));
306
+ }
307
+ getTouches(t) {
308
+ const e = t.touches, i = [];
309
+ for (let n = 0; n < e.length; n++) {
310
+ const s = e.item ? e.item(n) : e[n];
311
+ s && i.push({ x: s.clientX, y: s.clientY });
312
+ }
313
+ return i;
314
+ }
315
+ midpoint(t, e) {
316
+ return { x: (t.x + e.x) / 2, y: (t.y + e.y) / 2 };
317
+ }
318
+ distance(t, e) {
319
+ const i = t.x - e.x, n = t.y - e.y;
320
+ return Math.hypot(i, n);
321
+ }
322
+ onTouchStart(t) {
323
+ if (t.touches.length >= 2) {
324
+ if (t.preventDefault(), this.inContainerGesture = !0, !this.transforming) {
325
+ this.transforming = !0;
326
+ try {
327
+ const a = new CustomEvent("infiniteCanvas:transformStart", {
328
+ detail: {
329
+ scale: this.scale,
330
+ x: this.tx,
331
+ y: this.ty,
332
+ content: this.content
333
+ },
334
+ bubbles: !0
335
+ });
336
+ this.container.dispatchEvent(a);
337
+ } catch {
338
+ }
339
+ if (this.onTransformStartCb)
340
+ try {
341
+ this.onTransformStartCb({
342
+ scale: this.scale,
343
+ x: this.tx,
344
+ y: this.ty,
345
+ content: this.content
346
+ });
347
+ } catch {
348
+ }
349
+ }
350
+ const e = this.getTouches(t);
351
+ if (e.length < 2) return;
352
+ const i = e[0], n = e[1], s = this.midpoint(i, n), r = this.distance(i, n), h = (s.x - this.tx) / this.scale, o = (s.y - this.ty) / this.scale;
353
+ this.gesture = {
354
+ startMid: s,
355
+ startDist: Math.max(1, r),
356
+ startScale: this.scale,
357
+ startTx: this.tx,
358
+ startTy: this.ty,
359
+ worldAtMid: { x: h, y: o }
360
+ };
361
+ }
362
+ }
363
+ onTouchMove(t) {
364
+ if (!this.gesture) {
365
+ t.touches.length >= 2 && this.onTouchStart(t);
366
+ return;
367
+ }
368
+ if (t.touches.length < 2) return;
369
+ t.preventDefault();
370
+ const e = this.getTouches(t);
371
+ if (e.length < 2) return;
372
+ const i = e[0], n = e[1], s = this.midpoint(i, n), r = this.distance(i, n), h = this.gesture, o = this.clampScale(h.startScale * (r / h.startDist)), a = s.x - o * h.worldAtMid.x, f = s.y - o * h.worldAtMid.y;
373
+ this.scale = o, this.tx = a, this.ty = f, this.applyTransform();
374
+ }
375
+ onTouchEnd(t) {
376
+ if (t.touches.length < 2 && (this.gesture = null, this.inContainerGesture = !1, this.transforming)) {
377
+ this.transforming = !1, this.transformEndTimer && (clearTimeout(this.transformEndTimer), this.transformEndTimer = null);
378
+ try {
379
+ const e = new CustomEvent("infiniteCanvas:transformEnd", {
380
+ detail: {
381
+ scale: this.scale,
382
+ x: this.tx,
383
+ y: this.ty,
384
+ content: this.content
385
+ },
386
+ bubbles: !0
387
+ });
388
+ this.container.dispatchEvent(e);
389
+ } catch {
390
+ }
391
+ if (this.onTransformEndCb)
392
+ try {
393
+ this.onTransformEndCb({
394
+ scale: this.scale,
395
+ x: this.tx,
396
+ y: this.ty,
397
+ content: this.content
398
+ });
399
+ } catch {
400
+ }
401
+ }
402
+ }
403
+ // 轨迹板/鼠标:两指滚动 => 平移;Ctrl/⌘ + 滚轮 => 缩放(Chrome/Edge)
404
+ onWheel(t) {
405
+ if (this.container.contains(t.target)) {
406
+ if (t.ctrlKey) {
407
+ if (t.preventDefault(), !this.transforming) {
408
+ this.transforming = !0;
409
+ try {
410
+ const r = new CustomEvent("infiniteCanvas:transformStart", {
411
+ detail: {
412
+ scale: this.scale,
413
+ x: this.tx,
414
+ y: this.ty,
415
+ content: this.content
416
+ },
417
+ bubbles: !0
418
+ });
419
+ this.container.dispatchEvent(r);
420
+ } catch {
421
+ }
422
+ if (this.onTransformStartCb)
423
+ try {
424
+ this.onTransformStartCb({
425
+ scale: this.scale,
426
+ x: this.tx,
427
+ y: this.ty,
428
+ content: this.content
429
+ });
430
+ } catch {
431
+ }
432
+ }
433
+ const e = (t.clientX - this.tx) / this.scale, i = (t.clientY - this.ty) / this.scale, n = Math.exp(-t.deltaY * 2e-3), s = this.clampScale(this.scale * n);
434
+ this.tx = t.clientX - s * e, this.ty = t.clientY - s * i, this.scale = s, this.applyTransform();
435
+ return;
436
+ }
437
+ if (t.preventDefault(), !this.transforming) {
438
+ this.transforming = !0;
439
+ try {
440
+ const e = new CustomEvent("infiniteCanvas:transformStart", {
441
+ detail: {
442
+ scale: this.scale,
443
+ x: this.tx,
444
+ y: this.ty,
445
+ content: this.content
446
+ },
447
+ bubbles: !0
448
+ });
449
+ this.container.dispatchEvent(e);
450
+ } catch {
451
+ }
452
+ if (this.onTransformStartCb)
453
+ try {
454
+ this.onTransformStartCb({
455
+ scale: this.scale,
456
+ x: this.tx,
457
+ y: this.ty,
458
+ content: this.content
459
+ });
460
+ } catch {
461
+ }
462
+ }
463
+ this.tx -= t.deltaX, this.ty -= t.deltaY, this.applyTransform();
464
+ }
465
+ }
466
+ // iOS Safari 的非标准手势事件:阻止页面级缩放
467
+ onGesture(t) {
468
+ const e = t.target;
469
+ (!!(e && this.container.contains(e)) || this.inContainerGesture) && typeof t.preventDefault == "function" && t.preventDefault();
470
+ }
471
+ }
472
+ export {
473
+ G as InfiniteCanvas
474
+ };
475
+ //# sourceMappingURL=infinite-canvas.es.js.map