@hortonstudio/main 1.4.4 → 1.4.5

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.
@@ -106,28 +106,10 @@ async function waitForFonts() {
106
106
  }
107
107
 
108
108
  function findTextElement(container) {
109
- // Safari-optimized: Use simple querySelector instead of TreeWalker
110
- // TreeWalker is notoriously slow in Safari/WebKit
111
- const directTextElement = container.querySelector(':scope > *:first-child');
112
- if (directTextElement && directTextElement.textContent.trim()) {
113
- return directTextElement;
114
- }
115
-
116
- // Fallback: check if container itself has text
117
- if (container.textContent.trim()) {
118
- return container;
119
- }
120
-
121
- // Last resort: find any element with text content
122
- const textElements = container.querySelectorAll('*');
123
- for (let i = 0; i < textElements.length; i++) {
124
- const el = textElements[i];
125
- if (el.textContent.trim() && !el.querySelector('*')) {
126
- return el;
127
- }
128
- }
129
-
130
- return container;
109
+ // Simple direct approach: if container has direct text content, use it
110
+ // Otherwise use first child with text content
111
+ const firstChild = container.firstElementChild;
112
+ return firstChild && firstChild.textContent.trim() ? firstChild : container;
131
113
  }
132
114
 
133
115
  function showElementsWithoutAnimation() {
@@ -163,56 +145,21 @@ const CharSplitAnimations = {
163
145
 
164
146
  const elements = document.querySelectorAll('[data-hs-anim="char"]');
165
147
 
166
- // Safari optimization: Process elements in smaller batches to avoid blocking
167
- const processInBatches = (elements, batchSize = 5) => {
168
- return new Promise((resolve) => {
169
- let index = 0;
170
-
171
- const processBatch = () => {
172
- const endIndex = Math.min(index + batchSize, elements.length);
173
-
174
- for (let i = index; i < endIndex; i++) {
175
- const container = elements[i];
176
-
177
- // Skip if already processed
178
- if (container.hasAttribute('data-split-processed')) continue;
179
-
180
- const textElement = findTextElement(container);
181
- if (!textElement) continue;
182
-
183
- try {
184
- const split = SplitText.create(textElement, {
185
- type: "words,chars",
186
- mask: "chars",
187
- charsClass: "char",
188
- });
189
- container.splitTextInstance = split;
190
- container.setAttribute('data-split-processed', 'true');
191
-
192
- gsap.set(split.chars, {
193
- yPercent: config.charSplit.yPercent,
194
- });
195
- gsap.set(textElement, { autoAlpha: 1 });
196
- } catch (error) {
197
- console.warn("Error creating char split:", error);
198
- gsap.set(textElement, { autoAlpha: 1 });
199
- }
200
- }
201
-
202
- index = endIndex;
203
-
204
- if (index < elements.length) {
205
- requestAnimationFrame(processBatch);
206
- } else {
207
- resolve();
208
- }
209
- };
210
-
211
- processBatch();
148
+ elements.forEach((container) => {
149
+ const textElement = findTextElement(container);
150
+
151
+ const split = SplitText.create(textElement, {
152
+ type: "words,chars",
153
+ mask: "chars",
154
+ charsClass: "char",
212
155
  });
213
- };
156
+ container.splitTextInstance = split;
214
157
 
215
- await processInBatches(elements);
158
+ gsap.set(split.chars, {
159
+ yPercent: config.charSplit.yPercent,
160
+ });
161
+ gsap.set(textElement, { autoAlpha: 1 });
162
+ });
216
163
  },
217
164
 
218
165
  async animate() {
@@ -222,48 +169,35 @@ const CharSplitAnimations = {
222
169
  return;
223
170
  }
224
171
 
225
- // Safari optimization: Cache elements and reduce DOM queries
226
172
  const elements = document.querySelectorAll('[data-hs-anim="char"]');
227
173
 
228
174
  elements.forEach((container) => {
229
- const textElement = findTextElement(container);
230
- if (!textElement) return;
175
+ const chars = container.querySelectorAll(".char");
231
176
 
232
- try {
233
- const chars = container.querySelectorAll(".char");
234
- if (chars.length === 0) return;
235
-
236
- const tl = gsap.timeline({
237
- scrollTrigger: {
238
- trigger: container,
239
- start: config.charSplit.start,
240
- invalidateOnRefresh: true,
241
- toggleActions: "play none none none",
242
- once: true,
243
- },
244
- onComplete: () => {
245
- // Use requestAnimationFrame for cleanup to avoid Safari blocking
246
- requestAnimationFrame(() => {
247
- if (container.splitTextInstance) {
248
- container.splitTextInstance.revert();
249
- delete container.splitTextInstance;
250
- container.removeAttribute('data-split-processed');
251
- }
252
- });
253
- },
254
- });
255
-
256
- tl.to(chars, {
257
- yPercent: 0,
258
- duration: config.charSplit.duration,
259
- stagger: config.charSplit.stagger,
260
- ease: config.charSplit.ease,
261
- });
262
-
263
- activeAnimations.push({ timeline: tl, element: textElement });
264
- } catch (error) {
265
- console.warn("Error animating char split:", error);
266
- }
177
+ const tl = gsap.timeline({
178
+ scrollTrigger: {
179
+ trigger: container,
180
+ start: config.charSplit.start,
181
+ invalidateOnRefresh: true,
182
+ toggleActions: "play none none none",
183
+ once: true,
184
+ },
185
+ onComplete: () => {
186
+ if (container.splitTextInstance) {
187
+ container.splitTextInstance.revert();
188
+ delete container.splitTextInstance;
189
+ }
190
+ },
191
+ });
192
+
193
+ tl.to(chars, {
194
+ yPercent: 0,
195
+ duration: config.charSplit.duration,
196
+ stagger: config.charSplit.stagger,
197
+ ease: config.charSplit.ease,
198
+ });
199
+
200
+ activeAnimations.push({ timeline: tl, element: container });
267
201
  });
268
202
  },
269
203
  };
@@ -279,28 +213,19 @@ const WordSplitAnimations = {
279
213
  const elements = document.querySelectorAll('[data-hs-anim="word"]');
280
214
 
281
215
  elements.forEach((container) => {
282
- // Skip if already processed
283
- if (container.hasAttribute('data-split-processed')) return;
284
-
285
216
  const textElement = findTextElement(container);
286
- if (!textElement) return;
287
- try {
288
- const split = SplitText.create(textElement, {
289
- type: "words",
290
- mask: "words",
291
- wordsClass: "word",
292
- });
293
- container.splitTextInstance = split;
294
- container.setAttribute('data-split-processed', 'true');
295
-
296
- gsap.set(split.words, {
297
- yPercent: config.wordSplit.yPercent,
298
- });
299
- gsap.set(textElement, { autoAlpha: 1 });
300
- } catch (error) {
301
- console.warn("Error creating word split:", error);
302
- gsap.set(textElement, { autoAlpha: 1 });
303
- }
217
+
218
+ const split = SplitText.create(textElement, {
219
+ type: "words",
220
+ mask: "words",
221
+ wordsClass: "word",
222
+ });
223
+ container.splitTextInstance = split;
224
+
225
+ gsap.set(split.words, {
226
+ yPercent: config.wordSplit.yPercent,
227
+ });
228
+ gsap.set(textElement, { autoAlpha: 1 });
304
229
  });
305
230
  },
306
231
 
@@ -311,42 +236,36 @@ const WordSplitAnimations = {
311
236
  return;
312
237
  }
313
238
 
314
- document
315
- .querySelectorAll('[data-hs-anim="word"]')
316
- .forEach((container) => {
317
- const textElement = findTextElement(container);
318
- if (!textElement) return;
319
- try {
320
- const words = container.querySelectorAll(".word");
321
- const tl = gsap.timeline({
322
- scrollTrigger: {
323
- trigger: container,
324
- start: config.wordSplit.start,
325
- invalidateOnRefresh: true,
326
- toggleActions: "play none none none",
327
- once: true,
328
- },
329
- onComplete: () => {
330
- if (container.splitTextInstance) {
331
- container.splitTextInstance.revert();
332
- delete container.splitTextInstance;
333
- container.removeAttribute('data-split-processed');
334
- }
335
- },
336
- });
337
-
338
- tl.to(words, {
339
- yPercent: 0,
340
- duration: config.wordSplit.duration,
341
- stagger: config.wordSplit.stagger,
342
- ease: config.wordSplit.ease,
343
- });
344
-
345
- activeAnimations.push({ timeline: tl, element: textElement });
346
- } catch (error) {
347
- console.warn("Error animating word split:", error);
348
- }
239
+ const elements = document.querySelectorAll('[data-hs-anim="word"]');
240
+
241
+ elements.forEach((container) => {
242
+ const words = container.querySelectorAll(".word");
243
+
244
+ const tl = gsap.timeline({
245
+ scrollTrigger: {
246
+ trigger: container,
247
+ start: config.wordSplit.start,
248
+ invalidateOnRefresh: true,
249
+ toggleActions: "play none none none",
250
+ once: true,
251
+ },
252
+ onComplete: () => {
253
+ if (container.splitTextInstance) {
254
+ container.splitTextInstance.revert();
255
+ delete container.splitTextInstance;
256
+ }
257
+ },
349
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: container });
268
+ });
350
269
  },
351
270
  };
352
271
 
@@ -361,28 +280,19 @@ const LineSplitAnimations = {
361
280
  const elements = document.querySelectorAll('[data-hs-anim="line"]');
362
281
 
363
282
  elements.forEach((container) => {
364
- // Skip if already processed
365
- if (container.hasAttribute('data-split-processed')) return;
366
-
367
283
  const textElement = findTextElement(container);
368
- if (!textElement) return;
369
- try {
370
- const split = SplitText.create(textElement, {
371
- type: "lines",
372
- mask: "lines",
373
- linesClass: "line",
374
- });
375
- container.splitTextInstance = split;
376
- container.setAttribute('data-split-processed', 'true');
377
-
378
- gsap.set(split.lines, {
379
- yPercent: config.lineSplit.yPercent,
380
- });
381
- gsap.set(textElement, { autoAlpha: 1 });
382
- } catch (error) {
383
- console.warn("Error creating line split:", error);
384
- gsap.set(textElement, { autoAlpha: 1 });
385
- }
284
+
285
+ const split = SplitText.create(textElement, {
286
+ type: "lines",
287
+ mask: "lines",
288
+ linesClass: "line",
289
+ });
290
+ container.splitTextInstance = split;
291
+
292
+ gsap.set(split.lines, {
293
+ yPercent: config.lineSplit.yPercent,
294
+ });
295
+ gsap.set(textElement, { autoAlpha: 1 });
386
296
  });
387
297
  },
388
298
 
@@ -393,42 +303,36 @@ const LineSplitAnimations = {
393
303
  return;
394
304
  }
395
305
 
396
- document
397
- .querySelectorAll('[data-hs-anim="line"]')
398
- .forEach((container) => {
399
- const textElement = findTextElement(container);
400
- if (!textElement) return;
401
- try {
402
- const lines = container.querySelectorAll(".line");
403
- const tl = gsap.timeline({
404
- scrollTrigger: {
405
- trigger: container,
406
- start: config.lineSplit.start,
407
- invalidateOnRefresh: true,
408
- toggleActions: "play none none none",
409
- once: true,
410
- },
411
- onComplete: () => {
412
- if (container.splitTextInstance) {
413
- container.splitTextInstance.revert();
414
- delete container.splitTextInstance;
415
- container.removeAttribute('data-split-processed');
416
- }
417
- },
418
- });
419
-
420
- tl.to(lines, {
421
- yPercent: 0,
422
- duration: config.lineSplit.duration,
423
- stagger: config.lineSplit.stagger,
424
- ease: config.lineSplit.ease,
425
- });
426
-
427
- activeAnimations.push({ timeline: tl, element: textElement });
428
- } catch (error) {
429
- console.warn("Error animating line split:", error);
430
- }
306
+ const elements = document.querySelectorAll('[data-hs-anim="line"]');
307
+
308
+ elements.forEach((container) => {
309
+ const lines = container.querySelectorAll(".line");
310
+
311
+ const tl = gsap.timeline({
312
+ scrollTrigger: {
313
+ trigger: container,
314
+ start: config.lineSplit.start,
315
+ invalidateOnRefresh: true,
316
+ toggleActions: "play none none none",
317
+ once: true,
318
+ },
319
+ onComplete: () => {
320
+ if (container.splitTextInstance) {
321
+ container.splitTextInstance.revert();
322
+ delete container.splitTextInstance;
323
+ }
324
+ },
325
+ });
326
+
327
+ tl.to(lines, {
328
+ yPercent: 0,
329
+ duration: config.lineSplit.duration,
330
+ stagger: config.lineSplit.stagger,
331
+ ease: config.lineSplit.ease,
431
332
  });
333
+
334
+ activeAnimations.push({ timeline: tl, element: container });
335
+ });
432
336
  },
433
337
  };
434
338
 
@@ -442,14 +346,10 @@ const AppearAnimations = {
442
346
 
443
347
  const elements = document.querySelectorAll('[data-hs-anim="appear"]');
444
348
  elements.forEach((element) => {
445
- try {
446
- gsap.set(element, {
447
- y: config.appear.y,
448
- opacity: 0,
449
- });
450
- } catch (error) {
451
- console.warn("Error setting appear initial state:", error);
452
- }
349
+ gsap.set(element, {
350
+ y: config.appear.y,
351
+ opacity: 0,
352
+ });
453
353
  });
454
354
  },
455
355
 
@@ -460,29 +360,27 @@ const AppearAnimations = {
460
360
  return;
461
361
  }
462
362
 
463
- document.querySelectorAll('[data-hs-anim="appear"]').forEach((element) => {
464
- try {
465
- const tl = gsap.timeline({
466
- scrollTrigger: {
467
- trigger: element,
468
- start: config.appear.start,
469
- invalidateOnRefresh: true,
470
- toggleActions: "play none none none",
471
- once: true,
472
- },
473
- });
474
-
475
- tl.to(element, {
476
- y: 0,
477
- opacity: 1,
478
- duration: config.appear.duration,
479
- ease: config.appear.ease,
480
- });
481
-
482
- activeAnimations.push({ timeline: tl, element: element });
483
- } catch (error) {
484
- console.warn("Error animating appear:", error);
485
- }
363
+ const elements = document.querySelectorAll('[data-hs-anim="appear"]');
364
+
365
+ elements.forEach((element) => {
366
+ const tl = gsap.timeline({
367
+ scrollTrigger: {
368
+ trigger: element,
369
+ start: config.appear.start,
370
+ invalidateOnRefresh: true,
371
+ toggleActions: "play none none none",
372
+ once: true,
373
+ },
374
+ });
375
+
376
+ tl.to(element, {
377
+ y: 0,
378
+ opacity: 1,
379
+ duration: config.appear.duration,
380
+ ease: config.appear.ease,
381
+ });
382
+
383
+ activeAnimations.push({ timeline: tl, element: element });
486
384
  });
487
385
  },
488
386
  };
@@ -497,14 +395,10 @@ const RevealAnimations = {
497
395
 
498
396
  const elements = document.querySelectorAll('[data-hs-anim="reveal"]');
499
397
  elements.forEach((element) => {
500
- try {
501
- gsap.set(element, {
502
- y: config.reveal.y,
503
- opacity: 0,
504
- });
505
- } catch (error) {
506
- console.warn("Error setting reveal initial state:", error);
507
- }
398
+ gsap.set(element, {
399
+ y: config.reveal.y,
400
+ opacity: 0,
401
+ });
508
402
  });
509
403
  },
510
404
 
@@ -515,29 +409,27 @@ const RevealAnimations = {
515
409
  return;
516
410
  }
517
411
 
518
- document.querySelectorAll('[data-hs-anim="reveal"]').forEach((element) => {
519
- try {
520
- const tl = gsap.timeline({
521
- scrollTrigger: {
522
- trigger: element,
523
- start: config.reveal.start,
524
- invalidateOnRefresh: true,
525
- toggleActions: "play none none none",
526
- once: true,
527
- },
528
- });
529
-
530
- tl.to(element, {
531
- y: 0,
532
- opacity: 1,
533
- duration: config.reveal.duration,
534
- ease: config.reveal.ease,
535
- });
536
-
537
- activeAnimations.push({ timeline: tl, element: element });
538
- } catch (error) {
539
- console.warn("Error animating reveal:", error);
540
- }
412
+ const elements = document.querySelectorAll('[data-hs-anim="reveal"]');
413
+
414
+ elements.forEach((element) => {
415
+ const tl = gsap.timeline({
416
+ scrollTrigger: {
417
+ trigger: element,
418
+ start: config.reveal.start,
419
+ invalidateOnRefresh: true,
420
+ toggleActions: "play none none none",
421
+ once: true,
422
+ },
423
+ });
424
+
425
+ tl.to(element, {
426
+ y: 0,
427
+ opacity: 1,
428
+ duration: config.reveal.duration,
429
+ ease: config.reveal.ease,
430
+ });
431
+
432
+ activeAnimations.push({ timeline: tl, element: element });
541
433
  });
542
434
  },
543
435
  };
@@ -552,15 +444,11 @@ const GroupAnimations = {
552
444
 
553
445
  const elements = document.querySelectorAll('[data-hs-anim="group"]');
554
446
  elements.forEach((element) => {
555
- try {
556
- const children = Array.from(element.children);
557
- gsap.set(children, {
558
- y: config.appear.y,
559
- opacity: 0,
560
- });
561
- } catch (error) {
562
- console.warn("Error setting group initial state:", error);
563
- }
447
+ const children = Array.from(element.children);
448
+ gsap.set(children, {
449
+ y: config.appear.y,
450
+ opacity: 0,
451
+ });
564
452
  });
565
453
  },
566
454
 
@@ -571,31 +459,29 @@ const GroupAnimations = {
571
459
  return;
572
460
  }
573
461
 
574
- document.querySelectorAll('[data-hs-anim="group"]').forEach((element) => {
575
- try {
576
- const children = Array.from(element.children);
577
- const tl = gsap.timeline({
578
- scrollTrigger: {
579
- trigger: element,
580
- start: config.appear.start,
581
- invalidateOnRefresh: true,
582
- toggleActions: "play none none none",
583
- once: true,
584
- },
585
- });
586
-
587
- tl.to(children, {
588
- y: 0,
589
- opacity: 1,
590
- duration: config.appear.duration,
591
- ease: config.appear.ease,
592
- stagger: 0.1,
593
- });
594
-
595
- activeAnimations.push({ timeline: tl, element: element });
596
- } catch (error) {
597
- console.warn("Error animating group:", error);
598
- }
462
+ const elements = document.querySelectorAll('[data-hs-anim="group"]');
463
+
464
+ elements.forEach((element) => {
465
+ const children = Array.from(element.children);
466
+ const tl = gsap.timeline({
467
+ scrollTrigger: {
468
+ trigger: element,
469
+ start: config.appear.start,
470
+ invalidateOnRefresh: true,
471
+ toggleActions: "play none none none",
472
+ once: true,
473
+ },
474
+ });
475
+
476
+ tl.to(children, {
477
+ y: 0,
478
+ opacity: 1,
479
+ duration: config.appear.duration,
480
+ ease: config.appear.ease,
481
+ stagger: 0.1,
482
+ });
483
+
484
+ activeAnimations.push({ timeline: tl, element: element });
599
485
  });
600
486
  },
601
487
  };
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // Version:1.4.4
1
+ // Version:1.4.5
2
2
 
3
3
  const API_NAME = "hsmain";
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hortonstudio/main",
3
- "version": "1.4.4",
3
+ "version": "1.4.5",
4
4
  "description": "Animation and utility library for client websites",
5
5
  "main": "index.js",
6
6
  "type": "module",