customer-chat-sdk 1.1.11 → 1.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,6 +34,8 @@ export declare class IconManager {
34
34
  private iframeUpdateInterval;
35
35
  private rafId;
36
36
  private pendingPosition;
37
+ private activeDetectionRafId;
38
+ private lastMousePosition;
37
39
  private onDragHandler;
38
40
  private stopDragHandler;
39
41
  private startDragHandler;
@@ -98,6 +100,15 @@ export declare class IconManager {
98
100
  * 停止拖动
99
101
  */
100
102
  private stopDrag;
103
+ /**
104
+ * 启动主动检测机制:使用 requestAnimationFrame 定期检查鼠标位置
105
+ * 即使没有 mousemove 事件,也能检测到鼠标是否进入 iframe
106
+ */
107
+ private startActiveDetection;
108
+ /**
109
+ * 停止主动检测机制
110
+ */
111
+ private stopActiveDetection;
101
112
  /**
102
113
  * 清理拖动事件
103
114
  */
@@ -1 +1 @@
1
- {"version":3,"file":"IconManager.d.ts","sourceRoot":"","sources":["../../src/core/IconManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"IconManager.d.ts","sourceRoot":"","sources":["../../src/core/IconManager.ts"],"names":[],"mappings":"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"}
@@ -31,10 +31,13 @@ class IconManager {
31
31
  // 性能优化:缓存所有 iframe 的位置信息(用于检测鼠标是否在 iframe 上)
32
32
  this.cachedIframes = [];
33
33
  this.lastIframeUpdateTime = 0;
34
- this.iframeUpdateInterval = 50; // 每 50ms 更新一次 iframe 位置信息(更频繁,因为 iframe 可能移动)
34
+ this.iframeUpdateInterval = 16; // 每 16ms 更新一次 iframe 位置信息(约一帧,更频繁,因为 iframe 可能移动)
35
35
  // 性能优化:使用 requestAnimationFrame 节流位置更新
36
36
  this.rafId = null;
37
37
  this.pendingPosition = { x: 0, y: 0, needsUpdate: false };
38
+ // 主动检测机制:使用 requestAnimationFrame 定期检查鼠标位置(即使没有 mousemove 事件)
39
+ this.activeDetectionRafId = null;
40
+ this.lastMousePosition = { x: 0, y: 0, timestamp: 0 }; // 最后记录的鼠标位置和时间戳
38
41
  // 事件处理器引用(用于清理)
39
42
  this.onDragHandler = null;
40
43
  this.stopDragHandler = null;
@@ -342,6 +345,18 @@ class IconManager {
342
345
  rect: iframe.getBoundingClientRect()
343
346
  }));
344
347
  this.lastIframeUpdateTime = Date.now();
348
+ if (this.debug) {
349
+ console.log(`[IconManager] Drag start - Found ${this.cachedIframes.length} iframe(s)`, {
350
+ iframes: this.cachedIframes.map(({ rect }) => ({
351
+ left: rect.left,
352
+ top: rect.top,
353
+ right: rect.right,
354
+ bottom: rect.bottom,
355
+ width: rect.width,
356
+ height: rect.height
357
+ }))
358
+ });
359
+ }
345
360
  // 添加 document 事件监听器
346
361
  if (this.onDragHandler) {
347
362
  document.addEventListener('mousemove', this.onDragHandler);
@@ -384,7 +399,7 @@ class IconManager {
384
399
  };
385
400
  window.addEventListener('blur', this.blurHandler);
386
401
  // 3. 添加超时机制(如果一段时间没有收到 mousemove 事件,自动停止拖动)
387
- // 优化:缩短超时时间到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
402
+ // 优化:缩短超时时间到 50ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
388
403
  this.dragTimeoutId = window.setTimeout(() => {
389
404
  if (this.isDragging) {
390
405
  if (this.debug) {
@@ -392,9 +407,21 @@ class IconManager {
392
407
  }
393
408
  this.stopDrag();
394
409
  }
395
- }, 100); // 缩短到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
410
+ }, 50); // 缩短到 50ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
411
+ // 4. 启动主动检测机制:使用 requestAnimationFrame 定期检查鼠标位置
412
+ // 即使没有 mousemove 事件,也能检测到鼠标是否进入 iframe
413
+ this.startActiveDetection();
396
414
  if (this.debug) {
397
- console.log('Drag start');
415
+ console.log('[IconManager] Drag start', {
416
+ startPosition: { x: clientX, y: clientY },
417
+ iconRect: {
418
+ left: iconRect.left,
419
+ top: iconRect.top,
420
+ width: iconRect.width,
421
+ height: iconRect.height
422
+ },
423
+ dragOffset: this.dragOffset
424
+ });
398
425
  }
399
426
  }
400
427
  catch (error) {
@@ -422,39 +449,79 @@ class IconManager {
422
449
  now - this.lastIframeUpdateTime > this.iframeUpdateInterval) {
423
450
  // 更新 iframe 缓存
424
451
  const allIframes = document.querySelectorAll('iframe');
452
+ const previousCount = this.cachedIframes.length;
425
453
  this.cachedIframes = Array.from(allIframes).map(iframe => ({
426
454
  element: iframe,
427
455
  rect: iframe.getBoundingClientRect()
428
456
  }));
429
457
  this.lastIframeUpdateTime = now;
458
+ if (this.debug && this.cachedIframes.length !== previousCount) {
459
+ console.log(`[IconManager] Iframe cache updated - Found ${this.cachedIframes.length} iframe(s)`, {
460
+ iframes: this.cachedIframes.map(({ rect }) => ({
461
+ left: rect.left,
462
+ top: rect.top,
463
+ right: rect.right,
464
+ bottom: rect.bottom
465
+ }))
466
+ });
467
+ }
430
468
  }
431
469
  // 检查鼠标是否在任何 iframe 上
432
- for (const { rect } of this.cachedIframes) {
470
+ for (const { rect, element } of this.cachedIframes) {
433
471
  if (clientX >= rect.left &&
434
472
  clientX <= rect.right &&
435
473
  clientY >= rect.top &&
436
474
  clientY <= rect.bottom) {
437
475
  // 鼠标在 iframe 上,立即停止拖动
438
476
  if (this.debug) {
439
- console.log('Mouse over iframe, stopping drag immediately');
477
+ console.log('[IconManager] Mouse over iframe detected, stopping drag immediately', {
478
+ mousePosition: { x: clientX, y: clientY },
479
+ iframeRect: {
480
+ left: rect.left,
481
+ top: rect.top,
482
+ right: rect.right,
483
+ bottom: rect.bottom
484
+ },
485
+ iframeSrc: element.src || 'no src'
486
+ });
440
487
  }
441
488
  this.stopDrag();
442
489
  return;
443
490
  }
444
491
  }
445
492
  }
493
+ // 更新最后记录的鼠标位置和时间戳(用于主动检测)
494
+ const now = Date.now();
495
+ const timeSinceLastUpdate = now - this.lastMousePosition.timestamp;
496
+ this.lastMousePosition = {
497
+ x: clientX,
498
+ y: clientY,
499
+ timestamp: now
500
+ };
501
+ // 如果距离上次更新超过 50ms,记录警告(可能事件丢失)
502
+ if (this.debug && timeSinceLastUpdate > 50 && this.lastMousePosition.timestamp > 0) {
503
+ console.warn(`[IconManager] Long gap between mousemove events: ${timeSinceLastUpdate}ms`, {
504
+ lastPosition: { x: this.lastMousePosition.x, y: this.lastMousePosition.y },
505
+ currentPosition: { x: clientX, y: clientY }
506
+ });
507
+ }
446
508
  // 重置超时定时器(每次移动都重置,确保只有真正停止移动时才触发超时)
447
- // 优化:缩短超时时间到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
509
+ // 优化:缩短超时时间到 50ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
448
510
  if (this.dragTimeoutId !== null) {
449
511
  window.clearTimeout(this.dragTimeoutId);
450
512
  this.dragTimeoutId = window.setTimeout(() => {
451
513
  if (this.isDragging) {
452
514
  if (this.debug) {
453
- console.log('Drag timeout, stopping drag (likely mouse moved over iframe)');
515
+ const timeSinceLastMove = Date.now() - this.lastMousePosition.timestamp;
516
+ console.warn('[IconManager] Drag timeout triggered, stopping drag (likely mouse moved over iframe)', {
517
+ timeSinceLastMove,
518
+ lastMousePosition: this.lastMousePosition,
519
+ iframeCount: this.cachedIframes.length
520
+ });
454
521
  }
455
522
  this.stopDrag();
456
523
  }
457
- }, 100); // 缩短到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
524
+ }, 50); // 缩短到 50ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
458
525
  }
459
526
  // 检查是否有足够的移动距离
460
527
  const deltaX = Math.abs(clientX - this.lastTouchPosition.x);
@@ -566,6 +633,14 @@ class IconManager {
566
633
  * 停止拖动
567
634
  */
568
635
  stopDrag(_e) {
636
+ const stopReason = this.isDragging ? 'drag_stopped' : 'not_dragging';
637
+ const finalPosition = this.iconElement ? {
638
+ left: this.iconElement.style.left,
639
+ top: this.iconElement.style.top,
640
+ computed: window.getComputedStyle(this.iconElement).left !== 'auto'
641
+ ? { left: window.getComputedStyle(this.iconElement).left, top: window.getComputedStyle(this.iconElement).top }
642
+ : null
643
+ } : null;
569
644
  this.cleanupDragEvents();
570
645
  if (!this.iconElement)
571
646
  return;
@@ -576,11 +651,14 @@ class IconManager {
576
651
  const touchDuration = Date.now() - this.touchStartTime;
577
652
  const isValidClick = !this.hasMoved && touchDuration < 1000 && !this.dragStarted;
578
653
  if (this.debug) {
579
- console.log('Drag end', {
654
+ console.log('[IconManager] Drag end', {
655
+ stopReason,
580
656
  hasMoved: this.hasMoved,
581
657
  dragStarted: this.dragStarted,
582
658
  touchDuration,
583
- isValidClick
659
+ isValidClick,
660
+ finalPosition,
661
+ lastMousePosition: this.lastMousePosition
584
662
  });
585
663
  }
586
664
  // 先保存点击状态,然后重置拖动状态
@@ -620,6 +698,97 @@ class IconManager {
620
698
  }, 100); // 增加延迟到 100ms,确保拖动状态完全重置
621
699
  }
622
700
  }
701
+ /**
702
+ * 启动主动检测机制:使用 requestAnimationFrame 定期检查鼠标位置
703
+ * 即使没有 mousemove 事件,也能检测到鼠标是否进入 iframe
704
+ */
705
+ startActiveDetection() {
706
+ if (this.activeDetectionRafId !== null) {
707
+ if (this.debug) {
708
+ console.log('[IconManager] Active detection already running');
709
+ }
710
+ return; // 已经在运行
711
+ }
712
+ if (this.debug) {
713
+ console.log('[IconManager] Starting active detection mechanism');
714
+ }
715
+ const checkMousePosition = () => {
716
+ if (!this.isDragging) {
717
+ if (this.debug) {
718
+ console.log('[IconManager] Active detection stopped (drag ended)');
719
+ }
720
+ this.activeDetectionRafId = null;
721
+ return;
722
+ }
723
+ // 检查是否长时间没有收到 mousemove 事件(超过 50ms)
724
+ const now = Date.now();
725
+ const timeSinceLastMove = now - this.lastMousePosition.timestamp;
726
+ if (timeSinceLastMove > 50) {
727
+ // 长时间没有收到事件,可能鼠标已经进入 iframe
728
+ // 使用最后记录的鼠标位置进行检测
729
+ const clientX = this.lastMousePosition.x;
730
+ const clientY = this.lastMousePosition.y;
731
+ if (this.debug) {
732
+ console.log(`[IconManager] Active detection: No mousemove event for ${timeSinceLastMove}ms`, {
733
+ lastMousePosition: { x: clientX, y: clientY },
734
+ timestamp: this.lastMousePosition.timestamp
735
+ });
736
+ }
737
+ // 更新 iframe 缓存(更频繁,每帧更新)
738
+ if (this.cachedIframes.length === 0 ||
739
+ now - this.lastIframeUpdateTime > this.iframeUpdateInterval) {
740
+ const allIframes = document.querySelectorAll('iframe');
741
+ this.cachedIframes = Array.from(allIframes).map(iframe => ({
742
+ element: iframe,
743
+ rect: iframe.getBoundingClientRect()
744
+ }));
745
+ this.lastIframeUpdateTime = now;
746
+ if (this.debug) {
747
+ console.log(`[IconManager] Active detection: Updated iframe cache (${this.cachedIframes.length} iframes)`);
748
+ }
749
+ }
750
+ // 检查最后记录的鼠标位置是否在任何 iframe 上
751
+ for (const { rect, element } of this.cachedIframes) {
752
+ if (clientX >= rect.left &&
753
+ clientX <= rect.right &&
754
+ clientY >= rect.top &&
755
+ clientY <= rect.bottom) {
756
+ // 鼠标在 iframe 上,立即停止拖动
757
+ if (this.debug) {
758
+ console.log('[IconManager] Active detection: Mouse over iframe detected, stopping drag immediately', {
759
+ mousePosition: { x: clientX, y: clientY },
760
+ iframeRect: {
761
+ left: rect.left,
762
+ top: rect.top,
763
+ right: rect.right,
764
+ bottom: rect.bottom
765
+ },
766
+ iframeSrc: element.src || 'no src',
767
+ timeSinceLastMove
768
+ });
769
+ }
770
+ this.stopDrag();
771
+ return;
772
+ }
773
+ }
774
+ }
775
+ // 继续检测
776
+ this.activeDetectionRafId = requestAnimationFrame(checkMousePosition);
777
+ };
778
+ this.activeDetectionRafId = requestAnimationFrame(checkMousePosition);
779
+ }
780
+ /**
781
+ * 停止主动检测机制
782
+ */
783
+ stopActiveDetection() {
784
+ if (this.activeDetectionRafId !== null) {
785
+ if (this.debug) {
786
+ console.log('[IconManager] Stopping active detection mechanism');
787
+ }
788
+ cancelAnimationFrame(this.activeDetectionRafId);
789
+ this.activeDetectionRafId = null;
790
+ }
791
+ }
623
792
  /**
624
793
  * 清理拖动事件
625
794
  */
@@ -656,6 +825,8 @@ class IconManager {
656
825
  cancelAnimationFrame(this.rafId);
657
826
  this.rafId = null;
658
827
  }
828
+ // 清理主动检测机制
829
+ this.stopActiveDetection();
659
830
  // 清理缓存
660
831
  this.cachedContainer = null;
661
832
  this.cachedContainerRect = null;
@@ -27,10 +27,13 @@ class IconManager {
27
27
  // 性能优化:缓存所有 iframe 的位置信息(用于检测鼠标是否在 iframe 上)
28
28
  this.cachedIframes = [];
29
29
  this.lastIframeUpdateTime = 0;
30
- this.iframeUpdateInterval = 50; // 每 50ms 更新一次 iframe 位置信息(更频繁,因为 iframe 可能移动)
30
+ this.iframeUpdateInterval = 16; // 每 16ms 更新一次 iframe 位置信息(约一帧,更频繁,因为 iframe 可能移动)
31
31
  // 性能优化:使用 requestAnimationFrame 节流位置更新
32
32
  this.rafId = null;
33
33
  this.pendingPosition = { x: 0, y: 0, needsUpdate: false };
34
+ // 主动检测机制:使用 requestAnimationFrame 定期检查鼠标位置(即使没有 mousemove 事件)
35
+ this.activeDetectionRafId = null;
36
+ this.lastMousePosition = { x: 0, y: 0, timestamp: 0 }; // 最后记录的鼠标位置和时间戳
34
37
  // 事件处理器引用(用于清理)
35
38
  this.onDragHandler = null;
36
39
  this.stopDragHandler = null;
@@ -338,6 +341,18 @@ class IconManager {
338
341
  rect: iframe.getBoundingClientRect()
339
342
  }));
340
343
  this.lastIframeUpdateTime = Date.now();
344
+ if (this.debug) {
345
+ console.log(`[IconManager] Drag start - Found ${this.cachedIframes.length} iframe(s)`, {
346
+ iframes: this.cachedIframes.map(({ rect }) => ({
347
+ left: rect.left,
348
+ top: rect.top,
349
+ right: rect.right,
350
+ bottom: rect.bottom,
351
+ width: rect.width,
352
+ height: rect.height
353
+ }))
354
+ });
355
+ }
341
356
  // 添加 document 事件监听器
342
357
  if (this.onDragHandler) {
343
358
  document.addEventListener('mousemove', this.onDragHandler);
@@ -380,7 +395,7 @@ class IconManager {
380
395
  };
381
396
  window.addEventListener('blur', this.blurHandler);
382
397
  // 3. 添加超时机制(如果一段时间没有收到 mousemove 事件,自动停止拖动)
383
- // 优化:缩短超时时间到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
398
+ // 优化:缩短超时时间到 50ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
384
399
  this.dragTimeoutId = window.setTimeout(() => {
385
400
  if (this.isDragging) {
386
401
  if (this.debug) {
@@ -388,9 +403,21 @@ class IconManager {
388
403
  }
389
404
  this.stopDrag();
390
405
  }
391
- }, 100); // 缩短到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
406
+ }, 50); // 缩短到 50ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
407
+ // 4. 启动主动检测机制:使用 requestAnimationFrame 定期检查鼠标位置
408
+ // 即使没有 mousemove 事件,也能检测到鼠标是否进入 iframe
409
+ this.startActiveDetection();
392
410
  if (this.debug) {
393
- console.log('Drag start');
411
+ console.log('[IconManager] Drag start', {
412
+ startPosition: { x: clientX, y: clientY },
413
+ iconRect: {
414
+ left: iconRect.left,
415
+ top: iconRect.top,
416
+ width: iconRect.width,
417
+ height: iconRect.height
418
+ },
419
+ dragOffset: this.dragOffset
420
+ });
394
421
  }
395
422
  }
396
423
  catch (error) {
@@ -418,39 +445,79 @@ class IconManager {
418
445
  now - this.lastIframeUpdateTime > this.iframeUpdateInterval) {
419
446
  // 更新 iframe 缓存
420
447
  const allIframes = document.querySelectorAll('iframe');
448
+ const previousCount = this.cachedIframes.length;
421
449
  this.cachedIframes = Array.from(allIframes).map(iframe => ({
422
450
  element: iframe,
423
451
  rect: iframe.getBoundingClientRect()
424
452
  }));
425
453
  this.lastIframeUpdateTime = now;
454
+ if (this.debug && this.cachedIframes.length !== previousCount) {
455
+ console.log(`[IconManager] Iframe cache updated - Found ${this.cachedIframes.length} iframe(s)`, {
456
+ iframes: this.cachedIframes.map(({ rect }) => ({
457
+ left: rect.left,
458
+ top: rect.top,
459
+ right: rect.right,
460
+ bottom: rect.bottom
461
+ }))
462
+ });
463
+ }
426
464
  }
427
465
  // 检查鼠标是否在任何 iframe 上
428
- for (const { rect } of this.cachedIframes) {
466
+ for (const { rect, element } of this.cachedIframes) {
429
467
  if (clientX >= rect.left &&
430
468
  clientX <= rect.right &&
431
469
  clientY >= rect.top &&
432
470
  clientY <= rect.bottom) {
433
471
  // 鼠标在 iframe 上,立即停止拖动
434
472
  if (this.debug) {
435
- console.log('Mouse over iframe, stopping drag immediately');
473
+ console.log('[IconManager] Mouse over iframe detected, stopping drag immediately', {
474
+ mousePosition: { x: clientX, y: clientY },
475
+ iframeRect: {
476
+ left: rect.left,
477
+ top: rect.top,
478
+ right: rect.right,
479
+ bottom: rect.bottom
480
+ },
481
+ iframeSrc: element.src || 'no src'
482
+ });
436
483
  }
437
484
  this.stopDrag();
438
485
  return;
439
486
  }
440
487
  }
441
488
  }
489
+ // 更新最后记录的鼠标位置和时间戳(用于主动检测)
490
+ const now = Date.now();
491
+ const timeSinceLastUpdate = now - this.lastMousePosition.timestamp;
492
+ this.lastMousePosition = {
493
+ x: clientX,
494
+ y: clientY,
495
+ timestamp: now
496
+ };
497
+ // 如果距离上次更新超过 50ms,记录警告(可能事件丢失)
498
+ if (this.debug && timeSinceLastUpdate > 50 && this.lastMousePosition.timestamp > 0) {
499
+ console.warn(`[IconManager] Long gap between mousemove events: ${timeSinceLastUpdate}ms`, {
500
+ lastPosition: { x: this.lastMousePosition.x, y: this.lastMousePosition.y },
501
+ currentPosition: { x: clientX, y: clientY }
502
+ });
503
+ }
442
504
  // 重置超时定时器(每次移动都重置,确保只有真正停止移动时才触发超时)
443
- // 优化:缩短超时时间到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
505
+ // 优化:缩短超时时间到 50ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
444
506
  if (this.dragTimeoutId !== null) {
445
507
  window.clearTimeout(this.dragTimeoutId);
446
508
  this.dragTimeoutId = window.setTimeout(() => {
447
509
  if (this.isDragging) {
448
510
  if (this.debug) {
449
- console.log('Drag timeout, stopping drag (likely mouse moved over iframe)');
511
+ const timeSinceLastMove = Date.now() - this.lastMousePosition.timestamp;
512
+ console.warn('[IconManager] Drag timeout triggered, stopping drag (likely mouse moved over iframe)', {
513
+ timeSinceLastMove,
514
+ lastMousePosition: this.lastMousePosition,
515
+ iframeCount: this.cachedIframes.length
516
+ });
450
517
  }
451
518
  this.stopDrag();
452
519
  }
453
- }, 100); // 缩短到 100ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
520
+ }, 50); // 缩短到 50ms,更快检测到事件丢失(特别是嵌套 iframe 场景)
454
521
  }
455
522
  // 检查是否有足够的移动距离
456
523
  const deltaX = Math.abs(clientX - this.lastTouchPosition.x);
@@ -562,6 +629,14 @@ class IconManager {
562
629
  * 停止拖动
563
630
  */
564
631
  stopDrag(_e) {
632
+ const stopReason = this.isDragging ? 'drag_stopped' : 'not_dragging';
633
+ const finalPosition = this.iconElement ? {
634
+ left: this.iconElement.style.left,
635
+ top: this.iconElement.style.top,
636
+ computed: window.getComputedStyle(this.iconElement).left !== 'auto'
637
+ ? { left: window.getComputedStyle(this.iconElement).left, top: window.getComputedStyle(this.iconElement).top }
638
+ : null
639
+ } : null;
565
640
  this.cleanupDragEvents();
566
641
  if (!this.iconElement)
567
642
  return;
@@ -572,11 +647,14 @@ class IconManager {
572
647
  const touchDuration = Date.now() - this.touchStartTime;
573
648
  const isValidClick = !this.hasMoved && touchDuration < 1000 && !this.dragStarted;
574
649
  if (this.debug) {
575
- console.log('Drag end', {
650
+ console.log('[IconManager] Drag end', {
651
+ stopReason,
576
652
  hasMoved: this.hasMoved,
577
653
  dragStarted: this.dragStarted,
578
654
  touchDuration,
579
- isValidClick
655
+ isValidClick,
656
+ finalPosition,
657
+ lastMousePosition: this.lastMousePosition
580
658
  });
581
659
  }
582
660
  // 先保存点击状态,然后重置拖动状态
@@ -616,6 +694,97 @@ class IconManager {
616
694
  }, 100); // 增加延迟到 100ms,确保拖动状态完全重置
617
695
  }
618
696
  }
697
+ /**
698
+ * 启动主动检测机制:使用 requestAnimationFrame 定期检查鼠标位置
699
+ * 即使没有 mousemove 事件,也能检测到鼠标是否进入 iframe
700
+ */
701
+ startActiveDetection() {
702
+ if (this.activeDetectionRafId !== null) {
703
+ if (this.debug) {
704
+ console.log('[IconManager] Active detection already running');
705
+ }
706
+ return; // 已经在运行
707
+ }
708
+ if (this.debug) {
709
+ console.log('[IconManager] Starting active detection mechanism');
710
+ }
711
+ const checkMousePosition = () => {
712
+ if (!this.isDragging) {
713
+ if (this.debug) {
714
+ console.log('[IconManager] Active detection stopped (drag ended)');
715
+ }
716
+ this.activeDetectionRafId = null;
717
+ return;
718
+ }
719
+ // 检查是否长时间没有收到 mousemove 事件(超过 50ms)
720
+ const now = Date.now();
721
+ const timeSinceLastMove = now - this.lastMousePosition.timestamp;
722
+ if (timeSinceLastMove > 50) {
723
+ // 长时间没有收到事件,可能鼠标已经进入 iframe
724
+ // 使用最后记录的鼠标位置进行检测
725
+ const clientX = this.lastMousePosition.x;
726
+ const clientY = this.lastMousePosition.y;
727
+ if (this.debug) {
728
+ console.log(`[IconManager] Active detection: No mousemove event for ${timeSinceLastMove}ms`, {
729
+ lastMousePosition: { x: clientX, y: clientY },
730
+ timestamp: this.lastMousePosition.timestamp
731
+ });
732
+ }
733
+ // 更新 iframe 缓存(更频繁,每帧更新)
734
+ if (this.cachedIframes.length === 0 ||
735
+ now - this.lastIframeUpdateTime > this.iframeUpdateInterval) {
736
+ const allIframes = document.querySelectorAll('iframe');
737
+ this.cachedIframes = Array.from(allIframes).map(iframe => ({
738
+ element: iframe,
739
+ rect: iframe.getBoundingClientRect()
740
+ }));
741
+ this.lastIframeUpdateTime = now;
742
+ if (this.debug) {
743
+ console.log(`[IconManager] Active detection: Updated iframe cache (${this.cachedIframes.length} iframes)`);
744
+ }
745
+ }
746
+ // 检查最后记录的鼠标位置是否在任何 iframe 上
747
+ for (const { rect, element } of this.cachedIframes) {
748
+ if (clientX >= rect.left &&
749
+ clientX <= rect.right &&
750
+ clientY >= rect.top &&
751
+ clientY <= rect.bottom) {
752
+ // 鼠标在 iframe 上,立即停止拖动
753
+ if (this.debug) {
754
+ console.log('[IconManager] Active detection: Mouse over iframe detected, stopping drag immediately', {
755
+ mousePosition: { x: clientX, y: clientY },
756
+ iframeRect: {
757
+ left: rect.left,
758
+ top: rect.top,
759
+ right: rect.right,
760
+ bottom: rect.bottom
761
+ },
762
+ iframeSrc: element.src || 'no src',
763
+ timeSinceLastMove
764
+ });
765
+ }
766
+ this.stopDrag();
767
+ return;
768
+ }
769
+ }
770
+ }
771
+ // 继续检测
772
+ this.activeDetectionRafId = requestAnimationFrame(checkMousePosition);
773
+ };
774
+ this.activeDetectionRafId = requestAnimationFrame(checkMousePosition);
775
+ }
776
+ /**
777
+ * 停止主动检测机制
778
+ */
779
+ stopActiveDetection() {
780
+ if (this.activeDetectionRafId !== null) {
781
+ if (this.debug) {
782
+ console.log('[IconManager] Stopping active detection mechanism');
783
+ }
784
+ cancelAnimationFrame(this.activeDetectionRafId);
785
+ this.activeDetectionRafId = null;
786
+ }
787
+ }
619
788
  /**
620
789
  * 清理拖动事件
621
790
  */
@@ -652,6 +821,8 @@ class IconManager {
652
821
  cancelAnimationFrame(this.rafId);
653
822
  this.rafId = null;
654
823
  }
824
+ // 清理主动检测机制
825
+ this.stopActiveDetection();
655
826
  // 清理缓存
656
827
  this.cachedContainer = null;
657
828
  this.cachedContainerRect = null;