@hortonstudio/main 1.0.0 → 1.1.0
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/.claude/settings.local.json +18 -0
- package/CLAUDE.md +45 -0
- package/animations/hero.js +530 -0
- package/animations/text.js +305 -0
- package/animations/{modules/transition.js → transition.js} +2 -1
- package/autoInit/smooth-scroll.js +89 -0
- package/index.js +116 -48
- package/package.json +1 -1
- package/styles.css +17 -0
- package/utils/navbar.js +203 -0
- package/utils/scroll-progress.js +29 -0
- package/utils/toc.js +54 -0
- package/animations/READ +0 -0
- package/animations/modules/hero.js +0 -154
- package/animations/modules/text.js +0 -165
- package/utils/READ +0 -0
- package/utils/modules/toc.js +0 -0
package/utils/navbar.js
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
export const init = () => {
|
2
|
+
// Find all dropdown wrappers
|
3
|
+
const dropdownWrappers = document.querySelectorAll('[data-hs-nav-dropdown="wrapper"]');
|
4
|
+
|
5
|
+
// Global array to track all dropdown instances
|
6
|
+
const allDropdowns = [];
|
7
|
+
|
8
|
+
// Function to close all dropdowns except the specified one
|
9
|
+
const closeAllDropdowns = (exceptWrapper = null) => {
|
10
|
+
allDropdowns.forEach(dropdown => {
|
11
|
+
if (dropdown.wrapper !== exceptWrapper && dropdown.isOpen) {
|
12
|
+
dropdown.closeDropdown();
|
13
|
+
}
|
14
|
+
});
|
15
|
+
};
|
16
|
+
|
17
|
+
dropdownWrappers.forEach(wrapper => {
|
18
|
+
const animationDuration = 0.3;
|
19
|
+
|
20
|
+
// Find elements within this wrapper
|
21
|
+
const toggle = wrapper.querySelector('a'); // the toggle link
|
22
|
+
const list = wrapper.querySelector('[data-hs-nav-dropdown="list"]');
|
23
|
+
const contain = list.querySelector('[data-hs-nav-dropdown="container"]');
|
24
|
+
const arrow = toggle.querySelector('[data-hs-nav-dropdown="arrow"]');
|
25
|
+
const text = toggle.querySelector('[data-hs-nav-dropdown="text"]'); // find the text element
|
26
|
+
|
27
|
+
// Set initial states with GSAP
|
28
|
+
gsap.set(contain, { yPercent: -110 });
|
29
|
+
gsap.set(list, { display: 'none' });
|
30
|
+
gsap.set(arrow, { rotation: 0, scale: 1, x: 0, color: '' });
|
31
|
+
gsap.set(text, { scale: 1, color: '' }); // empty string = default color
|
32
|
+
|
33
|
+
// Track if dropdown is open
|
34
|
+
let isOpen = false;
|
35
|
+
let currentTimeline = null;
|
36
|
+
|
37
|
+
// Open animation
|
38
|
+
function openDropdown() {
|
39
|
+
if (isOpen) return;
|
40
|
+
|
41
|
+
// Kill any existing timeline
|
42
|
+
if (currentTimeline) {
|
43
|
+
currentTimeline.kill();
|
44
|
+
}
|
45
|
+
|
46
|
+
// Close all other dropdowns first
|
47
|
+
closeAllDropdowns(wrapper);
|
48
|
+
|
49
|
+
isOpen = true;
|
50
|
+
|
51
|
+
// Update ARIA states
|
52
|
+
toggle.setAttribute('aria-expanded', 'true');
|
53
|
+
list.setAttribute('aria-hidden', 'false');
|
54
|
+
|
55
|
+
// GSAP animation
|
56
|
+
currentTimeline = gsap.timeline();
|
57
|
+
currentTimeline.set(list, { display: 'flex' })
|
58
|
+
.to(contain, {
|
59
|
+
yPercent: 0,
|
60
|
+
duration: animationDuration,
|
61
|
+
ease: 'ease'
|
62
|
+
}, 0)
|
63
|
+
.to(arrow, {
|
64
|
+
rotation: 90,
|
65
|
+
scale: 1.2,
|
66
|
+
x: 4,
|
67
|
+
color: 'var(--swatch--brand)',
|
68
|
+
duration: animationDuration,
|
69
|
+
ease: 'ease'
|
70
|
+
}, 0)
|
71
|
+
.to(text, {
|
72
|
+
scale: 1.1,
|
73
|
+
color: 'var(--swatch--brand)',
|
74
|
+
duration: animationDuration,
|
75
|
+
ease: 'ease'
|
76
|
+
}, 0);
|
77
|
+
}
|
78
|
+
|
79
|
+
// Close animation
|
80
|
+
function closeDropdown() {
|
81
|
+
if (!isOpen) return;
|
82
|
+
|
83
|
+
// Kill any existing timeline
|
84
|
+
if (currentTimeline) {
|
85
|
+
currentTimeline.kill();
|
86
|
+
}
|
87
|
+
|
88
|
+
// Check if focus should be restored to toggle
|
89
|
+
const shouldRestoreFocus = list.contains(document.activeElement);
|
90
|
+
|
91
|
+
isOpen = false;
|
92
|
+
currentMenuItemIndex = -1;
|
93
|
+
|
94
|
+
// Update ARIA states
|
95
|
+
toggle.setAttribute('aria-expanded', 'false');
|
96
|
+
list.setAttribute('aria-hidden', 'true');
|
97
|
+
|
98
|
+
// GSAP animation
|
99
|
+
currentTimeline = gsap.timeline();
|
100
|
+
currentTimeline.to(contain, {
|
101
|
+
yPercent: -110,
|
102
|
+
duration: animationDuration,
|
103
|
+
ease: 'ease'
|
104
|
+
}, 0)
|
105
|
+
.to(arrow, {
|
106
|
+
rotation: 0,
|
107
|
+
scale: 1,
|
108
|
+
x: 0,
|
109
|
+
color: '', // back to default color
|
110
|
+
duration: animationDuration,
|
111
|
+
ease: 'ease'
|
112
|
+
}, 0)
|
113
|
+
.to(text, {
|
114
|
+
scale: 1,
|
115
|
+
color: '', // back to default color
|
116
|
+
duration: animationDuration,
|
117
|
+
ease: 'ease'
|
118
|
+
}, 0)
|
119
|
+
.set(list, { display: 'none' });
|
120
|
+
|
121
|
+
// Restore focus to toggle only if focus was inside dropdown
|
122
|
+
if (shouldRestoreFocus) {
|
123
|
+
toggle.focus();
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
// Get all menu items for navigation
|
128
|
+
const menuItems = list.querySelectorAll('a, button, [role="menuitem"]');
|
129
|
+
let currentMenuItemIndex = -1;
|
130
|
+
|
131
|
+
// Hover events
|
132
|
+
toggle.addEventListener('mouseenter', openDropdown);
|
133
|
+
wrapper.addEventListener('mouseleave', closeDropdown);
|
134
|
+
|
135
|
+
// Arrow key navigation within dropdown
|
136
|
+
list.addEventListener('keydown', function(e) {
|
137
|
+
if (!isOpen) return;
|
138
|
+
|
139
|
+
if (e.key === 'ArrowDown') {
|
140
|
+
e.preventDefault();
|
141
|
+
currentMenuItemIndex = (currentMenuItemIndex + 1) % menuItems.length;
|
142
|
+
menuItems[currentMenuItemIndex].focus();
|
143
|
+
} else if (e.key === 'ArrowUp') {
|
144
|
+
e.preventDefault();
|
145
|
+
currentMenuItemIndex = currentMenuItemIndex <= 0 ? menuItems.length - 1 : currentMenuItemIndex - 1;
|
146
|
+
menuItems[currentMenuItemIndex].focus();
|
147
|
+
} else if (e.key === 'Escape') {
|
148
|
+
e.preventDefault();
|
149
|
+
closeDropdown();
|
150
|
+
toggle.focus();
|
151
|
+
}
|
152
|
+
});
|
153
|
+
|
154
|
+
// Keyboard events for toggle
|
155
|
+
toggle.addEventListener('keydown', function(e) {
|
156
|
+
if (e.key === 'ArrowDown') {
|
157
|
+
e.preventDefault();
|
158
|
+
openDropdown();
|
159
|
+
// Focus first menu item after opening
|
160
|
+
if (menuItems.length > 0) {
|
161
|
+
currentMenuItemIndex = 0;
|
162
|
+
setTimeout(() => menuItems[0].focus(), 50);
|
163
|
+
}
|
164
|
+
} else if (e.key === ' ') {
|
165
|
+
e.preventDefault();
|
166
|
+
// Simple toggle: if closed open, if open close
|
167
|
+
if (isOpen) {
|
168
|
+
closeDropdown();
|
169
|
+
} else {
|
170
|
+
openDropdown();
|
171
|
+
}
|
172
|
+
} else if (e.key === 'ArrowUp' || e.key === 'Escape') {
|
173
|
+
e.preventDefault();
|
174
|
+
closeDropdown();
|
175
|
+
}
|
176
|
+
});
|
177
|
+
|
178
|
+
// Close dropdown when clicking outside
|
179
|
+
document.addEventListener('click', function(e) {
|
180
|
+
if (!wrapper.contains(e.target) && isOpen) {
|
181
|
+
closeDropdown();
|
182
|
+
}
|
183
|
+
});
|
184
|
+
|
185
|
+
// Add this dropdown instance to the global array
|
186
|
+
allDropdowns.push({
|
187
|
+
wrapper,
|
188
|
+
isOpen: () => isOpen,
|
189
|
+
closeDropdown
|
190
|
+
});
|
191
|
+
});
|
192
|
+
|
193
|
+
// Global focus management - close dropdown when tab focus moves outside
|
194
|
+
document.addEventListener('focusin', function(e) {
|
195
|
+
allDropdowns.forEach(dropdown => {
|
196
|
+
if (dropdown.isOpen() && !dropdown.wrapper.contains(e.target)) {
|
197
|
+
dropdown.closeDropdown();
|
198
|
+
}
|
199
|
+
});
|
200
|
+
});
|
201
|
+
|
202
|
+
return { result: 'navbar initialized' };
|
203
|
+
};
|
@@ -0,0 +1,29 @@
|
|
1
|
+
export async function init() {
|
2
|
+
const progressBar = document.querySelector('[data-hs-progress="bar"]');
|
3
|
+
const progressContent = document.querySelector('[data-hs-progress="wrapper"]');
|
4
|
+
|
5
|
+
// Check if elements exist before using them
|
6
|
+
if (!progressBar || !progressContent) {
|
7
|
+
return {
|
8
|
+
result: 'util-scroll-progress initialized'
|
9
|
+
};
|
10
|
+
}
|
11
|
+
|
12
|
+
gsap.set(progressBar, { width: "0%" });
|
13
|
+
|
14
|
+
// Create the scroll progress animation
|
15
|
+
gsap.to(progressBar, {
|
16
|
+
width: "100%",
|
17
|
+
ease: "none",
|
18
|
+
scrollTrigger: {
|
19
|
+
trigger: progressContent,
|
20
|
+
start: "top bottom",
|
21
|
+
end: "bottom bottom",
|
22
|
+
scrub: true
|
23
|
+
}
|
24
|
+
});
|
25
|
+
|
26
|
+
return {
|
27
|
+
result: 'util-scroll-progress initialized'
|
28
|
+
};
|
29
|
+
}
|
package/utils/toc.js
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
export async function init() {
|
2
|
+
const contentArea = document.querySelector('[data-hs-toc="content"]');
|
3
|
+
const tocList = document.querySelector('[data-hs-toc="list"]');
|
4
|
+
|
5
|
+
// Check main elements
|
6
|
+
if (!contentArea) { return; }
|
7
|
+
if (!tocList) { return; }
|
8
|
+
if (tocList.children.length === 0) { return; }
|
9
|
+
|
10
|
+
const template = tocList.children[0];
|
11
|
+
tocList.innerHTML = '';
|
12
|
+
const h2Headings = contentArea.querySelectorAll('h2');
|
13
|
+
|
14
|
+
// Create sections and wrap content
|
15
|
+
h2Headings.forEach((heading, index) => {
|
16
|
+
const sectionId = heading.textContent.toLowerCase()
|
17
|
+
.replace(/[^a-z0-9]+/g, '-')
|
18
|
+
.replace(/(^-|-$)/g, '');
|
19
|
+
const section = document.createElement('div');
|
20
|
+
section.id = sectionId;
|
21
|
+
heading.parentNode.insertBefore(section, heading);
|
22
|
+
section.appendChild(heading);
|
23
|
+
let nextElement = section.nextElementSibling;
|
24
|
+
while (nextElement && nextElement.tagName !== 'H2') {
|
25
|
+
const elementToMove = nextElement;
|
26
|
+
nextElement = nextElement.nextElementSibling;
|
27
|
+
section.appendChild(elementToMove);
|
28
|
+
}
|
29
|
+
});
|
30
|
+
|
31
|
+
// Create TOC entries
|
32
|
+
h2Headings.forEach((heading, index) => {
|
33
|
+
const tocItem = template.cloneNode(true);
|
34
|
+
const link = tocItem.querySelector('a');
|
35
|
+
const sectionId = heading.parentElement.id;
|
36
|
+
link.href = '#' + sectionId;
|
37
|
+
|
38
|
+
// Bold numbered text
|
39
|
+
const number = document.createElement('strong');
|
40
|
+
number.textContent = (index + 1) + '. ';
|
41
|
+
|
42
|
+
// Clear the link and add the number + text
|
43
|
+
link.innerHTML = '';
|
44
|
+
link.appendChild(number);
|
45
|
+
link.appendChild(document.createTextNode(heading.textContent));
|
46
|
+
|
47
|
+
// Add item to the TOC list
|
48
|
+
tocList.appendChild(tocItem);
|
49
|
+
});
|
50
|
+
|
51
|
+
return {
|
52
|
+
result: 'util-toc initialized'
|
53
|
+
};
|
54
|
+
}
|
package/animations/READ
DELETED
File without changes
|
@@ -1,154 +0,0 @@
|
|
1
|
-
// Hero Animations Module
|
2
|
-
let heroTimeline = null;
|
3
|
-
let headingSplits = [];
|
4
|
-
|
5
|
-
export async function init() {
|
6
|
-
|
7
|
-
// Check for GSAP
|
8
|
-
if (typeof window.gsap === "undefined") {
|
9
|
-
console.error('GSAP not found - hero animations disabled');
|
10
|
-
return;
|
11
|
-
}
|
12
|
-
|
13
|
-
// Register plugins
|
14
|
-
gsap.registerPlugin(ScrollTrigger, SplitText);
|
15
|
-
|
16
|
-
// Variable to control when animation starts (in seconds)
|
17
|
-
const heroAnimationDelay = 0.2;
|
18
|
-
|
19
|
-
// SET INITIAL STATES IMMEDIATELY
|
20
|
-
gsap.set('[hero-anim="announce"]', { opacity: 0, yPercent: -100 });
|
21
|
-
gsap.set('[hero-anim="nav-container"]', { opacity: 0, yPercent: -100 });
|
22
|
-
gsap.set('[hero-anim="subheading"]', { y: 50, opacity: 0 });
|
23
|
-
gsap.set('[hero-anim="button"]', { y: 50, opacity: 0 });
|
24
|
-
gsap.set('[hero-anim="image"]', { opacity: 0 });
|
25
|
-
gsap.set('[hero-anim="appear1"]', { y: 50, opacity: 0 });
|
26
|
-
gsap.set('[hero-anim="appear2"]', { y: 50, opacity: 0 });
|
27
|
-
gsap.set('[hero-anim="appear3"]', { y: 50, opacity: 0 });
|
28
|
-
gsap.set('[hero-anim="appear4"]', { y: 50, opacity: 0 });
|
29
|
-
|
30
|
-
// SET UP SPLIT TEXT WITH MASKS
|
31
|
-
const headingElements = document.querySelectorAll('[hero-anim="heading"]');
|
32
|
-
headingSplits = []; // Reset array
|
33
|
-
|
34
|
-
headingElements.forEach(heading => {
|
35
|
-
const split = new SplitText(heading, {
|
36
|
-
type: "words",
|
37
|
-
mask: "words",
|
38
|
-
wordsClass: "word"
|
39
|
-
});
|
40
|
-
headingSplits.push(split);
|
41
|
-
|
42
|
-
// Set initial state for split text
|
43
|
-
gsap.set(split.words, { yPercent: 110 });
|
44
|
-
gsap.set(heading, { autoAlpha: 1 });
|
45
|
-
});
|
46
|
-
|
47
|
-
// Wait for fonts to load, then start animation
|
48
|
-
document.fonts.ready.then(() => {
|
49
|
-
|
50
|
-
setTimeout(() => {
|
51
|
-
// Create main timeline
|
52
|
-
heroTimeline = gsap.timeline();
|
53
|
-
|
54
|
-
// 0s - Announce
|
55
|
-
heroTimeline.to('[hero-anim="announce"]',
|
56
|
-
{ opacity: 1, yPercent: 0, duration: 1, ease: 'power3.out' },
|
57
|
-
0
|
58
|
-
);
|
59
|
-
|
60
|
-
// 0.05s - Nav Container
|
61
|
-
heroTimeline.to('[hero-anim="nav-container"]',
|
62
|
-
{ opacity: 1, yPercent: 0, duration: 1, ease: 'power3.out' },
|
63
|
-
0.05
|
64
|
-
);
|
65
|
-
|
66
|
-
// 0.15s - Heading (Split Text with masks)
|
67
|
-
headingSplits.forEach(split => {
|
68
|
-
heroTimeline.to(split.words,
|
69
|
-
{ yPercent: 0, duration: 1.25, stagger: 0.1, ease: 'power4.out' },
|
70
|
-
0.15
|
71
|
-
);
|
72
|
-
});
|
73
|
-
|
74
|
-
// 0.25s - Subheading
|
75
|
-
heroTimeline.to('[hero-anim="subheading"]',
|
76
|
-
{ y: 0, opacity: 1, duration: 1.5, ease: 'power3.out' },
|
77
|
-
0.25
|
78
|
-
);
|
79
|
-
|
80
|
-
// 0.35s - Button
|
81
|
-
heroTimeline.to('[hero-anim="button"]',
|
82
|
-
{ y: 0, opacity: 1, duration: 1.5, ease: 'power3.out' },
|
83
|
-
0.35
|
84
|
-
);
|
85
|
-
|
86
|
-
// 0.35s - Image
|
87
|
-
heroTimeline.to('[hero-anim="image"]',
|
88
|
-
{ opacity: 1, duration: 1.25, ease: 'power3.out' },
|
89
|
-
0.35
|
90
|
-
);
|
91
|
-
|
92
|
-
// 0.45s - Appear1
|
93
|
-
heroTimeline.to('[hero-anim="appear1"]',
|
94
|
-
{ y: 0, opacity: 1, duration: 1.5, ease: 'power3.out' },
|
95
|
-
0.45
|
96
|
-
);
|
97
|
-
|
98
|
-
// 0.5s - Appear2
|
99
|
-
heroTimeline.to('[hero-anim="appear2"]',
|
100
|
-
{ y: 0, opacity: 1, duration: 1.5, ease: 'power3.out' },
|
101
|
-
0.5
|
102
|
-
);
|
103
|
-
|
104
|
-
// 0.55s - Appear3
|
105
|
-
heroTimeline.to('[hero-anim="appear3"]',
|
106
|
-
{ y: 0, opacity: 1, duration: 1.5, ease: 'power3.out' },
|
107
|
-
0.55
|
108
|
-
);
|
109
|
-
|
110
|
-
// 0.55s - Appear4
|
111
|
-
heroTimeline.to('[hero-anim="appear4"]',
|
112
|
-
{ y: 0, opacity: 1, duration: 1.5, ease: 'power3.out' },
|
113
|
-
0.55
|
114
|
-
);
|
115
|
-
|
116
|
-
}, heroAnimationDelay * 1000);
|
117
|
-
|
118
|
-
});
|
119
|
-
|
120
|
-
return { result: 'hero-main initialized' };
|
121
|
-
}
|
122
|
-
|
123
|
-
export function destroy() {
|
124
|
-
console.log('🧹 Hero animations destroyed');
|
125
|
-
|
126
|
-
// Kill the main timeline
|
127
|
-
if (heroTimeline) {
|
128
|
-
heroTimeline.kill();
|
129
|
-
heroTimeline = null;
|
130
|
-
}
|
131
|
-
|
132
|
-
// Revert all SplitText instances
|
133
|
-
headingSplits.forEach(split => {
|
134
|
-
if (split && split.revert) {
|
135
|
-
split.revert();
|
136
|
-
}
|
137
|
-
});
|
138
|
-
headingSplits = [];
|
139
|
-
|
140
|
-
// Kill any ScrollTriggers (in case there are any)
|
141
|
-
ScrollTrigger.getAll().forEach(trigger => trigger.kill());
|
142
|
-
|
143
|
-
// Reset elements to normal state
|
144
|
-
gsap.set('[hero-anim="announce"]', { clearProps: "all" });
|
145
|
-
gsap.set('[hero-anim="nav-container"]', { clearProps: "all" });
|
146
|
-
gsap.set('[hero-anim="subheading"]', { clearProps: "all" });
|
147
|
-
gsap.set('[hero-anim="button"]', { clearProps: "all" });
|
148
|
-
gsap.set('[hero-anim="image"]', { clearProps: "all" });
|
149
|
-
gsap.set('[hero-anim="appear1"]', { clearProps: "all" });
|
150
|
-
gsap.set('[hero-anim="appear2"]', { clearProps: "all" });
|
151
|
-
gsap.set('[hero-anim="appear3"]', { clearProps: "all" });
|
152
|
-
gsap.set('[hero-anim="appear4"]', { clearProps: "all" });
|
153
|
-
gsap.set('[hero-anim="heading"]', { clearProps: "all" });
|
154
|
-
}
|
@@ -1,165 +0,0 @@
|
|
1
|
-
let activeAnimations = [];
|
2
|
-
let resizeTimeout;
|
3
|
-
let lastWidth = window.innerWidth;
|
4
|
-
|
5
|
-
function waitForFonts() {
|
6
|
-
return document.fonts.ready;
|
7
|
-
}
|
8
|
-
|
9
|
-
function checkGSAP() {
|
10
|
-
if (typeof window.gsap === "undefined") {
|
11
|
-
document.documentElement.classList.add("gsap-not-found");
|
12
|
-
return false;
|
13
|
-
}
|
14
|
-
gsap.registerPlugin(ScrollTrigger, SplitText);
|
15
|
-
return true;
|
16
|
-
}
|
17
|
-
|
18
|
-
const WordSplitAnimations = {
|
19
|
-
async initial() {
|
20
|
-
await waitForFonts();
|
21
|
-
|
22
|
-
document.querySelectorAll(".a-word-split").forEach((text) => {
|
23
|
-
if (text.splitTextInstance) {
|
24
|
-
text.splitTextInstance.revert();
|
25
|
-
}
|
26
|
-
});
|
27
|
-
|
28
|
-
document.querySelectorAll(".a-word-split").forEach((text) => {
|
29
|
-
const split = SplitText.create(text, {
|
30
|
-
type: "words",
|
31
|
-
mask: "words",
|
32
|
-
wordsClass: "word",
|
33
|
-
});
|
34
|
-
text.splitTextInstance = split;
|
35
|
-
gsap.set(split.words, {
|
36
|
-
yPercent: 110
|
37
|
-
});
|
38
|
-
gsap.set(text, { autoAlpha: 1 });
|
39
|
-
});
|
40
|
-
},
|
41
|
-
|
42
|
-
async animate() {
|
43
|
-
await waitForFonts();
|
44
|
-
|
45
|
-
document.querySelectorAll(".a-word-split").forEach((text) => {
|
46
|
-
const words = text.querySelectorAll('.word');
|
47
|
-
const tl = gsap.timeline({
|
48
|
-
scrollTrigger: {
|
49
|
-
trigger: text,
|
50
|
-
start: "top 95%",
|
51
|
-
invalidateOnRefresh: true,
|
52
|
-
},
|
53
|
-
});
|
54
|
-
|
55
|
-
tl.to(words, {
|
56
|
-
yPercent: 0,
|
57
|
-
duration: 1.25,
|
58
|
-
stagger: .1,
|
59
|
-
ease: "power4.out",
|
60
|
-
});
|
61
|
-
|
62
|
-
activeAnimations.push({ timeline: tl, element: text });
|
63
|
-
});
|
64
|
-
|
65
|
-
ScrollTrigger.refresh();
|
66
|
-
}
|
67
|
-
};
|
68
|
-
|
69
|
-
const AppearAnimations = {
|
70
|
-
async initial() {
|
71
|
-
await waitForFonts();
|
72
|
-
|
73
|
-
const elements = document.querySelectorAll('.a-appear');
|
74
|
-
elements.forEach(element => {
|
75
|
-
gsap.set(element, {
|
76
|
-
y: 50,
|
77
|
-
opacity: 0
|
78
|
-
});
|
79
|
-
});
|
80
|
-
},
|
81
|
-
|
82
|
-
async animate() {
|
83
|
-
await waitForFonts();
|
84
|
-
|
85
|
-
document.querySelectorAll('.a-appear').forEach(element => {
|
86
|
-
const tl = gsap.timeline({
|
87
|
-
scrollTrigger: {
|
88
|
-
trigger: element,
|
89
|
-
start: 'top 95%',
|
90
|
-
invalidateOnRefresh: true,
|
91
|
-
}
|
92
|
-
});
|
93
|
-
|
94
|
-
tl.to(element, {
|
95
|
-
y: 0,
|
96
|
-
opacity: 1,
|
97
|
-
duration: 1.5,
|
98
|
-
ease: 'power3.out'
|
99
|
-
});
|
100
|
-
|
101
|
-
activeAnimations.push({ timeline: tl, element: element });
|
102
|
-
});
|
103
|
-
|
104
|
-
ScrollTrigger.refresh();
|
105
|
-
}
|
106
|
-
};
|
107
|
-
|
108
|
-
async function setInitialStates() {
|
109
|
-
if (!checkGSAP()) return;
|
110
|
-
|
111
|
-
await Promise.all([
|
112
|
-
WordSplitAnimations.initial(),
|
113
|
-
AppearAnimations.initial()
|
114
|
-
]);
|
115
|
-
}
|
116
|
-
|
117
|
-
async function initAnimations() {
|
118
|
-
if (!checkGSAP()) return;
|
119
|
-
|
120
|
-
await Promise.all([
|
121
|
-
WordSplitAnimations.animate(),
|
122
|
-
AppearAnimations.animate()
|
123
|
-
]);
|
124
|
-
}
|
125
|
-
|
126
|
-
function handleResize() {
|
127
|
-
clearTimeout(resizeTimeout);
|
128
|
-
resizeTimeout = setTimeout(() => {
|
129
|
-
const currentWidth = window.innerWidth;
|
130
|
-
|
131
|
-
if (Math.abs(currentWidth - lastWidth) > 10) {
|
132
|
-
lastWidth = currentWidth;
|
133
|
-
|
134
|
-
setTimeout(() => {
|
135
|
-
setInitialStates();
|
136
|
-
setTimeout(() => {
|
137
|
-
initAnimations();
|
138
|
-
}, 100);
|
139
|
-
}, 50);
|
140
|
-
}
|
141
|
-
}, 150);
|
142
|
-
}
|
143
|
-
|
144
|
-
export async function init() {
|
145
|
-
if (!checkGSAP()) return;
|
146
|
-
|
147
|
-
await setInitialStates();
|
148
|
-
|
149
|
-
setTimeout(() => {
|
150
|
-
initAnimations();
|
151
|
-
}, 750);
|
152
|
-
|
153
|
-
window.addEventListener('resize', handleResize);
|
154
|
-
window.hsMain.textResizeHandler = handleResize;
|
155
|
-
|
156
|
-
return {
|
157
|
-
result: 'text-main initialized',
|
158
|
-
reinit: () => {
|
159
|
-
setInitialStates();
|
160
|
-
setTimeout(() => {
|
161
|
-
initAnimations();
|
162
|
-
}, 100);
|
163
|
-
}
|
164
|
-
};
|
165
|
-
}
|
package/utils/READ
DELETED
File without changes
|
package/utils/modules/toc.js
DELETED
File without changes
|