@thinkpixellab-public/px-vue 4.1.26 → 4.1.27

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,60 @@
1
+ // composables/loadGsap.js
2
+
3
+ /**
4
+ * Lazy loading utilities for GSAP and ScrollTrigger
5
+ *
6
+ * These functions provide race-condition-safe loading of GSAP libraries.
7
+ * They ensure single initialization even with concurrent calls.
8
+ */
9
+
10
+ let gsapPromise = null;
11
+ let scrollTriggerPromise = null;
12
+
13
+ /**
14
+ * Loads the GSAP library lazily
15
+ *
16
+ * @returns {Promise} Promise that resolves to the GSAP instance
17
+ *
18
+ * @example
19
+ * // Direct usage (not recommended - use useGsap composable instead)
20
+ * import { loadGsap } from '@thinkpixellab-public/px-vue/composables/loadGsap';
21
+ *
22
+ * const gsap = await loadGsap();
23
+ * gsap.to('.element', { x: 100 });
24
+ */
25
+ export const loadGsap = async () => {
26
+ if (!gsapPromise) {
27
+ gsapPromise = import('gsap').then(module => module.gsap);
28
+ }
29
+ return gsapPromise;
30
+ };
31
+
32
+ /**
33
+ * Loads GSAP with ScrollTrigger plugin registered
34
+ *
35
+ * @returns {Promise} Promise that resolves to { gsap, ScrollTrigger }
36
+ *
37
+ * @example
38
+ * // Direct usage (not recommended - use useScrollAnimation composable instead)
39
+ * import { loadScrollTrigger } from '@thinkpixellab-public/px-vue/composables/loadGsap';
40
+ *
41
+ * const { gsap, ScrollTrigger } = await loadScrollTrigger();
42
+ *
43
+ * gsap.to('.element', {
44
+ * x: 100,
45
+ * scrollTrigger: {
46
+ * trigger: '.element',
47
+ * start: 'top center'
48
+ * }
49
+ * });
50
+ */
51
+ export const loadScrollTrigger = async () => {
52
+ if (!scrollTriggerPromise) {
53
+ scrollTriggerPromise = (async () => {
54
+ const [gsap, { ScrollTrigger }] = await Promise.all([loadGsap(), import('gsap/ScrollTrigger')]);
55
+ gsap.registerPlugin(ScrollTrigger);
56
+ return { gsap, ScrollTrigger };
57
+ })();
58
+ }
59
+ return scrollTriggerPromise;
60
+ };
@@ -0,0 +1,82 @@
1
+ // composables/loadLenis.js
2
+
3
+ /**
4
+ * Lazy loading utilities for Lenis smooth scrolling
5
+ *
6
+ * These functions provide race-condition-safe loading and initialization of Lenis.
7
+ * The instance is shared across all components for consistent scroll behavior.
8
+ */
9
+
10
+ let lenisPromise = null;
11
+ let lenisInstancePromise = null;
12
+
13
+ /**
14
+ * Loads the Lenis class lazily
15
+ *
16
+ * @returns {Promise} Promise that resolves to the Lenis constructor
17
+ *
18
+ * @example
19
+ * // Direct usage (not recommended - use useLenis composable instead)
20
+ * import { loadLenis } from '@thinkpixellab-public/px-vue/composables/loadLenis';
21
+ *
22
+ * const Lenis = await loadLenis();
23
+ * const customLenis = new Lenis({
24
+ * duration: 2,
25
+ * smoothWheel: true
26
+ * });
27
+ */
28
+ export const loadLenis = async () => {
29
+ if (!lenisPromise) {
30
+ lenisPromise = import('lenis').then(module => module.default);
31
+ }
32
+ return lenisPromise;
33
+ };
34
+
35
+ /**
36
+ * Loads a shared Lenis instance with default configuration
37
+ *
38
+ * Returns the same instance across all calls for consistent scroll behavior.
39
+ * Starts with autoRaf enabled - useScrollAnimation will take over RAF control when needed.
40
+ *
41
+ * @returns {Promise} Promise that resolves to the shared Lenis instance
42
+ *
43
+ * @example
44
+ * // Direct usage (not recommended - use useLenis composable instead)
45
+ * import { loadLenisInstance } from '@thinkpixellab-public/px-vue/composables/loadLenis';
46
+ *
47
+ * const lenis = await loadLenisInstance();
48
+ *
49
+ * // smooth scroll to element
50
+ * lenis.scrollTo('#section-2', { duration: 1.5 });
51
+ *
52
+ * // listen to scroll events
53
+ * lenis.on('scroll', (e) => {
54
+ * console.log('Scroll position:', e.scroll);
55
+ * });
56
+ *
57
+ * @example
58
+ * // Configuration details
59
+ * // The shared instance is created with:
60
+ * // {
61
+ * // smoothWheel: false, // disabled for better performance
62
+ * // smoothTouch: false, // disabled for mobile compatibility
63
+ * // autoRaf: true // auto RAF until ScrollTrigger takes over
64
+ * // }
65
+ */
66
+ export const loadLenisInstance = async () => {
67
+ if (!lenisInstancePromise) {
68
+ lenisInstancePromise = (async () => {
69
+ const Lenis = await loadLenis();
70
+
71
+ const lenisInstance = new Lenis({
72
+ smoothWheel: false,
73
+ smoothTouch: false,
74
+ autoRaf: true,
75
+ });
76
+
77
+ return lenisInstance;
78
+ })();
79
+ }
80
+
81
+ return lenisInstancePromise;
82
+ };
@@ -0,0 +1,81 @@
1
+ // composables/useGsap.js
2
+ import { loadGsap } from './loadGsap';
3
+
4
+ /**
5
+ * Ensures GSAP is loaded and available globally.
6
+ * After calling this composable, you can use gsap directly as a global.
7
+ *
8
+ * @returns {Object} Object with ready promise that resolves when GSAP is loaded
9
+ *
10
+ * @example
11
+ * // Basic usage in a Vue component
12
+ * import { useGsap } from '@thinkpixellab-public/px-vue/composables/useGsap';
13
+ *
14
+ * export default {
15
+ * setup() {
16
+ * const { ready } = useGsap();
17
+ *
18
+ * onMounted(async () => {
19
+ * await ready;
20
+ *
21
+ * // gsap is now available globally
22
+ * gsap.to('.my-element', {
23
+ * duration: 1,
24
+ * x: 100,
25
+ * opacity: 0.5
26
+ * });
27
+ * });
28
+ * }
29
+ * }
30
+ *
31
+ * @example
32
+ * // With refs and cleanup
33
+ * import { ref, onMounted, onBeforeUnmount } from 'vue';
34
+ * import { useGsap } from '@thinkpixellab-public/px-vue/composables/useGsap';
35
+ *
36
+ * export default {
37
+ * setup() {
38
+ * useGsap(); // ensures gsap loads
39
+ * const myElement = ref(null);
40
+ * let animation = null;
41
+ *
42
+ * onMounted(() => {
43
+ * // gsap is available globally
44
+ * animation = gsap.to(myElement.value, {
45
+ * duration: 2,
46
+ * rotation: 360,
47
+ * repeat: -1
48
+ * });
49
+ * });
50
+ *
51
+ * onBeforeUnmount(() => {
52
+ * if (animation) {
53
+ * animation.kill();
54
+ * }
55
+ * });
56
+ *
57
+ * return { myElement };
58
+ * }
59
+ * }
60
+ *
61
+ * @example
62
+ * // Immediate usage if you know GSAP is already loaded
63
+ * import { useGsap } from '@thinkpixellab-public/px-vue/composables/useGsap';
64
+ *
65
+ * export default {
66
+ * setup() {
67
+ * useGsap(); // triggers load if needed
68
+ *
69
+ * // can use gsap immediately in most cases
70
+ * const tl = gsap.timeline();
71
+ * }
72
+ * }
73
+ */
74
+ export const useGsap = () => {
75
+ if (process.server) {
76
+ return { ready: Promise.resolve() };
77
+ }
78
+
79
+ const ready = loadGsap();
80
+ return { ready };
81
+ };
@@ -0,0 +1,86 @@
1
+ // composables/useLenis.js
2
+ import { loadLenisInstance } from './loadLenis';
3
+
4
+ /**
5
+ * Vue composable for using Lenis smooth scrolling
6
+ *
7
+ * Provides access to a shared Lenis instance with autoRaf enabled.
8
+ * Perfect for components that need smooth scrolling without ScrollTrigger integration.
9
+ *
10
+ * @returns {Object} Object with getLenis loader function
11
+ *
12
+ * @example
13
+ * // Basic usage for smooth scrolling
14
+ * import { useLenis } from '@thinkpixellab-public/px-vue/composables/useLenis';
15
+ *
16
+ * export default {
17
+ * setup() {
18
+ * const { getLenis } = useLenis();
19
+ *
20
+ * onMounted(async () => {
21
+ * const lenis = await getLenis();
22
+ *
23
+ * // scroll to top smoothly
24
+ * lenis.scrollTo(0);
25
+ *
26
+ * // scroll to element
27
+ * lenis.scrollTo('#my-section');
28
+ *
29
+ * // scroll to specific position
30
+ * lenis.scrollTo(500, { duration: 2 });
31
+ * });
32
+ * }
33
+ * }
34
+ *
35
+ * @example
36
+ * // Listen to scroll events for parallax
37
+ * import { ref, onMounted } from 'vue';
38
+ * import { useLenis } from '@thinkpixellab-public/px-vue/composables/useLenis';
39
+ *
40
+ * export default {
41
+ * setup() {
42
+ * const { getLenis } = useLenis();
43
+ * const parallaxOffset = ref(0);
44
+ *
45
+ * onMounted(async () => {
46
+ * const lenis = await getLenis();
47
+ *
48
+ * lenis.on('scroll', (e) => {
49
+ * parallaxOffset.value = e.scroll * 0.5;
50
+ * });
51
+ * });
52
+ *
53
+ * return { parallaxOffset };
54
+ * }
55
+ * }
56
+ *
57
+ * @example
58
+ * // Custom scroll behavior
59
+ * import { useLenis } from '@thinkpixellab-public/px-vue/composables/useLenis';
60
+ *
61
+ * export default {
62
+ * setup() {
63
+ * const { getLenis } = useLenis();
64
+ *
65
+ * const scrollToSection = async (selector) => {
66
+ * const lenis = await getLenis();
67
+ *
68
+ * lenis.scrollTo(selector, {
69
+ * duration: 1.5,
70
+ * easing: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
71
+ * });
72
+ * };
73
+ *
74
+ * return { scrollToSection };
75
+ * }
76
+ * }
77
+ */
78
+ export const useLenis = () => {
79
+ if (process.server) {
80
+ return { getLenis: () => null };
81
+ }
82
+
83
+ return {
84
+ getLenis: loadLenisInstance
85
+ };
86
+ };
@@ -0,0 +1,142 @@
1
+ // composables/useScrollAnimation.js
2
+ import { loadScrollTrigger } from './loadGsap';
3
+ import { loadLenisInstance } from './loadLenis';
4
+
5
+ let scrollTriggerPromise = null;
6
+
7
+ /**
8
+ * Vue composable for scroll-triggered animations with GSAP ScrollTrigger and Lenis
9
+ *
10
+ * Ensures GSAP, ScrollTrigger, and Lenis are loaded and configured for optimal scroll animations.
11
+ * Automatically manages RAF control transfer from Lenis to GSAP for optimal performance.
12
+ * After ready resolves, gsap and ScrollTrigger are available globally.
13
+ *
14
+ * @returns {Object} Object with ready promise and getLenis function
15
+ *
16
+ * @example
17
+ * // Basic scroll-triggered animation
18
+ * import { useScrollAnimation } from '@thinkpixellab-public/px-vue/composables/useScrollAnimations';
19
+ *
20
+ * export default {
21
+ * setup() {
22
+ * const { ready } = useScrollAnimation();
23
+ *
24
+ * onMounted(async () => {
25
+ * await ready;
26
+ *
27
+ * // gsap and ScrollTrigger are now available globally
28
+ * gsap.to('.fade-in', {
29
+ * opacity: 1,
30
+ * y: 0,
31
+ * scrollTrigger: {
32
+ * trigger: '.fade-in',
33
+ * start: 'top 80%',
34
+ * end: 'bottom 20%',
35
+ * scrub: true
36
+ * }
37
+ * });
38
+ * });
39
+ * }
40
+ * }
41
+ *
42
+ * @example
43
+ * // Parallax scrolling with Lenis integration
44
+ * import { useScrollAnimation } from '@thinkpixellab-public/px-vue/composables/useScrollAnimations';
45
+ *
46
+ * export default {
47
+ * setup() {
48
+ * const { ready, getLenis } = useScrollAnimation();
49
+ *
50
+ * onMounted(async () => {
51
+ * await ready;
52
+ * const lenis = await getLenis();
53
+ *
54
+ * // smooth scroll to section
55
+ * lenis.scrollTo('#section-2', { duration: 2 });
56
+ *
57
+ * // parallax background with global gsap
58
+ * gsap.to('.bg-parallax', {
59
+ * yPercent: -50,
60
+ * ease: 'none',
61
+ * scrollTrigger: {
62
+ * trigger: '.parallax-section',
63
+ * start: 'top bottom',
64
+ * end: 'bottom top',
65
+ * scrub: true
66
+ * }
67
+ * });
68
+ * });
69
+ * }
70
+ * }
71
+ *
72
+ * @example
73
+ * // Complex timeline with ScrollTrigger
74
+ * import { ref, onMounted } from 'vue';
75
+ * import { useScrollAnimation } from '@thinkpixellab-public/px-vue/composables/useScrollAnimations';
76
+ *
77
+ * export default {
78
+ * setup() {
79
+ * const { ready } = useScrollAnimation();
80
+ * const container = ref(null);
81
+ *
82
+ * onMounted(async () => {
83
+ * await ready;
84
+ *
85
+ * // gsap is available globally
86
+ * const tl = gsap.timeline({
87
+ * scrollTrigger: {
88
+ * trigger: container.value,
89
+ * start: 'top center',
90
+ * end: 'bottom center',
91
+ * scrub: 1,
92
+ * pin: true
93
+ * }
94
+ * });
95
+ *
96
+ * tl.from('.item-1', { x: -100, opacity: 0 })
97
+ * .from('.item-2', { x: 100, opacity: 0 }, '-=0.5')
98
+ * .from('.item-3', { y: 100, opacity: 0 }, '-=0.3');
99
+ * });
100
+ *
101
+ * return { container };
102
+ * }
103
+ * }
104
+ */
105
+ export const useScrollAnimation = () => {
106
+ if (process.server) {
107
+ return {
108
+ ready: Promise.resolve(),
109
+ getLenis: () => null,
110
+ };
111
+ }
112
+
113
+ const init = async () => {
114
+ if (!scrollTriggerPromise) {
115
+ scrollTriggerPromise = (async () => {
116
+ const [{ gsap, ScrollTrigger }, lenis] = await Promise.all([
117
+ loadScrollTrigger(),
118
+ loadLenisInstance(),
119
+ ]);
120
+
121
+ // Take over RAF control from Lenis
122
+ lenis.stop();
123
+
124
+ lenis.on('scroll', ScrollTrigger.update);
125
+ gsap.ticker.add(time => {
126
+ lenis.raf(time * 1000);
127
+ });
128
+ gsap.ticker.lagSmoothing(0);
129
+
130
+ // ScrollTrigger is now available globally
131
+ return { lenis };
132
+ })();
133
+ }
134
+
135
+ return scrollTriggerPromise;
136
+ };
137
+
138
+ return {
139
+ ready: init().then(() => {}), // resolves when ScrollTrigger is ready
140
+ getLenis: async () => (await init()).lenis,
141
+ };
142
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkpixellab-public/px-vue",
3
- "version": "4.1.26",
3
+ "version": "4.1.27",
4
4
  "description": "General purpose Vue components and helpers that can be used across projects.",
5
5
  "author": {
6
6
  "name": "Pixel Lab"
@@ -29,8 +29,9 @@
29
29
  "chrono-node": "^2.7.8",
30
30
  "dateformat": "^5.0.3",
31
31
  "figma-squircle": "^1.1.0",
32
- "gsap": "^3.12.7",
32
+ "gsap": "^3.13.0",
33
33
  "highlight.js": "^11.11.1",
34
+ "lenis": "^1.1.17",
34
35
  "mitt": "^3.0.1",
35
36
  "qr-code-styling": "^1.9.2",
36
37
  "sortablejs": "^1.15.6",