gclass-anims 1.0.0-beta.12 → 1.0.0-beta.13
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 +42 -3
- package/CHANGELOG.md +3 -0
- package/Config.js +5 -0
- package/Listeners.js +18 -0
- package/package.json +1 -1
package/Animations.js
CHANGED
|
@@ -222,14 +222,43 @@ export function countUp (target , delay , dur, ease){
|
|
|
222
222
|
return gsap.timeline({ delay }).fromTo(obj , { n: start } , { n: end , duration:dur , ease:e , onUpdate: () => { target.textContent = obj.n.toFixed(decimals) } } , 0)
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
// Helpers for the `fill-svg` draw-modifier. When an element carries both
|
|
226
|
+
// `.draw`/`.draw-split` and `.fill-svg`, the stroke is drawn first and then
|
|
227
|
+
// the interior fills. `fill-time-N` / `fill-ease-NAME` override the fill
|
|
228
|
+
// phase; otherwise the fill takes half of `dur` and reuses the draw ease.
|
|
229
|
+
const fillTimeOf = (el , fallback) => {
|
|
230
|
+
const m = [...el.classList].find(c => c.startsWith("fill-time-"))
|
|
231
|
+
return m ? Number(m.slice("fill-time-".length)) : fallback * 0.5
|
|
232
|
+
}
|
|
233
|
+
const fillEaseOf = (el , fallbackEase) => {
|
|
234
|
+
const m = [...el.classList].find(c => c.startsWith("fill-ease-"))
|
|
235
|
+
return m ? m.slice("fill-ease-".length) : fallbackEase
|
|
236
|
+
}
|
|
237
|
+
|
|
225
238
|
// Stroke-draw reveal (strokes only — filled SVGs are deliberately out of
|
|
226
239
|
// scope for now). Explicit fromTo endpoints so the animation's hidden state
|
|
227
240
|
// matches this class's Config `from` metadata exactly: `.scroll-progress`
|
|
228
241
|
// scrubs between those two values and `.leave`/`.scroll` reversal tweens back
|
|
229
|
-
// into them.
|
|
242
|
+
// into them. When the target also carries `.fill-svg`, the interior is filled
|
|
243
|
+
// after the stroke finishes (draw → fill sequential timeline).
|
|
230
244
|
export function drawsvg (target , delay , dur , ease){
|
|
231
245
|
const e = easeOf(ease)
|
|
232
|
-
|
|
246
|
+
const first = gsap.utils.toArray(target)[0]
|
|
247
|
+
const hasFill = !!first?.classList?.contains("fill-svg")
|
|
248
|
+
if (!hasFill) {
|
|
249
|
+
return gsap.fromTo(target , {drawSVG:"0%"} , {ease:e , duration:dur , delay:delay , drawSVG:"100%"})
|
|
250
|
+
}
|
|
251
|
+
const fillDur = fillTimeOf(first , dur)
|
|
252
|
+
const fillEase = fillEaseOf(first , ease)
|
|
253
|
+
const fillTargets = gsap.utils.toArray(target).filter(el => el.classList.contains("fill-svg"))
|
|
254
|
+
const tl = gsap.timeline({ delay })
|
|
255
|
+
// Keep fill invisible while the stroke draws
|
|
256
|
+
if (fillTargets.length) gsap.set(fillTargets , { fillOpacity: 0 })
|
|
257
|
+
tl.fromTo(target , {drawSVG:"0%"} , {ease:e , duration:dur , drawSVG:"100%"})
|
|
258
|
+
if (fillTargets.length) {
|
|
259
|
+
tl.fromTo(fillTargets , {fillOpacity:0} , {ease:easeOf(fillEase) , duration:fillDur , fillOpacity:1})
|
|
260
|
+
}
|
|
261
|
+
return tl
|
|
233
262
|
}
|
|
234
263
|
|
|
235
264
|
// Busts a multi-segment <path> (one containing multiple "M" commands) apart
|
|
@@ -293,9 +322,15 @@ export function splitPaths (paths){
|
|
|
293
322
|
// of `dur` proportional to its own stroke length so the pen travels at a
|
|
294
323
|
// constant speed across the whole drawing. Returns a timeline, so leave /
|
|
295
324
|
// scroll reversal un-draws the segments back-to-front and the engine's
|
|
296
|
-
// onComplete hooks fire only after the final segment lands.
|
|
325
|
+
// onComplete hooks fire only after the final segment lands. When the source
|
|
326
|
+
// also carries `.fill-svg`, every resulting segment is filled together after
|
|
327
|
+
// the last draw segment lands (draw → fill).
|
|
297
328
|
export function drawsvgSplit (target , delay , dur , ease){
|
|
298
329
|
const e = easeOf(ease)
|
|
330
|
+
const first = gsap.utils.toArray(target)[0]
|
|
331
|
+
const hasFill = !!first?.classList?.contains("fill-svg")
|
|
332
|
+
const fillDur = hasFill ? fillTimeOf(first , dur) : 0
|
|
333
|
+
const fillEase = hasFill ? fillEaseOf(first , ease) : ease
|
|
299
334
|
const tl = gsap.timeline({ delay })
|
|
300
335
|
const paths = splitPaths(target)
|
|
301
336
|
let distance = 0
|
|
@@ -303,11 +338,15 @@ export function drawsvgSplit (target , delay , dur , ease){
|
|
|
303
338
|
// Nothing drawable (empty selection / zero-length strokes): hand back the
|
|
304
339
|
// inert timeline rather than divide by zero below.
|
|
305
340
|
if (!distance) return tl
|
|
341
|
+
if (hasFill && paths.length) gsap.set(paths , { fillOpacity: 0 })
|
|
306
342
|
paths.forEach(segment => {
|
|
307
343
|
tl.fromTo(segment ,
|
|
308
344
|
{drawSVG:"0%"} ,
|
|
309
345
|
{ease:e , duration:dur * (segment.getTotalLength() / distance) , drawSVG:"100%"})
|
|
310
346
|
})
|
|
347
|
+
if (hasFill && paths.length) {
|
|
348
|
+
tl.fromTo(paths , {fillOpacity:0} , {ease:easeOf(fillEase) , duration:fillDur , fillOpacity:1})
|
|
349
|
+
}
|
|
311
350
|
return tl
|
|
312
351
|
}
|
|
313
352
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `gclass-anims` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.0-beta.13] - 2026-08-27
|
|
6
|
+
- Added a new `.fill-svg` modifier for the `.draw` and `.draw-split` classes that fills the SVG after it has been drawn.
|
|
7
|
+
|
|
5
8
|
## [1.0.0-beta.12] - 2026-08-26
|
|
6
9
|
|
|
7
10
|
- Fixed `scramble` with `scroll-progress` throwing `can't convert undefined to object` — `computeTo` (`Listeners.js:424`) and scrub `to` builder (`Listeners.js:867`) now guard `from` (`scramble` has no `from`).
|
package/Config.js
CHANGED
|
@@ -142,6 +142,11 @@ export const animations = [
|
|
|
142
142
|
// to. `.draw` animates its target(s) as one stroke; `.draw-split` first
|
|
143
143
|
// splits multi-segment paths (paths with multiple "M" commands) into one
|
|
144
144
|
// <path> per segment and draws them sequentially at constant pen speed.
|
|
145
|
+
// `.fill-svg` is a MODIFIER for both: add it alongside `.draw`/`.draw-split`
|
|
146
|
+
// to fill the interior after the stroke finishes (draw → fill). Tunables:
|
|
147
|
+
// `fill-time-N` (fill duration, default = 0.5× draw time) and
|
|
148
|
+
// `fill-ease-NAME` (default reuses draw ease). Requires a fill color on
|
|
149
|
+
// the element (`fill` attribute or CSS); the modifier animates `fillOpacity`.
|
|
145
150
|
{ sel: ".draw", text: false, from: { drawSVG: "0%" }, play: (el, delay, dur, ease) => drawsvg(el, delay, dur, ease) },
|
|
146
151
|
{ sel: ".draw-split", text: false, from: { drawSVG: "0%" }, play: (el, delay, dur, ease) => drawsvgSplit(el, delay, dur, ease) },
|
|
147
152
|
|
package/Listeners.js
CHANGED
|
@@ -925,6 +925,24 @@ export default function initListeners() {
|
|
|
925
925
|
} else if (typewriterSplit) {
|
|
926
926
|
const parts = getParts(el, getGranularity(el))
|
|
927
927
|
if (parts.length) tl.fromTo(parts, { opacity: reverse ? 1 : 0 }, { opacity: reverse ? 0 : 1, ease })
|
|
928
|
+
} else if (el.classList.contains("fill-svg") && (el.classList.contains("draw") || el.classList.contains("draw-split"))) {
|
|
929
|
+
// fill-svg modifier for draw: stroke first, then fill, sequential scrub.
|
|
930
|
+
// For scrub the two phases are sequential tweens so scroll maps
|
|
931
|
+
// draw → fill. Reverse swaps order so unfill happens before undraw.
|
|
932
|
+
const fillEaseCls = [...el.classList].find(c => c.startsWith("fill-ease-"))
|
|
933
|
+
const fillEase = fillEaseCls ? fillEaseCls.slice("fill-ease-".length) : ease
|
|
934
|
+
const fillTimeCls = [...el.classList].find(c => c.startsWith("fill-time-"))
|
|
935
|
+
const fillDur = fillTimeCls ? Number(fillTimeCls.slice("fill-time-".length)) : 0.5
|
|
936
|
+
const drawDur = 1
|
|
937
|
+
// Ensure the hidden / revealed fill state is correct before scrub starts
|
|
938
|
+
gsap.set(el, { fillOpacity: reverse ? 1 : 0 })
|
|
939
|
+
if (!reverse) {
|
|
940
|
+
tl.fromTo(el, { drawSVG: "0%" , ...rnd }, { drawSVG: "100%" , ...rndEnds, ease, duration: drawDur })
|
|
941
|
+
tl.fromTo(el, { fillOpacity: 0 }, { fillOpacity: 1, ease: fillEase, duration: fillDur })
|
|
942
|
+
} else {
|
|
943
|
+
tl.fromTo(el, { fillOpacity: 1 }, { fillOpacity: 0, ease: fillEase, duration: fillDur })
|
|
944
|
+
tl.fromTo(el, { drawSVG: "100%" , ...rndEnds }, { drawSVG: "0%" , ...rnd, ease, duration: drawDur })
|
|
945
|
+
}
|
|
928
946
|
} else {
|
|
929
947
|
tl.fromTo(el, { ...(reverse ? to : from), ...rnd }, { ...rndEnds, ...(reverse ? from : to), ease })
|
|
930
948
|
}
|
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.13",
|
|
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",
|