customer-chat-sdk 1.1.10 → 1.1.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.
- package/dist/core/IconManager.d.ts +10 -0
- package/dist/core/IconManager.d.ts.map +1 -1
- package/dist/customer-sdk.cjs.js +101 -13
- package/dist/customer-sdk.esm.js +101 -13
- package/dist/customer-sdk.min.js +2 -2
- package/package.json +1 -1
|
@@ -24,6 +24,16 @@ export declare class IconManager {
|
|
|
24
24
|
private lastTouchPosition;
|
|
25
25
|
private touchStartTime;
|
|
26
26
|
private clickThreshold;
|
|
27
|
+
private cachedContainer;
|
|
28
|
+
private cachedContainerRect;
|
|
29
|
+
private cachedIconSize;
|
|
30
|
+
private lastContainerUpdateTime;
|
|
31
|
+
private containerUpdateInterval;
|
|
32
|
+
private cachedIframes;
|
|
33
|
+
private lastIframeUpdateTime;
|
|
34
|
+
private iframeUpdateInterval;
|
|
35
|
+
private rafId;
|
|
36
|
+
private pendingPosition;
|
|
27
37
|
private onDragHandler;
|
|
28
38
|
private stopDragHandler;
|
|
29
39
|
private startDragHandler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IconManager.d.ts","sourceRoot":"","sources":["../../src/core/IconManager.ts"],"names":[],"mappings":"AAGA,UAAU,mBAAmB;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,UAAU,YAAY;IACpB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACpB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,oBAAoB,CAAqC;IACjE,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,MAAM,CAAoC;IAGlD,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,iBAAiB,CAAiB;IAC1C,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,cAAc,CAAK;IAG3B,OAAO,CAAC,aAAa,CAAsD;IAC3E,OAAO,CAAC,eAAe,CAAsD;IAC7E,OAAO,CAAC,gBAAgB,CAAsD;IAC9E,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,mBAAmB,CAA2C;IACtE,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,aAAa,CAAsB;gBAE/B,QAAQ,CAAC,EAAE,YAAY,EAAE,KAAK,GAAE,OAAe,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM;IAQ1F;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAwH3B;;OAEG;IACH,sBAAsB,IAAI,IAAI;IAI9B;;OAEG;IACH,IAAI,IAAI,IAAI;IAoCZ;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI;IAIhC;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;IAqB7C;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAI1B;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAInC;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAInD;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IA8BpD;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAUzB;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;OAEG;IACH,OAAO,CAAC,SAAS;
|
|
1
|
+
{"version":3,"file":"IconManager.d.ts","sourceRoot":"","sources":["../../src/core/IconManager.ts"],"names":[],"mappings":"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"}
|
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -22,6 +22,19 @@ class IconManager {
|
|
|
22
22
|
this.lastTouchPosition = { x: 0, y: 0 }; // 最后触摸位置
|
|
23
23
|
this.touchStartTime = 0; // 触摸开始时间
|
|
24
24
|
this.clickThreshold = 15; // 点击阈值(像素)
|
|
25
|
+
// 性能优化:缓存容器信息,避免频繁查询 DOM
|
|
26
|
+
this.cachedContainer = null;
|
|
27
|
+
this.cachedContainerRect = null;
|
|
28
|
+
this.cachedIconSize = { width: 0, height: 0 };
|
|
29
|
+
this.lastContainerUpdateTime = 0;
|
|
30
|
+
this.containerUpdateInterval = 100; // 每 100ms 更新一次容器信息(避免频繁重排)
|
|
31
|
+
// 性能优化:缓存所有 iframe 的位置信息(用于检测鼠标是否在 iframe 上)
|
|
32
|
+
this.cachedIframes = [];
|
|
33
|
+
this.lastIframeUpdateTime = 0;
|
|
34
|
+
this.iframeUpdateInterval = 50; // 每 50ms 更新一次 iframe 位置信息(更频繁,因为 iframe 可能移动)
|
|
35
|
+
// 性能优化:使用 requestAnimationFrame 节流位置更新
|
|
36
|
+
this.rafId = null;
|
|
37
|
+
this.pendingPosition = { x: 0, y: 0, needsUpdate: false };
|
|
25
38
|
// 事件处理器引用(用于清理)
|
|
26
39
|
this.onDragHandler = null;
|
|
27
40
|
this.stopDragHandler = null;
|
|
@@ -321,6 +334,14 @@ class IconManager {
|
|
|
321
334
|
this.dragOffset.x = clientX - iconRect.left;
|
|
322
335
|
this.dragOffset.y = clientY - iconRect.top;
|
|
323
336
|
// 注意:不在这里转换位置,只在真正开始拖动时才转换(在 onDrag 中)
|
|
337
|
+
// 性能优化:在拖动开始时预加载所有 iframe 位置信息
|
|
338
|
+
// 这样可以避免在拖动过程中频繁查询 DOM
|
|
339
|
+
const allIframes = document.querySelectorAll('iframe');
|
|
340
|
+
this.cachedIframes = Array.from(allIframes).map(iframe => ({
|
|
341
|
+
element: iframe,
|
|
342
|
+
rect: iframe.getBoundingClientRect()
|
|
343
|
+
}));
|
|
344
|
+
this.lastIframeUpdateTime = Date.now();
|
|
324
345
|
// 添加 document 事件监听器
|
|
325
346
|
if (this.onDragHandler) {
|
|
326
347
|
document.addEventListener('mousemove', this.onDragHandler);
|
|
@@ -363,7 +384,7 @@ class IconManager {
|
|
|
363
384
|
};
|
|
364
385
|
window.addEventListener('blur', this.blurHandler);
|
|
365
386
|
// 3. 添加超时机制(如果一段时间没有收到 mousemove 事件,自动停止拖动)
|
|
366
|
-
//
|
|
387
|
+
// 优化:缩短超时时间到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
|
|
367
388
|
this.dragTimeoutId = window.setTimeout(() => {
|
|
368
389
|
if (this.isDragging) {
|
|
369
390
|
if (this.debug) {
|
|
@@ -371,7 +392,7 @@ class IconManager {
|
|
|
371
392
|
}
|
|
372
393
|
this.stopDrag();
|
|
373
394
|
}
|
|
374
|
-
},
|
|
395
|
+
}, 100); // 缩短到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
|
|
375
396
|
if (this.debug) {
|
|
376
397
|
console.log('Drag start');
|
|
377
398
|
}
|
|
@@ -389,7 +410,41 @@ class IconManager {
|
|
|
389
410
|
if (!this.iconElement)
|
|
390
411
|
return;
|
|
391
412
|
e.preventDefault();
|
|
413
|
+
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
414
|
+
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
415
|
+
// 检测鼠标是否在任何 iframe 上(通过坐标判断)
|
|
416
|
+
// 如果检测到鼠标在 iframe 区域,立即停止拖动
|
|
417
|
+
// 重要:需要检测所有 iframe(包括嵌套的),因为任何 iframe 都会导致事件丢失
|
|
418
|
+
if (this.isDragging) {
|
|
419
|
+
const now = Date.now();
|
|
420
|
+
// 性能优化:缓存 iframe 位置信息,避免频繁查询 DOM
|
|
421
|
+
if (this.cachedIframes.length === 0 ||
|
|
422
|
+
now - this.lastIframeUpdateTime > this.iframeUpdateInterval) {
|
|
423
|
+
// 更新 iframe 缓存
|
|
424
|
+
const allIframes = document.querySelectorAll('iframe');
|
|
425
|
+
this.cachedIframes = Array.from(allIframes).map(iframe => ({
|
|
426
|
+
element: iframe,
|
|
427
|
+
rect: iframe.getBoundingClientRect()
|
|
428
|
+
}));
|
|
429
|
+
this.lastIframeUpdateTime = now;
|
|
430
|
+
}
|
|
431
|
+
// 检查鼠标是否在任何 iframe 上
|
|
432
|
+
for (const { rect } of this.cachedIframes) {
|
|
433
|
+
if (clientX >= rect.left &&
|
|
434
|
+
clientX <= rect.right &&
|
|
435
|
+
clientY >= rect.top &&
|
|
436
|
+
clientY <= rect.bottom) {
|
|
437
|
+
// 鼠标在 iframe 上,立即停止拖动
|
|
438
|
+
if (this.debug) {
|
|
439
|
+
console.log('Mouse over iframe, stopping drag immediately');
|
|
440
|
+
}
|
|
441
|
+
this.stopDrag();
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
392
446
|
// 重置超时定时器(每次移动都重置,确保只有真正停止移动时才触发超时)
|
|
447
|
+
// 优化:缩短超时时间到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
|
|
393
448
|
if (this.dragTimeoutId !== null) {
|
|
394
449
|
window.clearTimeout(this.dragTimeoutId);
|
|
395
450
|
this.dragTimeoutId = window.setTimeout(() => {
|
|
@@ -399,10 +454,8 @@ class IconManager {
|
|
|
399
454
|
}
|
|
400
455
|
this.stopDrag();
|
|
401
456
|
}
|
|
402
|
-
},
|
|
457
|
+
}, 100); // 缩短到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
|
|
403
458
|
}
|
|
404
|
-
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
405
|
-
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
406
459
|
// 检查是否有足够的移动距离
|
|
407
460
|
const deltaX = Math.abs(clientX - this.lastTouchPosition.x);
|
|
408
461
|
const deltaY = Math.abs(clientY - this.lastTouchPosition.y);
|
|
@@ -446,17 +499,31 @@ class IconManager {
|
|
|
446
499
|
return;
|
|
447
500
|
}
|
|
448
501
|
try {
|
|
502
|
+
// 性能优化:缓存容器信息,避免频繁查询 DOM
|
|
503
|
+
const now = Date.now();
|
|
449
504
|
const container = this.getTargetElement();
|
|
450
505
|
if (!container) {
|
|
451
506
|
return;
|
|
452
507
|
}
|
|
453
|
-
|
|
508
|
+
// 只在必要时更新容器信息(避免频繁重排)
|
|
509
|
+
if (!this.cachedContainer ||
|
|
510
|
+
this.cachedContainer !== container ||
|
|
511
|
+
now - this.lastContainerUpdateTime > this.containerUpdateInterval) {
|
|
512
|
+
this.cachedContainer = container;
|
|
513
|
+
this.cachedContainerRect = container.getBoundingClientRect();
|
|
514
|
+
this.cachedIconSize = {
|
|
515
|
+
width: this.iconElement.offsetWidth,
|
|
516
|
+
height: this.iconElement.offsetHeight
|
|
517
|
+
};
|
|
518
|
+
this.lastContainerUpdateTime = now;
|
|
519
|
+
}
|
|
520
|
+
const containerRect = this.cachedContainerRect;
|
|
521
|
+
const iconWidth = this.cachedIconSize.width;
|
|
522
|
+
const iconHeight = this.cachedIconSize.height;
|
|
454
523
|
// 计算新位置
|
|
455
524
|
let newX = clientX - this.dragOffset.x - containerRect.left;
|
|
456
525
|
let newY = clientY - this.dragOffset.y - containerRect.top;
|
|
457
526
|
// 限制在容器内
|
|
458
|
-
const iconWidth = this.iconElement.offsetWidth;
|
|
459
|
-
const iconHeight = this.iconElement.offsetHeight;
|
|
460
527
|
if (container === document.body) {
|
|
461
528
|
// 限制在视口内
|
|
462
529
|
newX = Math.max(0, Math.min(newX, window.innerWidth - iconWidth));
|
|
@@ -469,11 +536,22 @@ class IconManager {
|
|
|
469
536
|
newX = Math.max(0, Math.min(newX, containerWidth - iconWidth));
|
|
470
537
|
newY = Math.max(0, Math.min(newY, containerHeight - iconHeight));
|
|
471
538
|
}
|
|
472
|
-
//
|
|
473
|
-
this.
|
|
474
|
-
this.
|
|
475
|
-
this.
|
|
476
|
-
this.
|
|
539
|
+
// 性能优化:使用 requestAnimationFrame 节流位置更新
|
|
540
|
+
this.pendingPosition.x = newX;
|
|
541
|
+
this.pendingPosition.y = newY;
|
|
542
|
+
this.pendingPosition.needsUpdate = true;
|
|
543
|
+
if (this.rafId === null) {
|
|
544
|
+
this.rafId = requestAnimationFrame(() => {
|
|
545
|
+
this.rafId = null;
|
|
546
|
+
if (this.pendingPosition.needsUpdate && this.iconElement && this.isDragging) {
|
|
547
|
+
this.iconElement.style.left = `${this.pendingPosition.x}px`;
|
|
548
|
+
this.iconElement.style.top = `${this.pendingPosition.y}px`;
|
|
549
|
+
this.iconElement.style.right = 'auto';
|
|
550
|
+
this.iconElement.style.bottom = 'auto';
|
|
551
|
+
this.pendingPosition.needsUpdate = false;
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
}
|
|
477
555
|
// 更新最后位置
|
|
478
556
|
this.lastTouchPosition.x = clientX;
|
|
479
557
|
this.lastTouchPosition.y = clientY;
|
|
@@ -573,6 +651,16 @@ class IconManager {
|
|
|
573
651
|
window.clearTimeout(this.dragTimeoutId);
|
|
574
652
|
this.dragTimeoutId = null;
|
|
575
653
|
}
|
|
654
|
+
// 清理 requestAnimationFrame
|
|
655
|
+
if (this.rafId !== null) {
|
|
656
|
+
cancelAnimationFrame(this.rafId);
|
|
657
|
+
this.rafId = null;
|
|
658
|
+
}
|
|
659
|
+
// 清理缓存
|
|
660
|
+
this.cachedContainer = null;
|
|
661
|
+
this.cachedContainerRect = null;
|
|
662
|
+
this.cachedIframes = []; // 清理 iframe 缓存
|
|
663
|
+
this.pendingPosition.needsUpdate = false;
|
|
576
664
|
}
|
|
577
665
|
/**
|
|
578
666
|
* 处理点击事件
|
package/dist/customer-sdk.esm.js
CHANGED
|
@@ -18,6 +18,19 @@ class IconManager {
|
|
|
18
18
|
this.lastTouchPosition = { x: 0, y: 0 }; // 最后触摸位置
|
|
19
19
|
this.touchStartTime = 0; // 触摸开始时间
|
|
20
20
|
this.clickThreshold = 15; // 点击阈值(像素)
|
|
21
|
+
// 性能优化:缓存容器信息,避免频繁查询 DOM
|
|
22
|
+
this.cachedContainer = null;
|
|
23
|
+
this.cachedContainerRect = null;
|
|
24
|
+
this.cachedIconSize = { width: 0, height: 0 };
|
|
25
|
+
this.lastContainerUpdateTime = 0;
|
|
26
|
+
this.containerUpdateInterval = 100; // 每 100ms 更新一次容器信息(避免频繁重排)
|
|
27
|
+
// 性能优化:缓存所有 iframe 的位置信息(用于检测鼠标是否在 iframe 上)
|
|
28
|
+
this.cachedIframes = [];
|
|
29
|
+
this.lastIframeUpdateTime = 0;
|
|
30
|
+
this.iframeUpdateInterval = 50; // 每 50ms 更新一次 iframe 位置信息(更频繁,因为 iframe 可能移动)
|
|
31
|
+
// 性能优化:使用 requestAnimationFrame 节流位置更新
|
|
32
|
+
this.rafId = null;
|
|
33
|
+
this.pendingPosition = { x: 0, y: 0, needsUpdate: false };
|
|
21
34
|
// 事件处理器引用(用于清理)
|
|
22
35
|
this.onDragHandler = null;
|
|
23
36
|
this.stopDragHandler = null;
|
|
@@ -317,6 +330,14 @@ class IconManager {
|
|
|
317
330
|
this.dragOffset.x = clientX - iconRect.left;
|
|
318
331
|
this.dragOffset.y = clientY - iconRect.top;
|
|
319
332
|
// 注意:不在这里转换位置,只在真正开始拖动时才转换(在 onDrag 中)
|
|
333
|
+
// 性能优化:在拖动开始时预加载所有 iframe 位置信息
|
|
334
|
+
// 这样可以避免在拖动过程中频繁查询 DOM
|
|
335
|
+
const allIframes = document.querySelectorAll('iframe');
|
|
336
|
+
this.cachedIframes = Array.from(allIframes).map(iframe => ({
|
|
337
|
+
element: iframe,
|
|
338
|
+
rect: iframe.getBoundingClientRect()
|
|
339
|
+
}));
|
|
340
|
+
this.lastIframeUpdateTime = Date.now();
|
|
320
341
|
// 添加 document 事件监听器
|
|
321
342
|
if (this.onDragHandler) {
|
|
322
343
|
document.addEventListener('mousemove', this.onDragHandler);
|
|
@@ -359,7 +380,7 @@ class IconManager {
|
|
|
359
380
|
};
|
|
360
381
|
window.addEventListener('blur', this.blurHandler);
|
|
361
382
|
// 3. 添加超时机制(如果一段时间没有收到 mousemove 事件,自动停止拖动)
|
|
362
|
-
//
|
|
383
|
+
// 优化:缩短超时时间到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
|
|
363
384
|
this.dragTimeoutId = window.setTimeout(() => {
|
|
364
385
|
if (this.isDragging) {
|
|
365
386
|
if (this.debug) {
|
|
@@ -367,7 +388,7 @@ class IconManager {
|
|
|
367
388
|
}
|
|
368
389
|
this.stopDrag();
|
|
369
390
|
}
|
|
370
|
-
},
|
|
391
|
+
}, 100); // 缩短到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
|
|
371
392
|
if (this.debug) {
|
|
372
393
|
console.log('Drag start');
|
|
373
394
|
}
|
|
@@ -385,7 +406,41 @@ class IconManager {
|
|
|
385
406
|
if (!this.iconElement)
|
|
386
407
|
return;
|
|
387
408
|
e.preventDefault();
|
|
409
|
+
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
410
|
+
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
411
|
+
// 检测鼠标是否在任何 iframe 上(通过坐标判断)
|
|
412
|
+
// 如果检测到鼠标在 iframe 区域,立即停止拖动
|
|
413
|
+
// 重要:需要检测所有 iframe(包括嵌套的),因为任何 iframe 都会导致事件丢失
|
|
414
|
+
if (this.isDragging) {
|
|
415
|
+
const now = Date.now();
|
|
416
|
+
// 性能优化:缓存 iframe 位置信息,避免频繁查询 DOM
|
|
417
|
+
if (this.cachedIframes.length === 0 ||
|
|
418
|
+
now - this.lastIframeUpdateTime > this.iframeUpdateInterval) {
|
|
419
|
+
// 更新 iframe 缓存
|
|
420
|
+
const allIframes = document.querySelectorAll('iframe');
|
|
421
|
+
this.cachedIframes = Array.from(allIframes).map(iframe => ({
|
|
422
|
+
element: iframe,
|
|
423
|
+
rect: iframe.getBoundingClientRect()
|
|
424
|
+
}));
|
|
425
|
+
this.lastIframeUpdateTime = now;
|
|
426
|
+
}
|
|
427
|
+
// 检查鼠标是否在任何 iframe 上
|
|
428
|
+
for (const { rect } of this.cachedIframes) {
|
|
429
|
+
if (clientX >= rect.left &&
|
|
430
|
+
clientX <= rect.right &&
|
|
431
|
+
clientY >= rect.top &&
|
|
432
|
+
clientY <= rect.bottom) {
|
|
433
|
+
// 鼠标在 iframe 上,立即停止拖动
|
|
434
|
+
if (this.debug) {
|
|
435
|
+
console.log('Mouse over iframe, stopping drag immediately');
|
|
436
|
+
}
|
|
437
|
+
this.stopDrag();
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
388
442
|
// 重置超时定时器(每次移动都重置,确保只有真正停止移动时才触发超时)
|
|
443
|
+
// 优化:缩短超时时间到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
|
|
389
444
|
if (this.dragTimeoutId !== null) {
|
|
390
445
|
window.clearTimeout(this.dragTimeoutId);
|
|
391
446
|
this.dragTimeoutId = window.setTimeout(() => {
|
|
@@ -395,10 +450,8 @@ class IconManager {
|
|
|
395
450
|
}
|
|
396
451
|
this.stopDrag();
|
|
397
452
|
}
|
|
398
|
-
},
|
|
453
|
+
}, 100); // 缩短到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
|
|
399
454
|
}
|
|
400
|
-
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
401
|
-
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
402
455
|
// 检查是否有足够的移动距离
|
|
403
456
|
const deltaX = Math.abs(clientX - this.lastTouchPosition.x);
|
|
404
457
|
const deltaY = Math.abs(clientY - this.lastTouchPosition.y);
|
|
@@ -442,17 +495,31 @@ class IconManager {
|
|
|
442
495
|
return;
|
|
443
496
|
}
|
|
444
497
|
try {
|
|
498
|
+
// 性能优化:缓存容器信息,避免频繁查询 DOM
|
|
499
|
+
const now = Date.now();
|
|
445
500
|
const container = this.getTargetElement();
|
|
446
501
|
if (!container) {
|
|
447
502
|
return;
|
|
448
503
|
}
|
|
449
|
-
|
|
504
|
+
// 只在必要时更新容器信息(避免频繁重排)
|
|
505
|
+
if (!this.cachedContainer ||
|
|
506
|
+
this.cachedContainer !== container ||
|
|
507
|
+
now - this.lastContainerUpdateTime > this.containerUpdateInterval) {
|
|
508
|
+
this.cachedContainer = container;
|
|
509
|
+
this.cachedContainerRect = container.getBoundingClientRect();
|
|
510
|
+
this.cachedIconSize = {
|
|
511
|
+
width: this.iconElement.offsetWidth,
|
|
512
|
+
height: this.iconElement.offsetHeight
|
|
513
|
+
};
|
|
514
|
+
this.lastContainerUpdateTime = now;
|
|
515
|
+
}
|
|
516
|
+
const containerRect = this.cachedContainerRect;
|
|
517
|
+
const iconWidth = this.cachedIconSize.width;
|
|
518
|
+
const iconHeight = this.cachedIconSize.height;
|
|
450
519
|
// 计算新位置
|
|
451
520
|
let newX = clientX - this.dragOffset.x - containerRect.left;
|
|
452
521
|
let newY = clientY - this.dragOffset.y - containerRect.top;
|
|
453
522
|
// 限制在容器内
|
|
454
|
-
const iconWidth = this.iconElement.offsetWidth;
|
|
455
|
-
const iconHeight = this.iconElement.offsetHeight;
|
|
456
523
|
if (container === document.body) {
|
|
457
524
|
// 限制在视口内
|
|
458
525
|
newX = Math.max(0, Math.min(newX, window.innerWidth - iconWidth));
|
|
@@ -465,11 +532,22 @@ class IconManager {
|
|
|
465
532
|
newX = Math.max(0, Math.min(newX, containerWidth - iconWidth));
|
|
466
533
|
newY = Math.max(0, Math.min(newY, containerHeight - iconHeight));
|
|
467
534
|
}
|
|
468
|
-
//
|
|
469
|
-
this.
|
|
470
|
-
this.
|
|
471
|
-
this.
|
|
472
|
-
this.
|
|
535
|
+
// 性能优化:使用 requestAnimationFrame 节流位置更新
|
|
536
|
+
this.pendingPosition.x = newX;
|
|
537
|
+
this.pendingPosition.y = newY;
|
|
538
|
+
this.pendingPosition.needsUpdate = true;
|
|
539
|
+
if (this.rafId === null) {
|
|
540
|
+
this.rafId = requestAnimationFrame(() => {
|
|
541
|
+
this.rafId = null;
|
|
542
|
+
if (this.pendingPosition.needsUpdate && this.iconElement && this.isDragging) {
|
|
543
|
+
this.iconElement.style.left = `${this.pendingPosition.x}px`;
|
|
544
|
+
this.iconElement.style.top = `${this.pendingPosition.y}px`;
|
|
545
|
+
this.iconElement.style.right = 'auto';
|
|
546
|
+
this.iconElement.style.bottom = 'auto';
|
|
547
|
+
this.pendingPosition.needsUpdate = false;
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
}
|
|
473
551
|
// 更新最后位置
|
|
474
552
|
this.lastTouchPosition.x = clientX;
|
|
475
553
|
this.lastTouchPosition.y = clientY;
|
|
@@ -569,6 +647,16 @@ class IconManager {
|
|
|
569
647
|
window.clearTimeout(this.dragTimeoutId);
|
|
570
648
|
this.dragTimeoutId = null;
|
|
571
649
|
}
|
|
650
|
+
// 清理 requestAnimationFrame
|
|
651
|
+
if (this.rafId !== null) {
|
|
652
|
+
cancelAnimationFrame(this.rafId);
|
|
653
|
+
this.rafId = null;
|
|
654
|
+
}
|
|
655
|
+
// 清理缓存
|
|
656
|
+
this.cachedContainer = null;
|
|
657
|
+
this.cachedContainerRect = null;
|
|
658
|
+
this.cachedIframes = []; // 清理 iframe 缓存
|
|
659
|
+
this.pendingPosition.needsUpdate = false;
|
|
572
660
|
}
|
|
573
661
|
/**
|
|
574
662
|
* 处理点击事件
|