@brandocms/jupiter 3.55.0 → 4.0.0-beta.2
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/README.md +509 -54
- package/package.json +30 -18
- package/src/index.js +15 -10
- package/src/modules/Application/index.js +236 -158
- package/src/modules/Breakpoints/index.js +116 -36
- package/src/modules/Cookies/index.js +95 -64
- package/src/modules/CoverOverlay/index.js +21 -14
- package/src/modules/Dataloader/index.js +71 -24
- package/src/modules/Dataloader/url-sync.js +238 -0
- package/src/modules/Dom/index.js +24 -0
- package/src/modules/DoubleHeader/index.js +571 -0
- package/src/modules/Dropdown/index.js +108 -73
- package/src/modules/EqualHeightElements/index.js +8 -8
- package/src/modules/EqualHeightImages/index.js +15 -7
- package/src/modules/FixedHeader/index.js +116 -30
- package/src/modules/FooterReveal/index.js +5 -5
- package/src/modules/HeroSlider/index.js +231 -106
- package/src/modules/HeroVideo/index.js +72 -44
- package/src/modules/Lazyload/index.js +128 -80
- package/src/modules/Lightbox/index.js +101 -80
- package/src/modules/Links/index.js +77 -51
- package/src/modules/Looper/index.js +1737 -0
- package/src/modules/Marquee/index.js +106 -37
- package/src/modules/MobileMenu/index.js +105 -130
- package/src/modules/Moonwalk/index.js +479 -153
- package/src/modules/Parallax/index.js +280 -57
- package/src/modules/Popover/index.js +187 -17
- package/src/modules/Popup/index.js +172 -53
- package/src/modules/ScrollSpy/index.js +21 -0
- package/src/modules/StackedBoxes/index.js +8 -6
- package/src/modules/StickyHeader/index.js +394 -164
- package/src/modules/Toggler/index.js +207 -11
- package/src/modules/Typography/index.js +33 -20
- package/src/utils/motion-helpers.js +330 -0
- package/types/README.md +159 -0
- package/types/events/index.d.ts +20 -0
- package/types/index.d.ts +6 -0
- package/types/modules/Application/index.d.ts +168 -0
- package/types/modules/Breakpoints/index.d.ts +40 -0
- package/types/modules/Cookies/index.d.ts +81 -0
- package/types/modules/CoverOverlay/index.d.ts +6 -0
- package/types/modules/Dataloader/index.d.ts +38 -0
- package/types/modules/Dataloader/url-sync.d.ts +36 -0
- package/types/modules/Dom/index.d.ts +47 -0
- package/types/modules/DoubleHeader/index.d.ts +63 -0
- package/types/modules/Dropdown/index.d.ts +15 -0
- package/types/modules/EqualHeightElements/index.d.ts +8 -0
- package/types/modules/EqualHeightImages/index.d.ts +11 -0
- package/types/modules/FeatureTests/index.d.ts +27 -0
- package/types/modules/FixedHeader/index.d.ts +219 -0
- package/types/modules/Fontloader/index.d.ts +5 -0
- package/types/modules/FooterReveal/index.d.ts +5 -0
- package/types/modules/HeroSlider/index.d.ts +28 -0
- package/types/modules/HeroVideo/index.d.ts +83 -0
- package/types/modules/Lazyload/index.d.ts +80 -0
- package/types/modules/Lightbox/index.d.ts +123 -0
- package/types/modules/Links/index.d.ts +55 -0
- package/types/modules/Looper/index.d.ts +127 -0
- package/types/modules/Marquee/index.d.ts +23 -0
- package/types/modules/MobileMenu/index.d.ts +63 -0
- package/types/modules/Moonwalk/index.d.ts +322 -0
- package/types/modules/Parallax/index.d.ts +71 -0
- package/types/modules/Popover/index.d.ts +29 -0
- package/types/modules/Popup/index.d.ts +76 -0
- package/types/modules/ScrollSpy/index.d.ts +29 -0
- package/types/modules/StackedBoxes/index.d.ts +9 -0
- package/types/modules/StickyHeader/index.d.ts +220 -0
- package/types/modules/Toggler/index.d.ts +48 -0
- package/types/modules/Typography/index.d.ts +77 -0
- package/types/utils/dispatchElementEvent.d.ts +1 -0
- package/types/utils/imageIsLoaded.d.ts +1 -0
- package/types/utils/imagesAreLoaded.d.ts +1 -0
- package/types/utils/loadScript.d.ts +2 -0
- package/types/utils/prefersReducedMotion.d.ts +4 -0
- package/types/utils/rafCallback.d.ts +2 -0
- package/types/utils/zoom.d.ts +4 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Vendor imports
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
import { CSSPlugin } from 'gsap/CSSPlugin'
|
|
4
|
+
import { animate, stagger } from 'motion'
|
|
6
5
|
import _defaultsDeep from 'lodash.defaultsdeep'
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -12,10 +11,74 @@ import * as Events from '../../events'
|
|
|
12
11
|
import prefersReducedMotion from '../../utils/prefersReducedMotion'
|
|
13
12
|
import imageIsLoaded from '../../utils/imageIsLoaded'
|
|
14
13
|
import imagesAreLoaded from '../../utils/imagesAreLoaded'
|
|
14
|
+
import { set, animateAutoAlpha, delayedCall, convertEasing } from '../../utils/motion-helpers'
|
|
15
15
|
import Dom from '../Dom'
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Debug logging
|
|
19
|
+
*/
|
|
20
|
+
const DEBUG = false
|
|
21
|
+
|
|
22
|
+
function logMoonwalk(category, message, data = {}) {
|
|
23
|
+
if (DEBUG) {
|
|
24
|
+
console.log(`[Moonwalk:${category}]`, message, data)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function logComputedStyle(element, props = ['opacity', 'transform']) {
|
|
29
|
+
if (DEBUG && element) {
|
|
30
|
+
const computed = window.getComputedStyle(element)
|
|
31
|
+
const values = {}
|
|
32
|
+
props.forEach((prop) => (values[prop] = computed[prop]))
|
|
33
|
+
console.log('[Moonwalk:ComputedStyle]', element, values)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {Object} MoonwalkTransition
|
|
39
|
+
* @property {Object} from - Starting properties for the transition
|
|
40
|
+
* @property {Object} to - Ending properties for the transition
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {Object} MoonwalkWalk
|
|
45
|
+
* @property {number} [startDelay=0] - Delay before the animation starts
|
|
46
|
+
* @property {number} [interval=0.15] - Time between animations in a sequence
|
|
47
|
+
* @property {number} [duration=0.65] - Duration of the animation
|
|
48
|
+
* @property {boolean|Object} [alphaTween=false] - Whether to add a separate opacity tween
|
|
49
|
+
* @property {MoonwalkTransition} transition - The transition configuration
|
|
50
|
+
* @property {string} [sectionTargets] - CSS selector for targeting elements in named sections
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {Object} MoonwalkRun
|
|
55
|
+
* @property {number} [threshold=0] - IntersectionObserver threshold
|
|
56
|
+
* @property {Function} callback - Function called when element enters viewport
|
|
57
|
+
* @property {Function} [onExit] - Function called when element exits viewport
|
|
58
|
+
* @property {boolean} [repeated=false] - Whether the run should repeat
|
|
59
|
+
* @property {string} [rootMargin] - IntersectionObserver rootMargin
|
|
60
|
+
* @property {Function} [initialize] - Function called during initialization
|
|
61
|
+
* @property {Function} [onReady] - Function called when APPLICATION_REVEALED fires, before viewport observers start
|
|
62
|
+
*/
|
|
18
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {Object} MoonwalkOptions
|
|
66
|
+
* @property {string|Function} [on=Events.APPLICATION_REVEALED] - Event to trigger animations
|
|
67
|
+
* @property {number} [initialDelay=0.1] - Delay before starting animations
|
|
68
|
+
* @property {boolean} [clearLazyload=false] - Clear data-ll-srcset attributes
|
|
69
|
+
* @property {boolean} [clearNestedSections=true] - Remove nested data-moonwalk-section attributes
|
|
70
|
+
* @property {boolean} [clearNestedWalks=true] - Remove nested data-moonwalk attributes
|
|
71
|
+
* @property {boolean} [clearMoonwalkOnAnchors=true] - Disable animations when page loaded via anchor
|
|
72
|
+
* @property {boolean} [warnRunWithSection=true] - Warn when run and section on same element
|
|
73
|
+
* @property {string} [rootMargin='-10% 0%'] - Default IntersectionObserver rootMargin
|
|
74
|
+
* @property {number} [threshold=0] - Default IntersectionObserver threshold
|
|
75
|
+
* @property {boolean} [uniqueIds=false] - Generate unique IDs for moonwalk elements
|
|
76
|
+
* @property {boolean} [addIndexes=false] - Add index attributes to elements
|
|
77
|
+
* @property {Object.<string, MoonwalkRun>} [runs={}] - Run configurations
|
|
78
|
+
* @property {Object.<string, MoonwalkWalk>} walks - Walk configurations
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/** @type {MoonwalkOptions} */
|
|
19
82
|
const DEFAULT_OPTIONS = {
|
|
20
83
|
/**
|
|
21
84
|
* If your app needs to do some initialization before the
|
|
@@ -102,7 +165,15 @@ const DEFAULT_OPTIONS = {
|
|
|
102
165
|
},
|
|
103
166
|
}
|
|
104
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Moonwalk animation system for scroll-based reveal animations
|
|
170
|
+
*/
|
|
105
171
|
export default class Moonwalk {
|
|
172
|
+
/**
|
|
173
|
+
* @param {Object} app - The application instance
|
|
174
|
+
* @param {MoonwalkOptions} [opts={}] - Configuration options
|
|
175
|
+
* @param {HTMLElement} [container=document.body] - Container element
|
|
176
|
+
*/
|
|
106
177
|
constructor(app, opts = {}, container = document.body) {
|
|
107
178
|
this.app = app
|
|
108
179
|
this.opts = _defaultsDeep(opts, DEFAULT_OPTIONS)
|
|
@@ -293,6 +364,7 @@ export default class Moonwalk {
|
|
|
293
364
|
el: run,
|
|
294
365
|
threshold: foundRun.threshold || 0,
|
|
295
366
|
initialize: foundRun.initialize,
|
|
367
|
+
onReady: foundRun.onReady,
|
|
296
368
|
callback: foundRun.callback,
|
|
297
369
|
onExit: foundRun.onExit,
|
|
298
370
|
repeated: foundRun.repeated,
|
|
@@ -335,16 +407,15 @@ export default class Moonwalk {
|
|
|
335
407
|
this.addIndexes(section)
|
|
336
408
|
}
|
|
337
409
|
|
|
338
|
-
const timeline = gsap.timeline({
|
|
339
|
-
autoRemoveChildren: false,
|
|
340
|
-
smoothChildTiming: false,
|
|
341
|
-
})
|
|
342
|
-
|
|
343
410
|
return {
|
|
344
411
|
id: Math.random().toString(36).substring(7),
|
|
345
412
|
el: section,
|
|
346
413
|
name: section.getAttribute('data-moonwalk-section') || null,
|
|
347
|
-
|
|
414
|
+
animation: {
|
|
415
|
+
lastDelay: 0,
|
|
416
|
+
lastDuration: 0,
|
|
417
|
+
lastStartTime: null,
|
|
418
|
+
},
|
|
348
419
|
observer: null,
|
|
349
420
|
stage: {
|
|
350
421
|
name: section.getAttribute('data-moonwalk-stage') || null,
|
|
@@ -430,14 +501,32 @@ export default class Moonwalk {
|
|
|
430
501
|
section.children = this.orderChildren(section.el.children)
|
|
431
502
|
}
|
|
432
503
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
504
|
+
// Only set initial states for JS animations (transition !== null)
|
|
505
|
+
if (sectionWalk.transition) {
|
|
506
|
+
const fromTransition = sectionWalk.alphaTween
|
|
507
|
+
? {
|
|
508
|
+
...sectionWalk.transition.from,
|
|
509
|
+
opacity: 0,
|
|
510
|
+
}
|
|
511
|
+
: sectionWalk.transition.from
|
|
512
|
+
|
|
513
|
+
logMoonwalk('InitialState', 'Setting initial state for named section', {
|
|
514
|
+
section: section.name,
|
|
515
|
+
childCount: section.children.length,
|
|
516
|
+
fromTransition,
|
|
517
|
+
})
|
|
518
|
+
set(section.children, fromTransition)
|
|
519
|
+
|
|
520
|
+
// Check if styles were actually applied
|
|
521
|
+
if (section.children.length > 0) {
|
|
522
|
+
logComputedStyle(section.children[0])
|
|
523
|
+
}
|
|
524
|
+
} else {
|
|
525
|
+
logMoonwalk('InitialState', 'Skipping initial state for CSS-only section', {
|
|
526
|
+
section: section.name,
|
|
527
|
+
childCount: section.children.length,
|
|
528
|
+
})
|
|
529
|
+
}
|
|
441
530
|
}
|
|
442
531
|
|
|
443
532
|
if (section.stage.name) {
|
|
@@ -449,7 +538,12 @@ export default class Moonwalk {
|
|
|
449
538
|
section.stage.name
|
|
450
539
|
)
|
|
451
540
|
} else {
|
|
452
|
-
|
|
541
|
+
logMoonwalk('InitialState', 'Setting stage initial state', {
|
|
542
|
+
stage: section.stage.name,
|
|
543
|
+
from: stageTween.transition.from,
|
|
544
|
+
})
|
|
545
|
+
set(section.el, stageTween.transition.from)
|
|
546
|
+
logComputedStyle(section.el)
|
|
453
547
|
}
|
|
454
548
|
}
|
|
455
549
|
|
|
@@ -479,12 +573,9 @@ export default class Moonwalk {
|
|
|
479
573
|
// run stage tween
|
|
480
574
|
const stageTween = walks[section.stage.name]
|
|
481
575
|
|
|
482
|
-
|
|
483
|
-
...stageTween.transition.to,
|
|
576
|
+
animate(entry.target, stageTween.transition.to, {
|
|
484
577
|
duration: stageTween.duration,
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
section.timeline.to(entry.target, to, 0)
|
|
578
|
+
})
|
|
488
579
|
section.stage.firstTween = true
|
|
489
580
|
}
|
|
490
581
|
}
|
|
@@ -499,44 +590,75 @@ export default class Moonwalk {
|
|
|
499
590
|
)
|
|
500
591
|
}
|
|
501
592
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
tween.alphaTween
|
|
508
|
-
|
|
509
|
-
|
|
593
|
+
logMoonwalk('SectionObserver', 'Named section triggered', {
|
|
594
|
+
sectionName: section.name,
|
|
595
|
+
childCount: section.children.length,
|
|
596
|
+
interval: tween.interval,
|
|
597
|
+
duration: tween.duration,
|
|
598
|
+
hasAlphaTween: !!tween.alphaTween,
|
|
599
|
+
isCssOnly: !tween.transition,
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
// Check if this is CSS-only animation (transition: null)
|
|
603
|
+
if (!tween.transition) {
|
|
604
|
+
// CSS-only mode - stagger adding the data-moonwalked attribute
|
|
605
|
+
logMoonwalk('SectionObserver', 'Using CSS-only mode', {
|
|
606
|
+
sectionName: section.name,
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
section.children.forEach((child, index) => {
|
|
610
|
+
const delay = (tween.startDelay || 0) + index * tween.interval
|
|
611
|
+
delayedCall(delay, () => {
|
|
612
|
+
child.setAttribute('data-moonwalked', '')
|
|
613
|
+
})
|
|
614
|
+
})
|
|
615
|
+
} else {
|
|
616
|
+
// JS animation mode
|
|
617
|
+
if (typeof tween.alphaTween === 'object') {
|
|
618
|
+
tween.alphaTween.duration = tween.alphaTween.duration
|
|
619
|
+
? tween.alphaTween.duration
|
|
620
|
+
: tween.duration
|
|
621
|
+
} else if (tween.alphaTween === true) {
|
|
622
|
+
tween.alphaTween = {
|
|
623
|
+
duration: tween.duration,
|
|
624
|
+
ease: 'easeIn',
|
|
625
|
+
}
|
|
510
626
|
}
|
|
511
|
-
}
|
|
512
627
|
|
|
513
|
-
|
|
514
|
-
tween.transition.to
|
|
515
|
-
|
|
516
|
-
delay: tween.startDelay,
|
|
517
|
-
}
|
|
518
|
-
}
|
|
628
|
+
// Extract ease from to values and convert for Motion.js
|
|
629
|
+
const { ease: tweenEase, ...toValues } = tween.transition.to
|
|
630
|
+
const convertedEase = convertEasing(tweenEase || 'easeOut')
|
|
519
631
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
632
|
+
const animationOptions = {
|
|
633
|
+
duration: tween.duration,
|
|
634
|
+
ease: convertedEase,
|
|
635
|
+
delay: stagger(tween.interval, {
|
|
636
|
+
startDelay: tween.startDelay || 0,
|
|
637
|
+
}),
|
|
638
|
+
}
|
|
527
639
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
640
|
+
logMoonwalk('SectionObserver', 'Starting stagger animation', {
|
|
641
|
+
sectionName: section.name,
|
|
642
|
+
to: toValues,
|
|
643
|
+
ease: convertedEase,
|
|
644
|
+
options: animationOptions,
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
animate(section.children, toValues, animationOptions)
|
|
648
|
+
|
|
649
|
+
if (tween.alphaTween) {
|
|
650
|
+
animate(
|
|
651
|
+
section.children,
|
|
652
|
+
{ opacity: 1 },
|
|
653
|
+
{
|
|
654
|
+
duration: tween.alphaTween.duration,
|
|
655
|
+
ease: convertEasing(tween.alphaTween.ease || 'easeIn'),
|
|
656
|
+
delay: stagger(tween.interval, {
|
|
657
|
+
startDelay: tween.startDelay || 0,
|
|
658
|
+
}),
|
|
659
|
+
}
|
|
660
|
+
)
|
|
661
|
+
}
|
|
540
662
|
}
|
|
541
663
|
}
|
|
542
664
|
|
|
@@ -578,6 +700,64 @@ export default class Moonwalk {
|
|
|
578
700
|
})
|
|
579
701
|
}
|
|
580
702
|
|
|
703
|
+
/**
|
|
704
|
+
* Calculate the delay for the next animation in the section.
|
|
705
|
+
* This replaces GSAP's timeline.recent() logic.
|
|
706
|
+
*
|
|
707
|
+
* @param {*} section - The section object
|
|
708
|
+
* @param {*} duration - Duration of the animation to add
|
|
709
|
+
* @param {*} overlap - How much the animations should overlap
|
|
710
|
+
* @returns {number} The delay in seconds
|
|
711
|
+
*/
|
|
712
|
+
calculateDelay(section, duration, overlap) {
|
|
713
|
+
if (!section.animation.lastStartTime) {
|
|
714
|
+
// First animation in section
|
|
715
|
+
logMoonwalk('DelayCalc', 'First animation in section', { delay: 0 })
|
|
716
|
+
return 0
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const now = performance.now()
|
|
720
|
+
const elapsed = (now - section.animation.lastStartTime) / 1000
|
|
721
|
+
// overlap is negative when animations should stagger (start before previous ends)
|
|
722
|
+
// So we ADD overlap (which is negative) to get the correct next start time
|
|
723
|
+
const idealNextStart =
|
|
724
|
+
section.animation.lastDelay + section.animation.lastDuration + overlap
|
|
725
|
+
const actualDelay = Math.max(0, idealNextStart - elapsed)
|
|
726
|
+
|
|
727
|
+
logMoonwalk('DelayCalc', 'Calculating delay', {
|
|
728
|
+
elapsed: elapsed.toFixed(3),
|
|
729
|
+
lastDelay: section.animation.lastDelay,
|
|
730
|
+
lastDuration: section.animation.lastDuration,
|
|
731
|
+
overlap,
|
|
732
|
+
idealNextStart,
|
|
733
|
+
actualDelay: actualDelay.toFixed(3),
|
|
734
|
+
})
|
|
735
|
+
|
|
736
|
+
return actualDelay
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Update the animation state after adding an animation.
|
|
741
|
+
*
|
|
742
|
+
* @param {*} section - The section object
|
|
743
|
+
* @param {*} delay - The delay that was used
|
|
744
|
+
* @param {*} duration - The duration of the animation
|
|
745
|
+
*/
|
|
746
|
+
updateAnimationState(section, delay, duration) {
|
|
747
|
+
const previousState = { ...section.animation }
|
|
748
|
+
|
|
749
|
+
section.animation.lastDelay = delay
|
|
750
|
+
section.animation.lastDuration = duration
|
|
751
|
+
section.animation.lastStartTime = performance.now()
|
|
752
|
+
|
|
753
|
+
logMoonwalk('StateUpdate', 'Updating animation state', {
|
|
754
|
+
delay,
|
|
755
|
+
duration,
|
|
756
|
+
previousState,
|
|
757
|
+
newState: { ...section.animation },
|
|
758
|
+
})
|
|
759
|
+
}
|
|
760
|
+
|
|
581
761
|
onReady() {
|
|
582
762
|
if (this.opts.initialDelay) {
|
|
583
763
|
setTimeout(() => {
|
|
@@ -595,6 +775,14 @@ export default class Moonwalk {
|
|
|
595
775
|
ready() {
|
|
596
776
|
const { opts } = this
|
|
597
777
|
|
|
778
|
+
// Execute onReady callbacks for all runs
|
|
779
|
+
for (let idx = 0; idx < this.runs.length; idx += 1) {
|
|
780
|
+
const run = this.runs[idx]
|
|
781
|
+
if (run && run.onReady) {
|
|
782
|
+
run.onReady(run.el)
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
598
786
|
for (let idx = 0; idx < this.runs.length; idx += 1) {
|
|
599
787
|
const run = this.runs[idx]
|
|
600
788
|
|
|
@@ -637,7 +825,29 @@ export default class Moonwalk {
|
|
|
637
825
|
}
|
|
638
826
|
|
|
639
827
|
section.elements = section.el.querySelectorAll('[data-moonwalk]')
|
|
640
|
-
|
|
828
|
+
|
|
829
|
+
// Only set initial states and observe individual elements for unnamed sections
|
|
830
|
+
// Named sections are observed at the section level via sectionObserver
|
|
831
|
+
if (!section.name) {
|
|
832
|
+
// Set initial states for tweenJS elements BEFORE observing
|
|
833
|
+
section.elements.forEach((element) => {
|
|
834
|
+
const walkName = element.getAttribute('data-moonwalk')
|
|
835
|
+
const cfg = !walkName.length
|
|
836
|
+
? opts.walks.default
|
|
837
|
+
: opts.walks[walkName]
|
|
838
|
+
|
|
839
|
+
// Only set initial state if this uses tweenJS (has transition property)
|
|
840
|
+
if (cfg && cfg.transition) {
|
|
841
|
+
logMoonwalk('InitialState', 'Setting initial state for individual element', {
|
|
842
|
+
walkName: walkName || 'default',
|
|
843
|
+
from: cfg.transition.from,
|
|
844
|
+
})
|
|
845
|
+
set(element, cfg.transition.from)
|
|
846
|
+
}
|
|
847
|
+
})
|
|
848
|
+
|
|
849
|
+
section.elements.forEach((box) => section.observer.observe(box))
|
|
850
|
+
}
|
|
641
851
|
}
|
|
642
852
|
}
|
|
643
853
|
|
|
@@ -648,15 +858,56 @@ export default class Moonwalk {
|
|
|
648
858
|
* @param {*} rootMargin
|
|
649
859
|
*/
|
|
650
860
|
runObserver(run, rootMargin) {
|
|
861
|
+
// Store the previous positions of observed elements to compare for exit direction
|
|
862
|
+
const elementPositions = new WeakMap()
|
|
863
|
+
|
|
651
864
|
return new IntersectionObserver(
|
|
652
865
|
(entries, self) => {
|
|
653
866
|
for (let i = 0; i < entries.length; i += 1) {
|
|
654
867
|
const entry = entries[i]
|
|
868
|
+
|
|
869
|
+
// Store the element's current position in the viewport
|
|
870
|
+
const boundingRect = entry.boundingClientRect
|
|
871
|
+
const viewportHeight = window.innerHeight
|
|
872
|
+
const viewportWidth = window.innerWidth
|
|
873
|
+
|
|
655
874
|
if (entry.isIntersecting && run.callback) {
|
|
875
|
+
// Calculate entry direction
|
|
876
|
+
let meta = { direction: null }
|
|
877
|
+
|
|
878
|
+
// Use the app's scroll direction for reliable detection
|
|
879
|
+
// If scrollDirection is null, the element was likely revealed on initial load
|
|
880
|
+
if (this.app.state && this.app.state.scrollDirection) {
|
|
881
|
+
// Map scroll direction to viewport entry direction
|
|
882
|
+
switch (this.app.state.scrollDirection) {
|
|
883
|
+
case 'down':
|
|
884
|
+
meta.direction = 'bottom' // When scrolling down, elements enter from bottom
|
|
885
|
+
break
|
|
886
|
+
case 'up':
|
|
887
|
+
meta.direction = 'top' // When scrolling up, elements enter from top
|
|
888
|
+
break
|
|
889
|
+
case 'right':
|
|
890
|
+
meta.direction = 'left' // When scrolling right, elements enter from left
|
|
891
|
+
break
|
|
892
|
+
case 'left':
|
|
893
|
+
meta.direction = 'right' // When scrolling left, elements enter from right
|
|
894
|
+
break
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
// If no scroll direction is available, direction remains null
|
|
898
|
+
|
|
899
|
+
// Store the element's position when it enters the viewport
|
|
900
|
+
elementPositions.set(entry.target, {
|
|
901
|
+
top: boundingRect.top,
|
|
902
|
+
bottom: boundingRect.bottom,
|
|
903
|
+
left: boundingRect.left,
|
|
904
|
+
right: boundingRect.right
|
|
905
|
+
})
|
|
906
|
+
|
|
656
907
|
const runRepeated = entry.target.hasAttribute(
|
|
657
908
|
'data-moonwalk-run-triggered'
|
|
658
909
|
)
|
|
659
|
-
run.callback(entry.target, runRepeated)
|
|
910
|
+
run.callback(entry.target, runRepeated, meta)
|
|
660
911
|
entry.target.setAttribute('data-moonwalk-run-triggered', '')
|
|
661
912
|
if (!run.onExit && !run.repeated) {
|
|
662
913
|
self.unobserve(entry.target)
|
|
@@ -670,7 +921,42 @@ export default class Moonwalk {
|
|
|
670
921
|
'data-moonwalk-run-exit-triggered'
|
|
671
922
|
)
|
|
672
923
|
entry.target.setAttribute('data-moonwalk-run-exit-triggered', '')
|
|
673
|
-
|
|
924
|
+
|
|
925
|
+
// Calculate exit direction
|
|
926
|
+
let meta = { direction: null }
|
|
927
|
+
|
|
928
|
+
// Use the app's scroll direction for reliable detection
|
|
929
|
+
// For exit direction, it's the opposite of the entry direction for the same scroll
|
|
930
|
+
if (this.app.state && this.app.state.scrollDirection) {
|
|
931
|
+
// Map scroll direction to viewport exit direction
|
|
932
|
+
switch (this.app.state.scrollDirection) {
|
|
933
|
+
case 'down':
|
|
934
|
+
meta.direction = 'top' // When scrolling down, elements exit from top
|
|
935
|
+
break
|
|
936
|
+
case 'up':
|
|
937
|
+
meta.direction = 'bottom' // When scrolling up, elements exit from bottom
|
|
938
|
+
break
|
|
939
|
+
case 'right':
|
|
940
|
+
meta.direction = 'right' // When scrolling right, elements exit from right
|
|
941
|
+
break
|
|
942
|
+
case 'left':
|
|
943
|
+
meta.direction = 'left' // When scrolling left, elements exit from left
|
|
944
|
+
break
|
|
945
|
+
}
|
|
946
|
+
} else {
|
|
947
|
+
// If no scroll direction is available, use the simplest position-based check
|
|
948
|
+
if (boundingRect.bottom <= 0) {
|
|
949
|
+
meta.direction = 'top'
|
|
950
|
+
} else if (boundingRect.top >= viewportHeight) {
|
|
951
|
+
meta.direction = 'bottom'
|
|
952
|
+
} else if (boundingRect.right <= 0) {
|
|
953
|
+
meta.direction = 'left'
|
|
954
|
+
} else if (boundingRect.left >= viewportWidth) {
|
|
955
|
+
meta.direction = 'right'
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
run.onExit(entry.target, runExited, meta)
|
|
674
960
|
if (!run.repeated) {
|
|
675
961
|
self.unobserve(entry.target)
|
|
676
962
|
}
|
|
@@ -703,16 +989,25 @@ export default class Moonwalk {
|
|
|
703
989
|
if (entry.isIntersecting || entry.intersectionRatio > 0) {
|
|
704
990
|
section.running = true
|
|
705
991
|
|
|
706
|
-
if (entry.target.dataset.moonwalkId) {
|
|
707
|
-
console.debug('-- intersecting', entry.target.dataset.moonwalkId)
|
|
708
|
-
}
|
|
709
|
-
|
|
710
992
|
const walkName = entry.target.getAttribute('data-moonwalk')
|
|
993
|
+
const targetId =
|
|
994
|
+
entry.target.getAttribute('data-testid') ||
|
|
995
|
+
walkName ||
|
|
996
|
+
entry.target.className
|
|
997
|
+
|
|
998
|
+
logMoonwalk('Observer', 'Element entered viewport', {
|
|
999
|
+
target: targetId,
|
|
1000
|
+
walkName,
|
|
1001
|
+
isIntersecting: entry.isIntersecting,
|
|
1002
|
+
intersectionRatio: entry.intersectionRatio,
|
|
1003
|
+
})
|
|
711
1004
|
const cfg = !walkName.length
|
|
712
1005
|
? opts.walks.default
|
|
713
1006
|
: opts.walks[walkName]
|
|
714
1007
|
|
|
715
|
-
const { duration, transition,
|
|
1008
|
+
const { duration, transition, startDelay } = cfg
|
|
1009
|
+
// Default interval to 0.15 if not specified (same as default walk)
|
|
1010
|
+
const interval = cfg.interval !== undefined ? cfg.interval : 0.15
|
|
716
1011
|
|
|
717
1012
|
let { alphaTween } = cfg
|
|
718
1013
|
let overlap = (duration - interval) * -1 // flip it
|
|
@@ -729,27 +1024,36 @@ export default class Moonwalk {
|
|
|
729
1024
|
} else if (alphaTween === true) {
|
|
730
1025
|
alphaTween = {
|
|
731
1026
|
duration,
|
|
732
|
-
ease: '
|
|
1027
|
+
ease: 'easeIn',
|
|
733
1028
|
}
|
|
734
1029
|
}
|
|
735
1030
|
|
|
736
|
-
const tween = transition ? this.tweenJS : this.tweenCSS
|
|
737
|
-
|
|
738
1031
|
const tweenFn = () => {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
1032
|
+
if (transition) {
|
|
1033
|
+
this.tweenJS(
|
|
1034
|
+
section,
|
|
1035
|
+
entry.target,
|
|
1036
|
+
duration,
|
|
1037
|
+
interval,
|
|
1038
|
+
transition,
|
|
1039
|
+
overlap,
|
|
1040
|
+
alphaTween
|
|
1041
|
+
)
|
|
1042
|
+
} else {
|
|
1043
|
+
this.tweenCSS(
|
|
1044
|
+
section,
|
|
1045
|
+
entry.target,
|
|
1046
|
+
duration,
|
|
1047
|
+
interval,
|
|
1048
|
+
transition,
|
|
1049
|
+
overlap
|
|
1050
|
+
)
|
|
1051
|
+
}
|
|
748
1052
|
}
|
|
749
1053
|
|
|
750
1054
|
const wrappedTweenFn = () => {
|
|
751
1055
|
if (startDelay) {
|
|
752
|
-
|
|
1056
|
+
delayedCall(startDelay, tweenFn)
|
|
753
1057
|
} else {
|
|
754
1058
|
tweenFn()
|
|
755
1059
|
}
|
|
@@ -813,56 +1117,90 @@ export default class Moonwalk {
|
|
|
813
1117
|
tweenOverlap,
|
|
814
1118
|
alphaTween
|
|
815
1119
|
) {
|
|
816
|
-
|
|
817
|
-
|
|
1120
|
+
const targetId =
|
|
1121
|
+
target.getAttribute('data-testid') ||
|
|
1122
|
+
target.getAttribute('data-moonwalk') ||
|
|
1123
|
+
target.className
|
|
1124
|
+
|
|
1125
|
+
logMoonwalk('TweenJS', 'Starting tweenJS', {
|
|
1126
|
+
target: targetId,
|
|
1127
|
+
duration: tweenDuration,
|
|
1128
|
+
overlap: tweenOverlap,
|
|
1129
|
+
hasAlphaTween: !!alphaTween,
|
|
1130
|
+
})
|
|
818
1131
|
|
|
819
1132
|
if (Dom.hasAttribute(target, 'data-moonwalked')) {
|
|
1133
|
+
logMoonwalk('TweenJS', 'Already moonwalked, skipping', { target: targetId })
|
|
820
1134
|
return
|
|
821
1135
|
}
|
|
822
1136
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
/* Still time, add as normal overlap at the end */
|
|
834
|
-
tweenPosition = () => `>${tweenOverlap}`
|
|
835
|
-
} else {
|
|
836
|
-
/* Won't make it */
|
|
837
|
-
tweenPosition = () => section.timeline.time()
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
} else {
|
|
841
|
-
tweenPosition = () => '>'
|
|
842
|
-
}
|
|
1137
|
+
// Calculate delay using our new helper method
|
|
1138
|
+
const delay = this.calculateDelay(section, tweenDuration, tweenOverlap)
|
|
1139
|
+
|
|
1140
|
+
// Initial state should already be set during ready()
|
|
1141
|
+
// Only log for debugging
|
|
1142
|
+
logMoonwalk('TweenJS', 'Element should already have initial state', {
|
|
1143
|
+
target: targetId,
|
|
1144
|
+
expectedFrom: tweenTransition.from,
|
|
1145
|
+
})
|
|
1146
|
+
logComputedStyle(target, ['opacity', 'transform', 'x', 'y'])
|
|
843
1147
|
|
|
844
|
-
|
|
1148
|
+
// Extract ease from to values (GSAP format) and convert to Motion easing option
|
|
1149
|
+
const { ease, ...toValues } = tweenTransition.to
|
|
1150
|
+
const easingOption = convertEasing(ease || 'easeOut')
|
|
845
1151
|
|
|
846
|
-
|
|
847
|
-
|
|
1152
|
+
logMoonwalk('TweenJS', 'Starting animation', {
|
|
1153
|
+
target: targetId,
|
|
1154
|
+
toValues,
|
|
848
1155
|
duration: tweenDuration,
|
|
849
|
-
|
|
850
|
-
|
|
1156
|
+
delay: delay.toFixed(3),
|
|
1157
|
+
ease: easingOption,
|
|
1158
|
+
})
|
|
851
1159
|
|
|
852
|
-
|
|
1160
|
+
// Animate to final state
|
|
1161
|
+
const animation = animate(target, toValues, {
|
|
1162
|
+
duration: tweenDuration,
|
|
1163
|
+
delay,
|
|
1164
|
+
ease: easingOption,
|
|
1165
|
+
})
|
|
853
1166
|
|
|
1167
|
+
// Use .finished promise for completion callback
|
|
1168
|
+
if (animation && animation.finished) {
|
|
1169
|
+
animation.finished
|
|
1170
|
+
.then(() => {
|
|
1171
|
+
logMoonwalk('TweenJS', 'Animation completed', { target: targetId })
|
|
1172
|
+
target.setAttribute('data-moonwalked', '')
|
|
1173
|
+
})
|
|
1174
|
+
.catch((err) => {
|
|
1175
|
+
// Animation cancelled or failed, still mark as walked
|
|
1176
|
+
logMoonwalk('TweenJS', 'Animation failed/cancelled', {
|
|
1177
|
+
target: targetId,
|
|
1178
|
+
error: err,
|
|
1179
|
+
})
|
|
1180
|
+
target.setAttribute('data-moonwalked', '')
|
|
1181
|
+
})
|
|
1182
|
+
} else {
|
|
1183
|
+
// No animation object returned, mark immediately
|
|
1184
|
+
logMoonwalk('TweenJS', 'No animation object returned', { target: targetId })
|
|
1185
|
+
target.setAttribute('data-moonwalked', '')
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// Optional separate alpha animation
|
|
854
1189
|
if (alphaTween) {
|
|
855
|
-
|
|
856
|
-
target,
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
)
|
|
1190
|
+
logMoonwalk('TweenJS', 'Adding alpha tween', {
|
|
1191
|
+
target: targetId,
|
|
1192
|
+
duration: alphaTween.duration,
|
|
1193
|
+
delay: (delay + (alphaTween.delay || 0)).toFixed(3),
|
|
1194
|
+
})
|
|
1195
|
+
animate(target, { opacity: 1 }, {
|
|
1196
|
+
duration: alphaTween.duration,
|
|
1197
|
+
ease: convertEasing(alphaTween.ease || 'easeIn'),
|
|
1198
|
+
delay: delay + (alphaTween.delay || 0),
|
|
1199
|
+
})
|
|
865
1200
|
}
|
|
1201
|
+
|
|
1202
|
+
// Update animation state for next element
|
|
1203
|
+
this.updateAnimationState(section, delay, tweenDuration)
|
|
866
1204
|
}
|
|
867
1205
|
|
|
868
1206
|
/**
|
|
@@ -882,47 +1220,35 @@ export default class Moonwalk {
|
|
|
882
1220
|
tweenTransition,
|
|
883
1221
|
tweenOverlap
|
|
884
1222
|
) {
|
|
885
|
-
let tweenPosition
|
|
886
|
-
const startingPoint = tweenDuration - tweenOverlap * -1
|
|
887
|
-
|
|
888
1223
|
if (Dom.hasAttribute(target, 'data-moonwalked')) {
|
|
889
1224
|
return
|
|
890
1225
|
}
|
|
891
1226
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
so insert at current time in timeline instead */
|
|
899
|
-
tweenPosition = () => section.timeline.time()
|
|
900
|
-
} else {
|
|
901
|
-
if (currentTime + tweenOverlap * -1 < lastTweenEndTime) {
|
|
902
|
-
/* Still time, add as normal overlap at the end */
|
|
903
|
-
tweenPosition = () => `>${tweenOverlap}`
|
|
904
|
-
} else {
|
|
905
|
-
/* Won't make it */
|
|
906
|
-
tweenPosition = () => section.timeline.time()
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
} else {
|
|
910
|
-
tweenPosition = () => '>'
|
|
911
|
-
}
|
|
1227
|
+
// Calculate delay using our helper method for stagger effect
|
|
1228
|
+
const calculatedDelay = this.calculateDelay(
|
|
1229
|
+
section,
|
|
1230
|
+
tweenDuration,
|
|
1231
|
+
tweenOverlap
|
|
1232
|
+
)
|
|
912
1233
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
)
|
|
926
|
-
|
|
1234
|
+
const targetId = target.getAttribute('data-testid') || target.className
|
|
1235
|
+
|
|
1236
|
+
logMoonwalk('TweenCSS', 'Scheduling CSS animation', {
|
|
1237
|
+
target: targetId,
|
|
1238
|
+
delay: calculatedDelay.toFixed(3),
|
|
1239
|
+
duration: tweenDuration,
|
|
1240
|
+
})
|
|
1241
|
+
|
|
1242
|
+
// Add class after delay to trigger CSS transition
|
|
1243
|
+
delayedCall(calculatedDelay, () => {
|
|
1244
|
+
logMoonwalk('TweenCSS', 'Adding moonwalked attribute', {
|
|
1245
|
+
target: targetId,
|
|
1246
|
+
})
|
|
1247
|
+
target.classList.add('moonwalked')
|
|
1248
|
+
target.setAttribute('data-moonwalked', '')
|
|
1249
|
+
})
|
|
1250
|
+
|
|
1251
|
+
// Update animation state for next element in section
|
|
1252
|
+
this.updateAnimationState(section, calculatedDelay, tweenDuration)
|
|
927
1253
|
}
|
|
928
1254
|
}
|