@hortonstudio/main 1.2.35 → 1.4.1
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 +532 -317
- package/animations/transition.js +36 -21
- package/autoInit/accessibility.js +173 -50
- package/autoInit/counter.js +338 -0
- package/autoInit/form.js +471 -0
- package/autoInit/modal.js +43 -38
- package/autoInit/navbar.js +494 -371
- package/autoInit/smooth-scroll.js +86 -84
- package/index.js +138 -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,604 @@
|
|
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: true,
|
180
|
+
toggleActions: "play none none none",
|
181
|
+
once: true,
|
182
|
+
},
|
183
|
+
onComplete: () => {
|
184
|
+
if (textElement.splitTextInstance) {
|
185
|
+
textElement.splitTextInstance.revert();
|
186
|
+
delete textElement.splitTextInstance;
|
187
|
+
}
|
188
|
+
},
|
189
|
+
});
|
190
|
+
|
191
|
+
tl.to(chars, {
|
192
|
+
yPercent: 0,
|
193
|
+
duration: config.charSplit.duration,
|
194
|
+
stagger: config.charSplit.stagger,
|
195
|
+
ease: config.charSplit.ease,
|
196
|
+
});
|
197
|
+
|
198
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
199
|
+
} catch (error) {
|
200
|
+
console.warn("Error animating char split:", error);
|
201
|
+
}
|
202
|
+
});
|
203
|
+
},
|
164
204
|
};
|
165
205
|
|
166
206
|
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
|
-
},
|
207
|
+
async initial() {
|
208
|
+
await waitForFonts();
|
209
|
+
|
210
|
+
if (prefersReducedMotion()) {
|
211
|
+
return;
|
212
|
+
}
|
190
213
|
|
191
|
-
|
192
|
-
await waitForFonts();
|
214
|
+
const elements = document.querySelectorAll(".a-word-split > *:first-child");
|
193
215
|
|
194
|
-
|
195
|
-
|
196
|
-
|
216
|
+
elements.forEach((textElement) => {
|
217
|
+
try {
|
218
|
+
const split = SplitText.create(textElement, {
|
219
|
+
type: "words",
|
220
|
+
mask: "words",
|
221
|
+
wordsClass: "word",
|
222
|
+
});
|
223
|
+
textElement.splitTextInstance = split;
|
197
224
|
|
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 });
|
225
|
+
gsap.set(split.words, {
|
226
|
+
yPercent: config.wordSplit.yPercent,
|
222
227
|
});
|
228
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
229
|
+
} catch (error) {
|
230
|
+
console.warn("Error creating word split:", error);
|
231
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
232
|
+
}
|
233
|
+
});
|
234
|
+
},
|
235
|
+
|
236
|
+
async animate() {
|
237
|
+
await waitForFonts();
|
238
|
+
|
239
|
+
if (prefersReducedMotion()) {
|
240
|
+
return;
|
223
241
|
}
|
242
|
+
|
243
|
+
document
|
244
|
+
.querySelectorAll(".a-word-split > *:first-child")
|
245
|
+
.forEach((textElement) => {
|
246
|
+
try {
|
247
|
+
const words = textElement.querySelectorAll(".word");
|
248
|
+
const tl = gsap.timeline({
|
249
|
+
scrollTrigger: {
|
250
|
+
trigger: textElement,
|
251
|
+
start: config.wordSplit.start,
|
252
|
+
invalidateOnRefresh: true,
|
253
|
+
toggleActions: "play none none none",
|
254
|
+
once: true,
|
255
|
+
},
|
256
|
+
onComplete: () => {
|
257
|
+
if (textElement.splitTextInstance) {
|
258
|
+
textElement.splitTextInstance.revert();
|
259
|
+
delete textElement.splitTextInstance;
|
260
|
+
}
|
261
|
+
},
|
262
|
+
});
|
263
|
+
|
264
|
+
tl.to(words, {
|
265
|
+
yPercent: 0,
|
266
|
+
duration: config.wordSplit.duration,
|
267
|
+
stagger: config.wordSplit.stagger,
|
268
|
+
ease: config.wordSplit.ease,
|
269
|
+
});
|
270
|
+
|
271
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
272
|
+
} catch (error) {
|
273
|
+
console.warn("Error animating word split:", error);
|
274
|
+
}
|
275
|
+
});
|
276
|
+
},
|
224
277
|
};
|
225
278
|
|
226
279
|
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
|
-
},
|
280
|
+
async initial() {
|
281
|
+
await waitForFonts();
|
250
282
|
|
251
|
-
|
252
|
-
|
283
|
+
if (prefersReducedMotion()) {
|
284
|
+
return;
|
285
|
+
}
|
253
286
|
|
254
|
-
|
255
|
-
return;
|
256
|
-
}
|
287
|
+
const elements = document.querySelectorAll(".a-line-split > *:first-child");
|
257
288
|
|
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 });
|
289
|
+
elements.forEach((textElement) => {
|
290
|
+
try {
|
291
|
+
const split = SplitText.create(textElement, {
|
292
|
+
type: "lines",
|
293
|
+
mask: "lines",
|
294
|
+
linesClass: "line",
|
282
295
|
});
|
296
|
+
textElement.splitTextInstance = split;
|
297
|
+
|
298
|
+
gsap.set(split.lines, {
|
299
|
+
yPercent: config.lineSplit.yPercent,
|
300
|
+
});
|
301
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
302
|
+
} catch (error) {
|
303
|
+
console.warn("Error creating line split:", error);
|
304
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
305
|
+
}
|
306
|
+
});
|
307
|
+
},
|
308
|
+
|
309
|
+
async animate() {
|
310
|
+
await waitForFonts();
|
311
|
+
|
312
|
+
if (prefersReducedMotion()) {
|
313
|
+
return;
|
283
314
|
}
|
315
|
+
|
316
|
+
document
|
317
|
+
.querySelectorAll(".a-line-split > *:first-child")
|
318
|
+
.forEach((textElement) => {
|
319
|
+
try {
|
320
|
+
const lines = textElement.querySelectorAll(".line");
|
321
|
+
const tl = gsap.timeline({
|
322
|
+
scrollTrigger: {
|
323
|
+
trigger: textElement,
|
324
|
+
start: config.lineSplit.start,
|
325
|
+
invalidateOnRefresh: true,
|
326
|
+
toggleActions: "play none none none",
|
327
|
+
once: true,
|
328
|
+
},
|
329
|
+
onComplete: () => {
|
330
|
+
if (textElement.splitTextInstance) {
|
331
|
+
textElement.splitTextInstance.revert();
|
332
|
+
delete textElement.splitTextInstance;
|
333
|
+
}
|
334
|
+
},
|
335
|
+
});
|
336
|
+
|
337
|
+
tl.to(lines, {
|
338
|
+
yPercent: 0,
|
339
|
+
duration: config.lineSplit.duration,
|
340
|
+
stagger: config.lineSplit.stagger,
|
341
|
+
ease: config.lineSplit.ease,
|
342
|
+
});
|
343
|
+
|
344
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
345
|
+
} catch (error) {
|
346
|
+
console.warn("Error animating line split:", error);
|
347
|
+
}
|
348
|
+
});
|
349
|
+
},
|
284
350
|
};
|
285
351
|
|
286
352
|
const AppearAnimations = {
|
287
|
-
|
288
|
-
|
353
|
+
async initial() {
|
354
|
+
await waitForFonts();
|
289
355
|
|
290
|
-
|
291
|
-
|
292
|
-
|
356
|
+
if (prefersReducedMotion()) {
|
357
|
+
return;
|
358
|
+
}
|
293
359
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
360
|
+
const elements = document.querySelectorAll(".a-appear");
|
361
|
+
elements.forEach((element) => {
|
362
|
+
try {
|
363
|
+
gsap.set(element, {
|
364
|
+
y: config.appear.y,
|
365
|
+
opacity: 0,
|
300
366
|
});
|
301
|
-
|
367
|
+
} catch (error) {
|
368
|
+
console.warn("Error setting appear initial state:", error);
|
369
|
+
}
|
370
|
+
});
|
371
|
+
},
|
302
372
|
|
303
|
-
|
304
|
-
|
373
|
+
async animate() {
|
374
|
+
await waitForFonts();
|
305
375
|
|
306
|
-
|
307
|
-
|
308
|
-
|
376
|
+
if (prefersReducedMotion()) {
|
377
|
+
return;
|
378
|
+
}
|
379
|
+
|
380
|
+
document.querySelectorAll(".a-appear").forEach((element) => {
|
381
|
+
try {
|
382
|
+
const tl = gsap.timeline({
|
383
|
+
scrollTrigger: {
|
384
|
+
trigger: element,
|
385
|
+
start: config.appear.start,
|
386
|
+
invalidateOnRefresh: true,
|
387
|
+
toggleActions: "play none none none",
|
388
|
+
once: true,
|
389
|
+
},
|
390
|
+
});
|
309
391
|
|
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 });
|
392
|
+
tl.to(element, {
|
393
|
+
y: 0,
|
394
|
+
opacity: 1,
|
395
|
+
duration: config.appear.duration,
|
396
|
+
ease: config.appear.ease,
|
327
397
|
});
|
328
398
|
|
399
|
+
activeAnimations.push({ timeline: tl, element: element });
|
400
|
+
} catch (error) {
|
401
|
+
console.warn("Error animating appear:", error);
|
402
|
+
}
|
403
|
+
});
|
404
|
+
},
|
405
|
+
};
|
406
|
+
|
407
|
+
const RevealAnimations = {
|
408
|
+
async initial() {
|
409
|
+
await waitForFonts();
|
410
|
+
|
411
|
+
if (prefersReducedMotion()) {
|
412
|
+
return;
|
329
413
|
}
|
414
|
+
|
415
|
+
const elements = document.querySelectorAll(".a-reveal");
|
416
|
+
elements.forEach((element) => {
|
417
|
+
try {
|
418
|
+
gsap.set(element, {
|
419
|
+
y: config.reveal.y,
|
420
|
+
opacity: 0,
|
421
|
+
});
|
422
|
+
} catch (error) {
|
423
|
+
console.warn("Error setting reveal initial state:", error);
|
424
|
+
}
|
425
|
+
});
|
426
|
+
},
|
427
|
+
|
428
|
+
async animate() {
|
429
|
+
await waitForFonts();
|
430
|
+
|
431
|
+
if (prefersReducedMotion()) {
|
432
|
+
return;
|
433
|
+
}
|
434
|
+
|
435
|
+
document.querySelectorAll(".a-reveal").forEach((element) => {
|
436
|
+
try {
|
437
|
+
const tl = gsap.timeline({
|
438
|
+
scrollTrigger: {
|
439
|
+
trigger: element,
|
440
|
+
start: config.reveal.start,
|
441
|
+
invalidateOnRefresh: true,
|
442
|
+
toggleActions: "play none none none",
|
443
|
+
once: true,
|
444
|
+
},
|
445
|
+
});
|
446
|
+
|
447
|
+
tl.to(element, {
|
448
|
+
y: 0,
|
449
|
+
opacity: 1,
|
450
|
+
duration: config.reveal.duration,
|
451
|
+
ease: config.reveal.ease,
|
452
|
+
});
|
453
|
+
|
454
|
+
activeAnimations.push({ timeline: tl, element: element });
|
455
|
+
} catch (error) {
|
456
|
+
console.warn("Error animating reveal:", error);
|
457
|
+
}
|
458
|
+
});
|
459
|
+
},
|
330
460
|
};
|
331
461
|
|
332
462
|
async function setInitialStates() {
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
463
|
+
await Promise.all([
|
464
|
+
CharSplitAnimations.initial(),
|
465
|
+
WordSplitAnimations.initial(),
|
466
|
+
LineSplitAnimations.initial(),
|
467
|
+
AppearAnimations.initial(),
|
468
|
+
RevealAnimations.initial(),
|
469
|
+
]);
|
339
470
|
}
|
340
471
|
|
341
472
|
async function initAnimations() {
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
473
|
+
if (config.global.animationDelay > 0) {
|
474
|
+
await new Promise((resolve) =>
|
475
|
+
setTimeout(resolve, config.global.animationDelay * 1000),
|
476
|
+
);
|
477
|
+
}
|
478
|
+
|
479
|
+
await Promise.all([
|
480
|
+
CharSplitAnimations.animate(),
|
481
|
+
WordSplitAnimations.animate(),
|
482
|
+
LineSplitAnimations.animate(),
|
483
|
+
AppearAnimations.animate(),
|
484
|
+
RevealAnimations.animate(),
|
485
|
+
]);
|
352
486
|
}
|
353
487
|
|
354
488
|
export async function init() {
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
489
|
+
// Prevent duplicate initialization
|
490
|
+
const elements = document.querySelectorAll(
|
491
|
+
"[data-hs-anim-text]:not([data-initialized])",
|
492
|
+
);
|
493
|
+
if (
|
494
|
+
elements.length === 0 &&
|
495
|
+
document.querySelectorAll(
|
496
|
+
".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])",
|
497
|
+
).length === 0
|
498
|
+
) {
|
499
|
+
return { result: "anim-text already initialized", destroy: () => {} };
|
500
|
+
}
|
501
|
+
|
502
|
+
// Mark elements as initialized
|
503
|
+
elements.forEach((el) => el.setAttribute("data-initialized", "true"));
|
504
|
+
document
|
505
|
+
.querySelectorAll(
|
506
|
+
".a-char-split, .a-word-split, .a-line-split, .a-appear, .a-reveal",
|
507
|
+
)
|
508
|
+
.forEach((el) => {
|
509
|
+
el.setAttribute("data-initialized", "true");
|
374
510
|
});
|
375
511
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
512
|
+
if (prefersReducedMotion()) {
|
513
|
+
// For reduced motion, just show elements without animation
|
514
|
+
showElementsWithoutAnimation();
|
515
|
+
} else {
|
516
|
+
await setInitialStates();
|
517
|
+
initAnimations();
|
518
|
+
}
|
519
|
+
|
520
|
+
// Set up resize handler with cleanup
|
521
|
+
let lastWidth = window.innerWidth;
|
522
|
+
resizeHandler = () => {
|
523
|
+
const currentWidth = window.innerWidth;
|
524
|
+
if (currentWidth !== lastWidth) {
|
525
|
+
lastWidth = currentWidth;
|
526
|
+
clearTimeout(resizeTimeout);
|
527
|
+
resizeTimeout = setTimeout(() => {
|
528
|
+
try {
|
529
|
+
ScrollTrigger.refresh();
|
530
|
+
} catch (error) {
|
531
|
+
console.warn("Error refreshing ScrollTrigger:", error);
|
385
532
|
}
|
386
|
-
|
533
|
+
}, 300);
|
534
|
+
}
|
535
|
+
};
|
536
|
+
window.addEventListener("resize", resizeHandler);
|
537
|
+
|
538
|
+
// Add page load handler for proper ScrollTrigger refresh timing
|
539
|
+
const handlePageLoad = () => {
|
540
|
+
setTimeout(() => {
|
541
|
+
try {
|
542
|
+
ScrollTrigger.refresh();
|
543
|
+
} catch (error) {
|
544
|
+
console.warn("Error refreshing ScrollTrigger on page load:", error);
|
545
|
+
}
|
546
|
+
}, 100);
|
547
|
+
};
|
548
|
+
|
549
|
+
if (document.readyState === 'complete') {
|
550
|
+
handlePageLoad();
|
551
|
+
} else {
|
552
|
+
window.addEventListener('load', handlePageLoad);
|
553
|
+
}
|
554
|
+
|
555
|
+
// Initialize API with proper checks
|
556
|
+
if (!window[API_NAME]) {
|
557
|
+
window[API_NAME] = {};
|
558
|
+
}
|
559
|
+
|
560
|
+
const api = window[API_NAME];
|
561
|
+
api.textAnimations = {
|
562
|
+
config: config,
|
563
|
+
updateConfig: updateConfig,
|
564
|
+
start: startTextAnimations,
|
565
|
+
kill: killTextAnimations,
|
566
|
+
restart: () => {
|
567
|
+
killTextAnimations();
|
568
|
+
startTextAnimations();
|
569
|
+
},
|
570
|
+
cleanup: () => {
|
571
|
+
killTextAnimations();
|
572
|
+
window.removeEventListener("resize", resizeHandler);
|
573
|
+
clearTimeout(resizeTimeout);
|
574
|
+
},
|
575
|
+
};
|
576
|
+
|
577
|
+
// Return destroy function
|
578
|
+
const destroy = () => {
|
579
|
+
// Kill all animations
|
580
|
+
killTextAnimations();
|
387
581
|
|
388
|
-
|
389
|
-
|
582
|
+
// Remove resize handler
|
583
|
+
if (resizeHandler) {
|
584
|
+
window.removeEventListener("resize", resizeHandler);
|
585
|
+
}
|
586
|
+
|
587
|
+
// Clear timeout
|
588
|
+
if (resizeTimeout) {
|
589
|
+
clearTimeout(resizeTimeout);
|
590
|
+
}
|
591
|
+
|
592
|
+
// Remove initialized markers
|
593
|
+
document.querySelectorAll("[data-initialized]").forEach((el) => {
|
594
|
+
el.removeAttribute("data-initialized");
|
595
|
+
});
|
596
|
+
|
597
|
+
// Clean up API
|
598
|
+
if (api.textAnimations) {
|
599
|
+
delete api.textAnimations;
|
600
|
+
}
|
601
|
+
};
|
602
|
+
|
603
|
+
return { result: "anim-text initialized", destroy };
|
604
|
+
}
|