gclass-anims 1.0.0-beta.6 → 1.0.0-beta.7
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/Animations.js +17 -0
- package/Config.js +2 -2
- package/Listeners.js +32 -6
- package/package.json +1 -1
package/Animations.js
CHANGED
|
@@ -28,6 +28,23 @@ export const finalOpacity = (target) => {
|
|
|
28
28
|
return isNaN(v) ? 1 : v
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// TextPlugin tweens take their endpoints from the LIVE DOM: the `.typewriter`
|
|
32
|
+
// play callbacks pass `el.innerHTML` as the text to type. The tween's from
|
|
33
|
+
// state ("") is applied the instant the tween is created, and a teardown that
|
|
34
|
+
// kills the tween mid-flight leaves that wiped state behind — so a later
|
|
35
|
+
// engine re-init reading `el.innerHTML` again would type an empty (or
|
|
36
|
+
// partially-typed) string forever. Stash the full HTML on first sight and
|
|
37
|
+
// reuse it. The stash only refreshes from SETTLED content: never while a
|
|
38
|
+
// typewriter tween on the element is actively rendering partial progress, and
|
|
39
|
+
// never from a blank DOM. Legit content changes (React re-renders, dynamic
|
|
40
|
+
// `.appear` elements) therefore update the stash naturally.
|
|
41
|
+
export const stashText = (el) => {
|
|
42
|
+
const busy = (el.typewriter || el._scrollTween)?.isActive?.()
|
|
43
|
+
const html = el.innerHTML
|
|
44
|
+
if (!busy && html && html.trim()) el._gcText = html
|
|
45
|
+
return el._gcText !== undefined ? el._gcText : html
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
|
|
32
49
|
//Spawn animations
|
|
33
50
|
|
package/Config.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
spinCCW, spinCW, expandA, typewriter, bell, spawnBlur, spawnFade, spawnXDown,
|
|
4
4
|
spawnXUp, spawnYRight, spawnYLeft, pulse, radiate, hover, expandRight,
|
|
5
5
|
expandLeft, expandUp, expandDown, marquee, countUp,
|
|
6
|
-
spawnClipReveal, curtainHorizontal, curtainVertical,
|
|
6
|
+
spawnClipReveal, curtainHorizontal, curtainVertical, stashText,
|
|
7
7
|
} from './Animations.js'
|
|
8
8
|
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
@@ -91,7 +91,7 @@ export const animations = [
|
|
|
91
91
|
{ sel: ".expand-up", from: { opacity: 0, scaleY: 0 }, play: (el, delay, dur, ease) => expandUp(el, delay, dur, ease) },
|
|
92
92
|
{ sel: ".expand-down", from: { opacity: 0, scaleY: 0 }, play: (el, delay, dur, ease) => expandDown(el, delay, dur, ease) },
|
|
93
93
|
{ sel: ".expand-all", from: { opacity: 0, scale: 0 }, play: (el, delay, dur, ease) => expandA(el, delay, dur, ease) },
|
|
94
|
-
{ sel: ".typewriter", typewriter: true, from: { text: "" }, play: (el, delay, dur, ease) => typewriter(el, el
|
|
94
|
+
{ sel: ".typewriter", typewriter: true, from: { text: "" }, play: (el, delay, dur, ease) => typewriter(el, stashText(el), dur, delay, ease) },
|
|
95
95
|
{ sel: ".typewriter-split", typewriter: true, typewriterSplit: true, from: { opacity: 0 }, play: (el, delay, dur, ease) => null },
|
|
96
96
|
|
|
97
97
|
// Custom-function animation: counts from the `.spawn-num-N` value (N = the
|
package/Listeners.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import gsap from 'gsap'
|
|
2
|
-
import { SpawnV, verticalmove, expandmove, magnet, magnet3d, reset, typewriter, countTargetVars } from './Animations'
|
|
2
|
+
import { SpawnV, verticalmove, expandmove, magnet, magnet3d, reset, typewriter, countTargetVars, stashText } from './Animations'
|
|
3
3
|
import { customAnims } from './CustomAnims'
|
|
4
4
|
import { defaults, normalize } from './Config'
|
|
5
5
|
import { TextPlugin, ScrollTrigger, SplitText } from 'gsap/all'
|
|
@@ -724,7 +724,7 @@ export default function initListeners() {
|
|
|
724
724
|
for (const [key] of Object.entries(from)) {
|
|
725
725
|
if (key === "opacity") to[key] = 1
|
|
726
726
|
else if (key === "filter") to[key] = "blur(0px)"
|
|
727
|
-
else if (key === "text") to[key] = el
|
|
727
|
+
else if (key === "text") to[key] = stashText(el)
|
|
728
728
|
else if (key === "clipPath") to[key] = "inset(0% 0% 0% 0%)"
|
|
729
729
|
else to[key] = key.startsWith("scale") ? 1 : 0
|
|
730
730
|
}
|
|
@@ -767,7 +767,7 @@ export default function initListeners() {
|
|
|
767
767
|
? ([...el.classList].find(c => c.startsWith("ease-"))?.split("-")[1] ?? "none")
|
|
768
768
|
: getEase(el)
|
|
769
769
|
|
|
770
|
-
const fullText = el
|
|
770
|
+
const fullText = stashText(el)
|
|
771
771
|
|
|
772
772
|
const enter = () => {
|
|
773
773
|
if (el._scrollTween) el._scrollTween.kill()
|
|
@@ -902,7 +902,7 @@ export default function initListeners() {
|
|
|
902
902
|
el._spawnTween = playTypewriterSplit(el, delay, duration, elEase)
|
|
903
903
|
} else {
|
|
904
904
|
el.typewriter?.kill()
|
|
905
|
-
el.typewriter = typewriter(el, el
|
|
905
|
+
el.typewriter = typewriter(el, stashText(el), duration, delay, elEase)
|
|
906
906
|
}
|
|
907
907
|
} else {
|
|
908
908
|
el._spawnTween = play(el, delay, duration, getEase(el))
|
|
@@ -1442,18 +1442,44 @@ export default function initListeners() {
|
|
|
1442
1442
|
window.removeEventListener("load", ScrollTrigger.refresh)
|
|
1443
1443
|
clearTimeout(refreshTimer)
|
|
1444
1444
|
scrollTriggers.forEach((t) => {
|
|
1445
|
-
t.
|
|
1445
|
+
const tw = t.trigger._scrollTween
|
|
1446
|
+
// Finalize a mid-flight typewriter entrance at its end state
|
|
1447
|
+
// BEFORE killing: stranded partial text would otherwise be read as
|
|
1448
|
+
// settled content by the next run's stash.
|
|
1449
|
+
if (tw && !tw.reversed() && t.trigger.classList?.contains("typewriter")) tw.progress(1)
|
|
1450
|
+
// kill(true): revert pinning (remove pin-spacers, restore inline
|
|
1451
|
+
// styles) so a later init can re-pin cleanly instead of nesting a
|
|
1452
|
+
// second spacer inside the leaked first one.
|
|
1453
|
+
t.kill(true)
|
|
1446
1454
|
t.trigger._scrollTween?.kill()
|
|
1447
1455
|
delete t.trigger._scrollTween
|
|
1448
1456
|
})
|
|
1449
1457
|
ScrollTrigger.refresh()
|
|
1458
|
+
// Clear per-element wire-up tags. Without this, any engine restart
|
|
1459
|
+
// (initAnimations() called again, StrictMode remounts, route-level
|
|
1460
|
+
// re-mounts that keep DOM nodes alive) would silently skip rewiring:
|
|
1461
|
+
// .scroll/.pin/parallax triggers stay dead (their old ones were just
|
|
1462
|
+
// killed) and clicks/loops/hover never re-bind. `_appeared` goes too,
|
|
1463
|
+
// so dynamically-added .appear elements can animate under the new
|
|
1464
|
+
// engine run. data-gsap-preserved (cross-reset memory) and
|
|
1465
|
+
// data-gsap-ghost (detached .leave clones) are intentionally KEPT.
|
|
1466
|
+
gsap.utils.toArray('[data-gsap-scroll],[data-gsap-setup],[data-gsap-pinned],[data-gsap-scroll-driven]').forEach((el) => {
|
|
1467
|
+
delete el.dataset.gsapScroll
|
|
1468
|
+
delete el.dataset.gsapSetup
|
|
1469
|
+
delete el.dataset.gsapPinned
|
|
1470
|
+
delete el.dataset.gsapScrollDriven
|
|
1471
|
+
delete el._appeared
|
|
1472
|
+
})
|
|
1450
1473
|
registeredListeners.forEach(({ el, type, fn }) => el.removeEventListener(type, fn))
|
|
1451
1474
|
magnetListeners.forEach(({ el, type, fn }) => el.removeEventListener(type, fn))
|
|
1452
1475
|
magnetQuery?.removeEventListener("change", applyMagnet)
|
|
1453
1476
|
loopEls.forEach(({ el, key }) => el[key]?.kill())
|
|
1454
1477
|
cssTweens.forEach((t) => t?.kill())
|
|
1455
1478
|
cssTweens.length = 0
|
|
1456
|
-
gsap.utils.toArray(".typewriter").forEach(el =>
|
|
1479
|
+
gsap.utils.toArray(".typewriter").forEach(el => {
|
|
1480
|
+
if (el.typewriter && !el.typewriter.reversed()) el.typewriter.progress(1)
|
|
1481
|
+
el.typewriter?.kill()
|
|
1482
|
+
})
|
|
1457
1483
|
textSplits.forEach((s) => s.revert())
|
|
1458
1484
|
textSplits.length = 0
|
|
1459
1485
|
onCompleteTweens.forEach((t) => t?.kill())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gclass-anims",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.7",
|
|
4
4
|
"description": "A Tailwind-style utility layer on top of GSAP. Framework-agnostic — works in vanilla JS, React, Vue, Svelte, or any bundler.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|