@hortonstudio/main 1.2.35 → 1.4.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 +22 -1
- package/TEMP-before-after-attributes.md +158 -0
- package/animations/hero.js +741 -611
- package/animations/text.js +505 -317
- package/animations/transition.js +36 -21
- package/autoInit/accessibility.js +7 -67
- package/autoInit/counter.js +338 -0
- package/autoInit/custom-values.js +266 -0
- package/autoInit/form.js +471 -0
- package/autoInit/modal.js +43 -38
- package/autoInit/navbar.js +484 -371
- package/autoInit/smooth-scroll.js +86 -84
- package/index.js +140 -88
- package/package.json +1 -1
- package/utils/before-after.js +279 -146
- package/utils/scroll-progress.js +26 -21
- package/utils/toc.js +73 -66
- package/CLAUDE.md +0 -45
- package/debug-version.html +0 -37
package/animations/text.js
CHANGED
@@ -1,389 +1,577 @@
|
|
1
|
-
const API_NAME =
|
1
|
+
const API_NAME = "hsmain";
|
2
|
+
|
3
|
+
// Module-scoped variables for resize handling
|
4
|
+
let resizeTimeout;
|
5
|
+
let resizeHandler;
|
2
6
|
|
3
7
|
// Check for reduced motion preference
|
4
8
|
const prefersReducedMotion = () => {
|
5
|
-
|
9
|
+
return (
|
10
|
+
window.matchMedia &&
|
11
|
+
window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
12
|
+
);
|
6
13
|
};
|
7
14
|
|
8
15
|
const config = {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
16
|
+
global: {
|
17
|
+
animationDelay: 0,
|
18
|
+
},
|
19
|
+
wordSplit: {
|
20
|
+
duration: 1.5,
|
21
|
+
stagger: 0.05,
|
22
|
+
yPercent: 110,
|
23
|
+
ease: "power4.out",
|
24
|
+
start: "top 97%",
|
25
|
+
},
|
26
|
+
lineSplit: {
|
27
|
+
duration: 1.5,
|
28
|
+
stagger: 0.1,
|
29
|
+
yPercent: 110,
|
30
|
+
ease: "power4.out",
|
31
|
+
start: "top 97%",
|
32
|
+
},
|
33
|
+
charSplit: {
|
34
|
+
duration: 1.2,
|
35
|
+
stagger: 0.015,
|
36
|
+
yPercent: 110,
|
37
|
+
ease: "power4.out",
|
38
|
+
start: "top 97%",
|
39
|
+
},
|
40
|
+
appear: {
|
41
|
+
y: 50,
|
42
|
+
duration: 1.5,
|
43
|
+
ease: "power3.out",
|
44
|
+
start: "top 97%",
|
45
|
+
},
|
46
|
+
reveal: {
|
47
|
+
y: 50,
|
48
|
+
duration: 1.5,
|
49
|
+
ease: "power3.out",
|
50
|
+
start: "top 97%",
|
51
|
+
},
|
39
52
|
};
|
40
53
|
|
41
54
|
function updateConfig(newConfig) {
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
55
|
+
function deepMerge(target, source) {
|
56
|
+
for (const key in source) {
|
57
|
+
if (
|
58
|
+
source[key] &&
|
59
|
+
typeof source[key] === "object" &&
|
60
|
+
!Array.isArray(source[key])
|
61
|
+
) {
|
62
|
+
target[key] = target[key] || {};
|
63
|
+
deepMerge(target[key], source[key]);
|
64
|
+
} else {
|
65
|
+
target[key] = source[key];
|
66
|
+
}
|
52
67
|
}
|
53
|
-
|
54
|
-
|
68
|
+
return target;
|
69
|
+
}
|
70
|
+
|
71
|
+
deepMerge(config, newConfig);
|
55
72
|
}
|
56
73
|
|
57
74
|
function killTextAnimations() {
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
75
|
+
activeAnimations.forEach(({ timeline, element }) => {
|
76
|
+
if (timeline) {
|
77
|
+
timeline.kill();
|
78
|
+
}
|
79
|
+
if (element?.splitTextInstance) {
|
80
|
+
element.splitTextInstance.revert();
|
81
|
+
}
|
82
|
+
});
|
83
|
+
activeAnimations.length = 0;
|
67
84
|
}
|
68
85
|
|
69
86
|
function startTextAnimations() {
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
87
|
+
if (prefersReducedMotion()) {
|
88
|
+
// For reduced motion, just show elements without animation
|
89
|
+
showElementsWithoutAnimation();
|
90
|
+
return;
|
91
|
+
}
|
92
|
+
|
93
|
+
setInitialStates().then(() => {
|
94
|
+
initAnimations();
|
95
|
+
});
|
79
96
|
}
|
80
97
|
|
81
98
|
let activeAnimations = [];
|
82
99
|
|
83
|
-
function waitForFonts() {
|
84
|
-
|
100
|
+
async function waitForFonts() {
|
101
|
+
try {
|
102
|
+
return await document.fonts.ready;
|
103
|
+
} catch (error) {
|
104
|
+
console.warn("Font loading error:", error);
|
105
|
+
return Promise.resolve();
|
106
|
+
}
|
85
107
|
}
|
86
108
|
|
87
109
|
function showElementsWithoutAnimation() {
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
110
|
+
// Simply show all text elements without any animation or split text
|
111
|
+
const allTextElements = [
|
112
|
+
...document.querySelectorAll(".a-char-split > *:first-child"),
|
113
|
+
...document.querySelectorAll(".a-word-split > *:first-child"),
|
114
|
+
...document.querySelectorAll(".a-line-split > *:first-child"),
|
115
|
+
...document.querySelectorAll(".a-appear"),
|
116
|
+
...document.querySelectorAll(".a-reveal"),
|
117
|
+
];
|
118
|
+
|
119
|
+
allTextElements.forEach((element) => {
|
120
|
+
try {
|
121
|
+
gsap.set(element, {
|
122
|
+
autoAlpha: 1,
|
123
|
+
y: 0,
|
124
|
+
yPercent: 0,
|
125
|
+
opacity: 1,
|
126
|
+
});
|
127
|
+
} catch (error) {
|
128
|
+
console.warn("Error setting element visibility:", error);
|
129
|
+
}
|
130
|
+
});
|
104
131
|
}
|
105
132
|
|
106
133
|
const CharSplitAnimations = {
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
const elements = document.querySelectorAll(".a-char-split > *:first-child");
|
115
|
-
|
116
|
-
elements.forEach((textElement) => {
|
117
|
-
const split = SplitText.create(textElement, {
|
118
|
-
type: "words,chars",
|
119
|
-
mask: "chars",
|
120
|
-
charsClass: "char",
|
121
|
-
});
|
122
|
-
textElement.splitTextInstance = split;
|
123
|
-
|
124
|
-
gsap.set(split.chars, {
|
125
|
-
yPercent: config.charSplit.yPercent
|
126
|
-
});
|
127
|
-
gsap.set(textElement, { autoAlpha: 1 });
|
128
|
-
});
|
129
|
-
},
|
134
|
+
async initial() {
|
135
|
+
await waitForFonts();
|
136
|
+
|
137
|
+
if (prefersReducedMotion()) {
|
138
|
+
return;
|
139
|
+
}
|
130
140
|
|
131
|
-
|
132
|
-
await waitForFonts();
|
141
|
+
const elements = document.querySelectorAll(".a-char-split > *:first-child");
|
133
142
|
|
134
|
-
|
135
|
-
|
136
|
-
|
143
|
+
elements.forEach((textElement) => {
|
144
|
+
try {
|
145
|
+
const split = SplitText.create(textElement, {
|
146
|
+
type: "words,chars",
|
147
|
+
mask: "chars",
|
148
|
+
charsClass: "char",
|
149
|
+
});
|
150
|
+
textElement.splitTextInstance = split;
|
137
151
|
|
138
|
-
|
139
|
-
|
140
|
-
const tl = gsap.timeline({
|
141
|
-
scrollTrigger: {
|
142
|
-
trigger: textElement,
|
143
|
-
start: config.charSplit.start,
|
144
|
-
invalidateOnRefresh: false,
|
145
|
-
},
|
146
|
-
onComplete: () => {
|
147
|
-
if (textElement.splitTextInstance) {
|
148
|
-
textElement.splitTextInstance.revert();
|
149
|
-
delete textElement.splitTextInstance;
|
150
|
-
}
|
151
|
-
}
|
152
|
-
});
|
153
|
-
|
154
|
-
tl.to(chars, {
|
155
|
-
yPercent: 0,
|
156
|
-
duration: config.charSplit.duration,
|
157
|
-
stagger: config.charSplit.stagger,
|
158
|
-
ease: config.charSplit.ease,
|
159
|
-
});
|
160
|
-
|
161
|
-
activeAnimations.push({ timeline: tl, element: textElement });
|
152
|
+
gsap.set(split.chars, {
|
153
|
+
yPercent: config.charSplit.yPercent,
|
162
154
|
});
|
155
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
156
|
+
} catch (error) {
|
157
|
+
console.warn("Error creating char split:", error);
|
158
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
159
|
+
}
|
160
|
+
});
|
161
|
+
},
|
162
|
+
|
163
|
+
async animate() {
|
164
|
+
await waitForFonts();
|
165
|
+
|
166
|
+
if (prefersReducedMotion()) {
|
167
|
+
return;
|
163
168
|
}
|
169
|
+
|
170
|
+
document
|
171
|
+
.querySelectorAll(".a-char-split > *:first-child")
|
172
|
+
.forEach((textElement) => {
|
173
|
+
try {
|
174
|
+
const chars = textElement.querySelectorAll(".char");
|
175
|
+
const tl = gsap.timeline({
|
176
|
+
scrollTrigger: {
|
177
|
+
trigger: textElement,
|
178
|
+
start: config.charSplit.start,
|
179
|
+
invalidateOnRefresh: false,
|
180
|
+
},
|
181
|
+
onComplete: () => {
|
182
|
+
if (textElement.splitTextInstance) {
|
183
|
+
textElement.splitTextInstance.revert();
|
184
|
+
delete textElement.splitTextInstance;
|
185
|
+
}
|
186
|
+
},
|
187
|
+
});
|
188
|
+
|
189
|
+
tl.to(chars, {
|
190
|
+
yPercent: 0,
|
191
|
+
duration: config.charSplit.duration,
|
192
|
+
stagger: config.charSplit.stagger,
|
193
|
+
ease: config.charSplit.ease,
|
194
|
+
});
|
195
|
+
|
196
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
197
|
+
} catch (error) {
|
198
|
+
console.warn("Error animating char split:", error);
|
199
|
+
}
|
200
|
+
});
|
201
|
+
},
|
164
202
|
};
|
165
203
|
|
166
204
|
const WordSplitAnimations = {
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
const elements = document.querySelectorAll(".a-word-split > *:first-child");
|
175
|
-
|
176
|
-
elements.forEach((textElement) => {
|
177
|
-
const split = SplitText.create(textElement, {
|
178
|
-
type: "words",
|
179
|
-
mask: "words",
|
180
|
-
wordsClass: "word",
|
181
|
-
});
|
182
|
-
textElement.splitTextInstance = split;
|
183
|
-
|
184
|
-
gsap.set(split.words, {
|
185
|
-
yPercent: config.wordSplit.yPercent
|
186
|
-
});
|
187
|
-
gsap.set(textElement, { autoAlpha: 1 });
|
188
|
-
});
|
189
|
-
},
|
205
|
+
async initial() {
|
206
|
+
await waitForFonts();
|
207
|
+
|
208
|
+
if (prefersReducedMotion()) {
|
209
|
+
return;
|
210
|
+
}
|
190
211
|
|
191
|
-
|
192
|
-
await waitForFonts();
|
212
|
+
const elements = document.querySelectorAll(".a-word-split > *:first-child");
|
193
213
|
|
194
|
-
|
195
|
-
|
196
|
-
|
214
|
+
elements.forEach((textElement) => {
|
215
|
+
try {
|
216
|
+
const split = SplitText.create(textElement, {
|
217
|
+
type: "words",
|
218
|
+
mask: "words",
|
219
|
+
wordsClass: "word",
|
220
|
+
});
|
221
|
+
textElement.splitTextInstance = split;
|
197
222
|
|
198
|
-
|
199
|
-
|
200
|
-
const tl = gsap.timeline({
|
201
|
-
scrollTrigger: {
|
202
|
-
trigger: textElement,
|
203
|
-
start: config.wordSplit.start,
|
204
|
-
invalidateOnRefresh: false,
|
205
|
-
},
|
206
|
-
onComplete: () => {
|
207
|
-
if (textElement.splitTextInstance) {
|
208
|
-
textElement.splitTextInstance.revert();
|
209
|
-
delete textElement.splitTextInstance;
|
210
|
-
}
|
211
|
-
}
|
212
|
-
});
|
213
|
-
|
214
|
-
tl.to(words, {
|
215
|
-
yPercent: 0,
|
216
|
-
duration: config.wordSplit.duration,
|
217
|
-
stagger: config.wordSplit.stagger,
|
218
|
-
ease: config.wordSplit.ease,
|
219
|
-
});
|
220
|
-
|
221
|
-
activeAnimations.push({ timeline: tl, element: textElement });
|
223
|
+
gsap.set(split.words, {
|
224
|
+
yPercent: config.wordSplit.yPercent,
|
222
225
|
});
|
226
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
227
|
+
} catch (error) {
|
228
|
+
console.warn("Error creating word split:", error);
|
229
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
230
|
+
}
|
231
|
+
});
|
232
|
+
},
|
233
|
+
|
234
|
+
async animate() {
|
235
|
+
await waitForFonts();
|
236
|
+
|
237
|
+
if (prefersReducedMotion()) {
|
238
|
+
return;
|
223
239
|
}
|
240
|
+
|
241
|
+
document
|
242
|
+
.querySelectorAll(".a-word-split > *:first-child")
|
243
|
+
.forEach((textElement) => {
|
244
|
+
try {
|
245
|
+
const words = textElement.querySelectorAll(".word");
|
246
|
+
const tl = gsap.timeline({
|
247
|
+
scrollTrigger: {
|
248
|
+
trigger: textElement,
|
249
|
+
start: config.wordSplit.start,
|
250
|
+
invalidateOnRefresh: false,
|
251
|
+
},
|
252
|
+
onComplete: () => {
|
253
|
+
if (textElement.splitTextInstance) {
|
254
|
+
textElement.splitTextInstance.revert();
|
255
|
+
delete textElement.splitTextInstance;
|
256
|
+
}
|
257
|
+
},
|
258
|
+
});
|
259
|
+
|
260
|
+
tl.to(words, {
|
261
|
+
yPercent: 0,
|
262
|
+
duration: config.wordSplit.duration,
|
263
|
+
stagger: config.wordSplit.stagger,
|
264
|
+
ease: config.wordSplit.ease,
|
265
|
+
});
|
266
|
+
|
267
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
268
|
+
} catch (error) {
|
269
|
+
console.warn("Error animating word split:", error);
|
270
|
+
}
|
271
|
+
});
|
272
|
+
},
|
224
273
|
};
|
225
274
|
|
226
275
|
const LineSplitAnimations = {
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
if (prefersReducedMotion()) {
|
231
|
-
return;
|
232
|
-
}
|
233
|
-
|
234
|
-
const elements = document.querySelectorAll(".a-line-split > *:first-child");
|
235
|
-
|
236
|
-
elements.forEach((textElement) => {
|
237
|
-
const split = SplitText.create(textElement, {
|
238
|
-
type: "lines",
|
239
|
-
mask: "lines",
|
240
|
-
linesClass: "line",
|
241
|
-
});
|
242
|
-
textElement.splitTextInstance = split;
|
243
|
-
|
244
|
-
gsap.set(split.lines, {
|
245
|
-
yPercent: config.lineSplit.yPercent
|
246
|
-
});
|
247
|
-
gsap.set(textElement, { autoAlpha: 1 });
|
248
|
-
});
|
249
|
-
},
|
276
|
+
async initial() {
|
277
|
+
await waitForFonts();
|
250
278
|
|
251
|
-
|
252
|
-
|
279
|
+
if (prefersReducedMotion()) {
|
280
|
+
return;
|
281
|
+
}
|
253
282
|
|
254
|
-
|
255
|
-
return;
|
256
|
-
}
|
283
|
+
const elements = document.querySelectorAll(".a-line-split > *:first-child");
|
257
284
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
invalidateOnRefresh: false,
|
265
|
-
},
|
266
|
-
onComplete: () => {
|
267
|
-
if (textElement.splitTextInstance) {
|
268
|
-
textElement.splitTextInstance.revert();
|
269
|
-
delete textElement.splitTextInstance;
|
270
|
-
}
|
271
|
-
}
|
272
|
-
});
|
273
|
-
|
274
|
-
tl.to(lines, {
|
275
|
-
yPercent: 0,
|
276
|
-
duration: config.lineSplit.duration,
|
277
|
-
stagger: config.lineSplit.stagger,
|
278
|
-
ease: config.lineSplit.ease,
|
279
|
-
});
|
280
|
-
|
281
|
-
activeAnimations.push({ timeline: tl, element: textElement });
|
285
|
+
elements.forEach((textElement) => {
|
286
|
+
try {
|
287
|
+
const split = SplitText.create(textElement, {
|
288
|
+
type: "lines",
|
289
|
+
mask: "lines",
|
290
|
+
linesClass: "line",
|
282
291
|
});
|
292
|
+
textElement.splitTextInstance = split;
|
293
|
+
|
294
|
+
gsap.set(split.lines, {
|
295
|
+
yPercent: config.lineSplit.yPercent,
|
296
|
+
});
|
297
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
298
|
+
} catch (error) {
|
299
|
+
console.warn("Error creating line split:", error);
|
300
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
301
|
+
}
|
302
|
+
});
|
303
|
+
},
|
304
|
+
|
305
|
+
async animate() {
|
306
|
+
await waitForFonts();
|
307
|
+
|
308
|
+
if (prefersReducedMotion()) {
|
309
|
+
return;
|
283
310
|
}
|
311
|
+
|
312
|
+
document
|
313
|
+
.querySelectorAll(".a-line-split > *:first-child")
|
314
|
+
.forEach((textElement) => {
|
315
|
+
try {
|
316
|
+
const lines = textElement.querySelectorAll(".line");
|
317
|
+
const tl = gsap.timeline({
|
318
|
+
scrollTrigger: {
|
319
|
+
trigger: textElement,
|
320
|
+
start: config.lineSplit.start,
|
321
|
+
invalidateOnRefresh: false,
|
322
|
+
},
|
323
|
+
onComplete: () => {
|
324
|
+
if (textElement.splitTextInstance) {
|
325
|
+
textElement.splitTextInstance.revert();
|
326
|
+
delete textElement.splitTextInstance;
|
327
|
+
}
|
328
|
+
},
|
329
|
+
});
|
330
|
+
|
331
|
+
tl.to(lines, {
|
332
|
+
yPercent: 0,
|
333
|
+
duration: config.lineSplit.duration,
|
334
|
+
stagger: config.lineSplit.stagger,
|
335
|
+
ease: config.lineSplit.ease,
|
336
|
+
});
|
337
|
+
|
338
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
339
|
+
} catch (error) {
|
340
|
+
console.warn("Error animating line split:", error);
|
341
|
+
}
|
342
|
+
});
|
343
|
+
},
|
284
344
|
};
|
285
345
|
|
286
346
|
const AppearAnimations = {
|
287
|
-
|
288
|
-
|
347
|
+
async initial() {
|
348
|
+
await waitForFonts();
|
289
349
|
|
290
|
-
|
291
|
-
|
292
|
-
|
350
|
+
if (prefersReducedMotion()) {
|
351
|
+
return;
|
352
|
+
}
|
293
353
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
354
|
+
const elements = document.querySelectorAll(".a-appear");
|
355
|
+
elements.forEach((element) => {
|
356
|
+
try {
|
357
|
+
gsap.set(element, {
|
358
|
+
y: config.appear.y,
|
359
|
+
opacity: 0,
|
300
360
|
});
|
301
|
-
|
361
|
+
} catch (error) {
|
362
|
+
console.warn("Error setting appear initial state:", error);
|
363
|
+
}
|
364
|
+
});
|
365
|
+
},
|
302
366
|
|
303
|
-
|
304
|
-
|
367
|
+
async animate() {
|
368
|
+
await waitForFonts();
|
305
369
|
|
306
|
-
|
307
|
-
|
308
|
-
|
370
|
+
if (prefersReducedMotion()) {
|
371
|
+
return;
|
372
|
+
}
|
373
|
+
|
374
|
+
document.querySelectorAll(".a-appear").forEach((element) => {
|
375
|
+
try {
|
376
|
+
const tl = gsap.timeline({
|
377
|
+
scrollTrigger: {
|
378
|
+
trigger: element,
|
379
|
+
start: config.appear.start,
|
380
|
+
invalidateOnRefresh: false,
|
381
|
+
},
|
382
|
+
});
|
309
383
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
invalidateOnRefresh: false,
|
316
|
-
}
|
317
|
-
});
|
318
|
-
|
319
|
-
tl.to(element, {
|
320
|
-
y: 0,
|
321
|
-
opacity: 1,
|
322
|
-
duration: config.appear.duration,
|
323
|
-
ease: config.appear.ease
|
324
|
-
});
|
325
|
-
|
326
|
-
activeAnimations.push({ timeline: tl, element: element });
|
384
|
+
tl.to(element, {
|
385
|
+
y: 0,
|
386
|
+
opacity: 1,
|
387
|
+
duration: config.appear.duration,
|
388
|
+
ease: config.appear.ease,
|
327
389
|
});
|
328
390
|
|
391
|
+
activeAnimations.push({ timeline: tl, element: element });
|
392
|
+
} catch (error) {
|
393
|
+
console.warn("Error animating appear:", error);
|
394
|
+
}
|
395
|
+
});
|
396
|
+
},
|
397
|
+
};
|
398
|
+
|
399
|
+
const RevealAnimations = {
|
400
|
+
async initial() {
|
401
|
+
await waitForFonts();
|
402
|
+
|
403
|
+
if (prefersReducedMotion()) {
|
404
|
+
return;
|
329
405
|
}
|
406
|
+
|
407
|
+
const elements = document.querySelectorAll(".a-reveal");
|
408
|
+
elements.forEach((element) => {
|
409
|
+
try {
|
410
|
+
gsap.set(element, {
|
411
|
+
y: config.reveal.y,
|
412
|
+
opacity: 0,
|
413
|
+
});
|
414
|
+
} catch (error) {
|
415
|
+
console.warn("Error setting reveal initial state:", error);
|
416
|
+
}
|
417
|
+
});
|
418
|
+
},
|
419
|
+
|
420
|
+
async animate() {
|
421
|
+
await waitForFonts();
|
422
|
+
|
423
|
+
if (prefersReducedMotion()) {
|
424
|
+
return;
|
425
|
+
}
|
426
|
+
|
427
|
+
document.querySelectorAll(".a-reveal").forEach((element) => {
|
428
|
+
try {
|
429
|
+
const tl = gsap.timeline({
|
430
|
+
scrollTrigger: {
|
431
|
+
trigger: element,
|
432
|
+
start: config.reveal.start,
|
433
|
+
invalidateOnRefresh: false,
|
434
|
+
},
|
435
|
+
});
|
436
|
+
|
437
|
+
tl.to(element, {
|
438
|
+
y: 0,
|
439
|
+
opacity: 1,
|
440
|
+
duration: config.reveal.duration,
|
441
|
+
ease: config.reveal.ease,
|
442
|
+
});
|
443
|
+
|
444
|
+
activeAnimations.push({ timeline: tl, element: element });
|
445
|
+
} catch (error) {
|
446
|
+
console.warn("Error animating reveal:", error);
|
447
|
+
}
|
448
|
+
});
|
449
|
+
},
|
330
450
|
};
|
331
451
|
|
332
452
|
async function setInitialStates() {
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
453
|
+
await Promise.all([
|
454
|
+
CharSplitAnimations.initial(),
|
455
|
+
WordSplitAnimations.initial(),
|
456
|
+
LineSplitAnimations.initial(),
|
457
|
+
AppearAnimations.initial(),
|
458
|
+
RevealAnimations.initial(),
|
459
|
+
]);
|
339
460
|
}
|
340
461
|
|
341
462
|
async function initAnimations() {
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
463
|
+
if (config.global.animationDelay > 0) {
|
464
|
+
await new Promise((resolve) =>
|
465
|
+
setTimeout(resolve, config.global.animationDelay * 1000),
|
466
|
+
);
|
467
|
+
}
|
468
|
+
|
469
|
+
await Promise.all([
|
470
|
+
CharSplitAnimations.animate(),
|
471
|
+
WordSplitAnimations.animate(),
|
472
|
+
LineSplitAnimations.animate(),
|
473
|
+
AppearAnimations.animate(),
|
474
|
+
RevealAnimations.animate(),
|
475
|
+
]);
|
352
476
|
}
|
353
477
|
|
354
478
|
export async function init() {
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
479
|
+
// Prevent duplicate initialization
|
480
|
+
const elements = document.querySelectorAll(
|
481
|
+
"[data-hs-anim-text]:not([data-initialized])",
|
482
|
+
);
|
483
|
+
if (
|
484
|
+
elements.length === 0 &&
|
485
|
+
document.querySelectorAll(
|
486
|
+
".a-char-split:not([data-initialized]), .a-word-split:not([data-initialized]), .a-line-split:not([data-initialized]), .a-appear:not([data-initialized]), .a-reveal:not([data-initialized])",
|
487
|
+
).length === 0
|
488
|
+
) {
|
489
|
+
return { result: "anim-text already initialized", destroy: () => {} };
|
490
|
+
}
|
491
|
+
|
492
|
+
// Mark elements as initialized
|
493
|
+
elements.forEach((el) => el.setAttribute("data-initialized", "true"));
|
494
|
+
document
|
495
|
+
.querySelectorAll(
|
496
|
+
".a-char-split, .a-word-split, .a-line-split, .a-appear, .a-reveal",
|
497
|
+
)
|
498
|
+
.forEach((el) => {
|
499
|
+
el.setAttribute("data-initialized", "true");
|
374
500
|
});
|
375
501
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
502
|
+
if (prefersReducedMotion()) {
|
503
|
+
// For reduced motion, just show elements without animation
|
504
|
+
showElementsWithoutAnimation();
|
505
|
+
} else {
|
506
|
+
await setInitialStates();
|
507
|
+
initAnimations();
|
508
|
+
}
|
509
|
+
|
510
|
+
// Set up resize handler with cleanup
|
511
|
+
let lastWidth = window.innerWidth;
|
512
|
+
resizeHandler = () => {
|
513
|
+
const currentWidth = window.innerWidth;
|
514
|
+
if (currentWidth !== lastWidth) {
|
515
|
+
lastWidth = currentWidth;
|
516
|
+
clearTimeout(resizeTimeout);
|
517
|
+
resizeTimeout = setTimeout(() => {
|
518
|
+
try {
|
519
|
+
ScrollTrigger.refresh();
|
520
|
+
} catch (error) {
|
521
|
+
console.warn("Error refreshing ScrollTrigger:", error);
|
385
522
|
}
|
386
|
-
|
523
|
+
}, 300);
|
524
|
+
}
|
525
|
+
};
|
526
|
+
window.addEventListener("resize", resizeHandler);
|
527
|
+
|
528
|
+
// Initialize API with proper checks
|
529
|
+
if (!window[API_NAME]) {
|
530
|
+
window[API_NAME] = {};
|
531
|
+
}
|
532
|
+
|
533
|
+
const api = window[API_NAME];
|
534
|
+
api.textAnimations = {
|
535
|
+
config: config,
|
536
|
+
updateConfig: updateConfig,
|
537
|
+
start: startTextAnimations,
|
538
|
+
kill: killTextAnimations,
|
539
|
+
restart: () => {
|
540
|
+
killTextAnimations();
|
541
|
+
startTextAnimations();
|
542
|
+
},
|
543
|
+
cleanup: () => {
|
544
|
+
killTextAnimations();
|
545
|
+
window.removeEventListener("resize", resizeHandler);
|
546
|
+
clearTimeout(resizeTimeout);
|
547
|
+
},
|
548
|
+
};
|
549
|
+
|
550
|
+
// Return destroy function
|
551
|
+
const destroy = () => {
|
552
|
+
// Kill all animations
|
553
|
+
killTextAnimations();
|
387
554
|
|
388
|
-
|
389
|
-
|
555
|
+
// Remove resize handler
|
556
|
+
if (resizeHandler) {
|
557
|
+
window.removeEventListener("resize", resizeHandler);
|
558
|
+
}
|
559
|
+
|
560
|
+
// Clear timeout
|
561
|
+
if (resizeTimeout) {
|
562
|
+
clearTimeout(resizeTimeout);
|
563
|
+
}
|
564
|
+
|
565
|
+
// Remove initialized markers
|
566
|
+
document.querySelectorAll("[data-initialized]").forEach((el) => {
|
567
|
+
el.removeAttribute("data-initialized");
|
568
|
+
});
|
569
|
+
|
570
|
+
// Clean up API
|
571
|
+
if (api.textAnimations) {
|
572
|
+
delete api.textAnimations;
|
573
|
+
}
|
574
|
+
};
|
575
|
+
|
576
|
+
return { result: "anim-text initialized", destroy };
|
577
|
+
}
|