@vectoriox/iox-builder 1.4.72 → 1.4.73

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.
@@ -1431,31 +1431,59 @@ class InteractionEngineService {
1431
1431
  }
1432
1432
  attachViewportEnter(node, ix, element) {
1433
1433
  const firedSet = new Set();
1434
- // In builder mode, use .preview-scroll as root so IntersectionObserver fires
1435
- // against the canvas clipping container (scrollTop-driven) rather than the browser viewport.
1436
- const root = this.overlayService.getIntersectionRoot() ?? null;
1437
- const observer = new IntersectionObserver((entries) => {
1438
- for (const entry of entries) {
1439
- if (entry.isIntersecting) {
1440
- const key = ix.id;
1434
+ const fire = () => {
1435
+ const key = ix.id;
1436
+ if (ix.actions.some(a => a.once) && firedSet.has(key))
1437
+ return;
1438
+ if (this.hasPreStatedAncestor(element)) {
1439
+ this.deferUntilVisible(element, () => {
1441
1440
  if (ix.actions.some(a => a.once) && firedSet.has(key))
1442
- continue;
1443
- // Element is in viewport but may be inside a still-fading parent.
1444
- // Defer until all pre-stated ancestors finish their entrance animation.
1445
- if (this.hasPreStatedAncestor(element)) {
1446
- this.deferUntilVisible(element, () => {
1447
- if (ix.actions.some(a => a.once) && firedSet.has(key))
1448
- return;
1449
- firedSet.add(key);
1450
- this.runActions(node, ix.actions);
1451
- });
1452
- continue;
1453
- }
1441
+ return;
1454
1442
  firedSet.add(key);
1455
1443
  this.runActions(node, ix.actions);
1444
+ });
1445
+ return;
1446
+ }
1447
+ firedSet.add(key);
1448
+ this.runActions(node, ix.actions);
1449
+ };
1450
+ const canvasRoot = this.overlayService.getIntersectionRoot();
1451
+ if (canvasRoot) {
1452
+ // Builder mode: IntersectionObserver with a scaled/transformed canvas root is unreliable.
1453
+ // Use getBoundingClientRect against the canvas clipping container instead.
1454
+ const isInCanvasViewport = () => {
1455
+ const rootRect = canvasRoot.getBoundingClientRect();
1456
+ const elRect = element.getBoundingClientRect();
1457
+ const overlapH = Math.min(elRect.bottom, rootRect.bottom) - Math.max(elRect.top, rootRect.top);
1458
+ const elH = elRect.bottom - elRect.top;
1459
+ return elH > 0 && overlapH / elH >= 0.1;
1460
+ };
1461
+ const checkAndFire = () => { if (isInCanvasViewport())
1462
+ fire(); };
1463
+ // Initial check — defer one animation frame so layout is settled after render.
1464
+ const rafId = requestAnimationFrame(checkAndFire);
1465
+ // Lenis changes scrollTop on .preview-scroll → native scroll event fires.
1466
+ // Builder also dispatches a synthetic scroll on .preview-lenis-content.
1467
+ // Listen on both so we catch all scroll notifications.
1468
+ const lenisEl = this.overlayService.getScrollContainer();
1469
+ canvasRoot.addEventListener('scroll', checkAndFire, { passive: true });
1470
+ if (lenisEl)
1471
+ lenisEl.addEventListener('scroll', checkAndFire, { passive: true });
1472
+ return () => {
1473
+ cancelAnimationFrame(rafId);
1474
+ canvasRoot.removeEventListener('scroll', checkAndFire);
1475
+ if (lenisEl)
1476
+ lenisEl.removeEventListener('scroll', checkAndFire);
1477
+ };
1478
+ }
1479
+ // Production mode: standard IntersectionObserver against browser viewport.
1480
+ const observer = new IntersectionObserver((entries) => {
1481
+ for (const entry of entries) {
1482
+ if (entry.isIntersecting) {
1483
+ fire();
1456
1484
  }
1457
1485
  }
1458
- }, { threshold: 0.1, root });
1486
+ }, { threshold: 0.1 });
1459
1487
  observer.observe(element);
1460
1488
  return () => observer.disconnect();
1461
1489
  }