fluxo-animation 1.0.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.
@@ -0,0 +1,1248 @@
1
+ "use strict";
2
+ var fluxo = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ ScrollTrigger: () => ScrollTrigger,
25
+ Timeline: () => Timeline,
26
+ Tween: () => Tween,
27
+ drawSVG: () => drawSVG,
28
+ easings: () => easings,
29
+ explodeOnScroll: () => explodeOnScroll,
30
+ fluxo: () => fluxo,
31
+ implodeOnScroll: () => implodeOnScroll,
32
+ magnetic: () => magnetic,
33
+ resolveTargets: () => resolveTargets,
34
+ revealText: () => revealText,
35
+ scrollReveal: () => scrollReveal,
36
+ slidingMenu: () => slidingMenu,
37
+ splitText: () => splitText,
38
+ ticker: () => ticker,
39
+ tilt: () => tilt
40
+ });
41
+
42
+ // src/ticker.ts
43
+ var Ticker = class {
44
+ callbacks = /* @__PURE__ */ new Set();
45
+ rafId = null;
46
+ lastTime = 0;
47
+ time = 0;
48
+ constructor() {
49
+ this.tick = this.tick.bind(this);
50
+ }
51
+ add(cb) {
52
+ this.callbacks.add(cb);
53
+ if (this.rafId === null) {
54
+ this.start();
55
+ }
56
+ }
57
+ remove(cb) {
58
+ this.callbacks.delete(cb);
59
+ if (this.callbacks.size === 0 && this.rafId !== null) {
60
+ this.stop();
61
+ }
62
+ }
63
+ start() {
64
+ this.lastTime = performance.now();
65
+ this.rafId = requestAnimationFrame(this.tick);
66
+ }
67
+ stop() {
68
+ if (this.rafId !== null) {
69
+ cancelAnimationFrame(this.rafId);
70
+ this.rafId = null;
71
+ }
72
+ }
73
+ tick(now) {
74
+ const dt = (now - this.lastTime) / 1e3;
75
+ this.lastTime = now;
76
+ this.time += dt;
77
+ const activeCallbacks = Array.from(this.callbacks);
78
+ for (const cb of activeCallbacks) {
79
+ cb(this.time, dt);
80
+ }
81
+ if (this.callbacks.size > 0) {
82
+ this.rafId = requestAnimationFrame(this.tick);
83
+ } else {
84
+ this.rafId = null;
85
+ }
86
+ }
87
+ };
88
+ var ticker = new Ticker();
89
+
90
+ // src/easings.ts
91
+ var easings = {
92
+ none: (t) => t,
93
+ linear: (t) => t,
94
+ "slow.in": (t) => t * t,
95
+ "slow.out": (t) => t * (2 - t),
96
+ "slow.inOut": (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,
97
+ "medium.in": (t) => t * t * t,
98
+ "medium.out": (t) => 1 - Math.pow(1 - t, 3),
99
+ "medium.inOut": (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
100
+ "fast.in": (t) => t * t * t * t,
101
+ "fast.out": (t) => 1 - Math.pow(1 - t, 4),
102
+ "fast.inOut": (t) => t < 0.5 ? 8 * t * t * t * t : 1 - Math.pow(-2 * t + 2, 4) / 2,
103
+ "veryFast.in": (t) => t * t * t * t * t,
104
+ "veryFast.out": (t) => 1 - Math.pow(1 - t, 5),
105
+ "veryFast.inOut": (t) => t < 0.5 ? 16 * t * t * t * t * t : 1 - Math.pow(-2 * t + 2, 5) / 2
106
+ };
107
+ function getEasing(ease) {
108
+ if (!ease) return easings["slow.out"];
109
+ if (typeof ease === "function") return ease;
110
+ return easings[ease] || easings["slow.out"];
111
+ }
112
+
113
+ // src/utils.ts
114
+ function resolveTargets(target) {
115
+ if (!target) return [];
116
+ if (typeof target === "string") {
117
+ if (typeof document !== "undefined") {
118
+ return Array.from(document.querySelectorAll(target));
119
+ }
120
+ return [];
121
+ }
122
+ if (Array.isArray(target)) {
123
+ return target;
124
+ }
125
+ if (typeof NodeList !== "undefined" && target instanceof NodeList) {
126
+ return Array.from(target);
127
+ }
128
+ if (typeof HTMLCollection !== "undefined" && target instanceof HTMLCollection) {
129
+ return Array.from(target);
130
+ }
131
+ return [target];
132
+ }
133
+ function splitText(target, options = {}) {
134
+ const targets = resolveTargets(target);
135
+ const type = options.type || "chars";
136
+ const resultSpans = [];
137
+ targets.forEach((el) => {
138
+ if (!(el instanceof HTMLElement)) return;
139
+ const originalText = el.textContent || "";
140
+ el.innerHTML = "";
141
+ if (type === "chars") {
142
+ const chars = originalText.split("");
143
+ chars.forEach((char) => {
144
+ if (char === " ") {
145
+ el.appendChild(document.createTextNode(" "));
146
+ } else {
147
+ const span = document.createElement("span");
148
+ span.className = "fluxo-char";
149
+ span.style.display = "inline-block";
150
+ span.textContent = char;
151
+ el.appendChild(span);
152
+ resultSpans.push(span);
153
+ }
154
+ });
155
+ } else {
156
+ const words = originalText.split(" ");
157
+ words.forEach((word, index) => {
158
+ if (index > 0) {
159
+ el.appendChild(document.createTextNode(" "));
160
+ }
161
+ const span = document.createElement("span");
162
+ span.className = "fluxo-word";
163
+ span.style.display = "inline-block";
164
+ span.textContent = word;
165
+ el.appendChild(span);
166
+ resultSpans.push(span);
167
+ });
168
+ }
169
+ });
170
+ return resultSpans;
171
+ }
172
+ function drawSVG(target, vars) {
173
+ const targets = resolveTargets(target);
174
+ const path = targets.length > 0 ? targets[0] : null;
175
+ if (!path || typeof path.getTotalLength !== "function") {
176
+ throw new Error(
177
+ "drawSVG: Target must be a valid SVG element with getTotalLength (such as <path>)"
178
+ );
179
+ }
180
+ const length = path.getTotalLength();
181
+ path.style.strokeDasharray = String(length);
182
+ path.style.strokeDashoffset = String(length);
183
+ return new Tween(path, {
184
+ strokeDashoffset: 0,
185
+ ...vars
186
+ });
187
+ }
188
+
189
+ // src/tween.ts
190
+ var TRANSFORM_KEYS = /* @__PURE__ */ new Set(["x", "y", "rotation", "scale"]);
191
+ var RESERVED_KEYS = /* @__PURE__ */ new Set([
192
+ "duration",
193
+ "delay",
194
+ "ease",
195
+ "autoPlay",
196
+ "repeat",
197
+ "alternate",
198
+ "onStart",
199
+ "onUpdate",
200
+ "onComplete",
201
+ "onRepeat"
202
+ ]);
203
+ var Tween = class {
204
+ target;
205
+ vars;
206
+ duration;
207
+ delay;
208
+ ease;
209
+ fromVars;
210
+ isFrom = false;
211
+ playhead = 0;
212
+ reversed = false;
213
+ started = false;
214
+ completed = false;
215
+ repeatCount = 0;
216
+ propTweens = [];
217
+ constructor(target, vars, fromVars, isFrom = false) {
218
+ const resolved = resolveTargets(target);
219
+ this.target = resolved.length > 0 ? resolved[0] : null;
220
+ this.vars = vars;
221
+ this.fromVars = fromVars;
222
+ this.isFrom = isFrom;
223
+ this.duration = vars.duration !== void 0 ? vars.duration : 0.5;
224
+ this.delay = vars.delay !== void 0 ? vars.delay : 0;
225
+ this.ease = getEasing(vars.ease);
226
+ this.update = this.update.bind(this);
227
+ const autoPlay = vars.autoPlay !== false;
228
+ if (this.target) {
229
+ if (this.isFrom || this.fromVars) {
230
+ this.initProperties();
231
+ this.started = true;
232
+ }
233
+ if (autoPlay) {
234
+ ticker.add(this.update);
235
+ }
236
+ }
237
+ }
238
+ initProperties() {
239
+ this.propTweens = [];
240
+ const isDOM = this.target instanceof HTMLElement || typeof SVGElement !== "undefined" && this.target instanceof SVGElement;
241
+ let transformState = null;
242
+ if (isDOM) {
243
+ transformState = this.getTransformState(this.target);
244
+ }
245
+ for (const key in this.vars) {
246
+ if (RESERVED_KEYS.has(key)) continue;
247
+ const endValueRaw = this.vars[key];
248
+ const parsedEnd = this.parseValue(endValueRaw);
249
+ let currentVal = 0;
250
+ let unit = parsedEnd.unit;
251
+ const isTransform = isDOM && TRANSFORM_KEYS.has(key);
252
+ if (isTransform && transformState) {
253
+ currentVal = transformState[key];
254
+ } else if (isDOM) {
255
+ const computedStyle = window.getComputedStyle(this.target);
256
+ const styleVal = computedStyle[key] || this.target.style[key];
257
+ const parsedStart = this.parseValue(styleVal);
258
+ currentVal = parsedStart.value;
259
+ if (unit === "" && parsedStart.unit !== "") {
260
+ unit = parsedStart.unit;
261
+ }
262
+ } else {
263
+ currentVal = typeof this.target[key] === "number" ? this.target[key] : 0;
264
+ }
265
+ let startValue = 0;
266
+ let endValue = 0;
267
+ if (this.fromVars) {
268
+ const parsedFrom = this.parseValue(this.fromVars[key]);
269
+ startValue = parsedFrom.value;
270
+ endValue = parsedEnd.value;
271
+ unit = parsedEnd.unit || parsedFrom.unit || unit;
272
+ } else if (this.isFrom) {
273
+ startValue = parsedEnd.value;
274
+ endValue = currentVal;
275
+ } else {
276
+ startValue = currentVal;
277
+ endValue = parsedEnd.value;
278
+ }
279
+ this.propTweens.push({
280
+ key,
281
+ isTransform,
282
+ start: startValue,
283
+ end: endValue,
284
+ unit
285
+ });
286
+ if (this.isFrom || this.fromVars) {
287
+ if (isTransform && transformState) {
288
+ transformState[key] = startValue;
289
+ } else if (isDOM) {
290
+ this.target.style[key] = startValue + unit;
291
+ } else {
292
+ this.target[key] = startValue;
293
+ }
294
+ }
295
+ }
296
+ if ((this.isFrom || this.fromVars) && transformState && isDOM) {
297
+ this.applyTransform(this.target, transformState);
298
+ }
299
+ }
300
+ parseValue(val) {
301
+ if (typeof val === "number") {
302
+ return { value: val, unit: "" };
303
+ }
304
+ const num = parseFloat(val);
305
+ if (isNaN(num)) {
306
+ return { value: 0, unit: "" };
307
+ }
308
+ const unit = String(val).replace(/^[-\d.]+/, "");
309
+ return { value: num, unit };
310
+ }
311
+ getTransformState(el) {
312
+ if (!el._fluxoTransform) {
313
+ el._fluxoTransform = { x: 0, y: 0, rotation: 0, scale: 1 };
314
+ }
315
+ return el._fluxoTransform;
316
+ }
317
+ applyTransform(el, state) {
318
+ el.style.transform = `translate3d(${state.x}px, ${state.y}px, 0px) rotate(${state.rotation}deg) scale(${state.scale})`;
319
+ }
320
+ play() {
321
+ this.reversed = false;
322
+ this.completed = false;
323
+ const maxTime = this.duration + this.delay;
324
+ if (this.playhead >= maxTime) {
325
+ this.playhead = 0;
326
+ this.repeatCount = 0;
327
+ }
328
+ ticker.add(this.update);
329
+ }
330
+ reverse() {
331
+ this.reversed = true;
332
+ this.completed = false;
333
+ if (this.playhead <= 0) {
334
+ this.playhead = this.duration + this.delay;
335
+ this.repeatCount = 0;
336
+ }
337
+ ticker.add(this.update);
338
+ }
339
+ pause() {
340
+ ticker.remove(this.update);
341
+ }
342
+ render(time) {
343
+ let progress = 0;
344
+ if (time >= this.delay) {
345
+ const activeTime = time - this.delay;
346
+ progress = this.duration > 0 ? activeTime / this.duration : 1;
347
+ if (progress > 1) progress = 1;
348
+ }
349
+ if (!this.started && time >= this.delay) {
350
+ this.started = true;
351
+ this.initProperties();
352
+ if (this.vars.onStart) {
353
+ this.vars.onStart();
354
+ }
355
+ }
356
+ const easedProgress = this.ease(progress);
357
+ let hasTransform = false;
358
+ const transformState = this.target instanceof HTMLElement || typeof SVGElement !== "undefined" && this.target instanceof SVGElement ? this.getTransformState(this.target) : null;
359
+ for (const pt of this.propTweens) {
360
+ const currentVal = pt.start + (pt.end - pt.start) * easedProgress;
361
+ if (pt.isTransform && transformState) {
362
+ transformState[pt.key] = currentVal;
363
+ hasTransform = true;
364
+ } else if (this.target.style !== void 0) {
365
+ this.target.style[pt.key] = currentVal + pt.unit;
366
+ } else {
367
+ this.target[pt.key] = currentVal;
368
+ }
369
+ }
370
+ if (hasTransform && transformState) {
371
+ this.applyTransform(this.target, transformState);
372
+ }
373
+ if (this.vars.onUpdate) {
374
+ this.vars.onUpdate();
375
+ }
376
+ }
377
+ update(totalTime, dt) {
378
+ if (this.completed) return;
379
+ this.playhead += this.reversed ? -dt : dt;
380
+ const maxTime = this.duration + this.delay;
381
+ const repeatOption = this.vars.repeat !== void 0 ? this.vars.repeat : 0;
382
+ const alternateOption = this.vars.alternate === true;
383
+ if (this.reversed) {
384
+ if (this.playhead <= this.delay) {
385
+ if (repeatOption === -1 || this.repeatCount < repeatOption) {
386
+ this.repeatCount++;
387
+ if (this.vars.onRepeat) {
388
+ this.vars.onRepeat();
389
+ }
390
+ if (alternateOption) {
391
+ this.reversed = false;
392
+ this.playhead = this.delay;
393
+ } else {
394
+ this.playhead = maxTime;
395
+ }
396
+ } else {
397
+ this.playhead = 0;
398
+ this.completed = true;
399
+ }
400
+ }
401
+ } else {
402
+ if (this.playhead >= maxTime) {
403
+ if (repeatOption === -1 || this.repeatCount < repeatOption) {
404
+ this.repeatCount++;
405
+ if (this.vars.onRepeat) {
406
+ this.vars.onRepeat();
407
+ }
408
+ if (alternateOption) {
409
+ this.reversed = true;
410
+ this.playhead = maxTime;
411
+ } else {
412
+ this.playhead = this.delay;
413
+ }
414
+ } else {
415
+ this.playhead = maxTime;
416
+ this.completed = true;
417
+ }
418
+ }
419
+ }
420
+ this.render(this.playhead);
421
+ if (this.completed) {
422
+ ticker.remove(this.update);
423
+ if (this.vars.onComplete) {
424
+ this.vars.onComplete();
425
+ }
426
+ }
427
+ }
428
+ kill() {
429
+ ticker.remove(this.update);
430
+ }
431
+ };
432
+
433
+ // src/timeline.ts
434
+ var Timeline = class {
435
+ children = [];
436
+ vars;
437
+ duration = 0;
438
+ delay;
439
+ playhead = 0;
440
+ reversed = false;
441
+ started = false;
442
+ completed = false;
443
+ isPlaying = false;
444
+ repeatCount = 0;
445
+ constructor(vars = {}) {
446
+ this.vars = vars;
447
+ this.delay = vars.delay !== void 0 ? vars.delay : 0;
448
+ this.update = this.update.bind(this);
449
+ if (!vars.paused) {
450
+ this.play();
451
+ }
452
+ }
453
+ /**
454
+ * Adds an existing, externally created Tween instance to this timeline.
455
+ * Useful for inserting custom tweens like fluxo.drawSVG().
456
+ *
457
+ * @param tween The Tween instance to add.
458
+ * @param position Optional position key (number, relative offset like "+=0.5", or alignment like "<").
459
+ */
460
+ add(tween, position) {
461
+ tween.pause();
462
+ const baseTime = this.parsePosition(position);
463
+ this.addTween(tween, baseTime);
464
+ return this;
465
+ }
466
+ /**
467
+ * Adds .to() tweens to the timeline.
468
+ */
469
+ to(target, vars, position) {
470
+ const targets = resolveTargets(target);
471
+ const baseTime = this.parsePosition(position);
472
+ const stagger = vars.stagger || 0;
473
+ const tweenVars = { ...vars };
474
+ delete tweenVars.stagger;
475
+ targets.forEach((t, i) => {
476
+ const tween = new Tween(t, { ...tweenVars, autoPlay: false });
477
+ this.addTween(tween, baseTime + i * stagger);
478
+ });
479
+ return this;
480
+ }
481
+ /**
482
+ * Adds .from() tweens to the timeline.
483
+ */
484
+ from(target, vars, position) {
485
+ const targets = resolveTargets(target);
486
+ const baseTime = this.parsePosition(position);
487
+ const stagger = vars.stagger || 0;
488
+ const tweenVars = { ...vars };
489
+ delete tweenVars.stagger;
490
+ targets.forEach((t, i) => {
491
+ const tween = new Tween(
492
+ t,
493
+ { ...tweenVars, autoPlay: false },
494
+ void 0,
495
+ true
496
+ );
497
+ this.addTween(tween, baseTime + i * stagger);
498
+ });
499
+ return this;
500
+ }
501
+ /**
502
+ * Adds .fromTo() tweens to the timeline.
503
+ */
504
+ fromTo(target, fromVars, toVars, position) {
505
+ const targets = resolveTargets(target);
506
+ const baseTime = this.parsePosition(position);
507
+ const stagger = toVars.stagger || 0;
508
+ const tweenVars = { ...toVars };
509
+ delete tweenVars.stagger;
510
+ targets.forEach((t, i) => {
511
+ const tween = new Tween(t, { ...tweenVars, autoPlay: false }, fromVars);
512
+ this.addTween(tween, baseTime + i * stagger);
513
+ });
514
+ return this;
515
+ }
516
+ play() {
517
+ this.reversed = false;
518
+ this.completed = false;
519
+ const maxTime = this.duration + this.delay;
520
+ if (this.playhead >= maxTime) {
521
+ this.playhead = 0;
522
+ this.repeatCount = 0;
523
+ }
524
+ this.isPlaying = true;
525
+ ticker.add(this.update);
526
+ }
527
+ reverse() {
528
+ this.reversed = true;
529
+ this.completed = false;
530
+ if (this.playhead <= 0) {
531
+ this.playhead = this.duration + this.delay;
532
+ this.repeatCount = 0;
533
+ }
534
+ this.isPlaying = true;
535
+ ticker.add(this.update);
536
+ }
537
+ pause() {
538
+ if (!this.isPlaying) return;
539
+ this.isPlaying = false;
540
+ ticker.remove(this.update);
541
+ }
542
+ kill() {
543
+ this.isPlaying = false;
544
+ ticker.remove(this.update);
545
+ for (const child of this.children) {
546
+ child.tween.kill();
547
+ }
548
+ }
549
+ parsePosition(position) {
550
+ let startTime = this.duration;
551
+ const prevChild = this.children[this.children.length - 1];
552
+ if (position !== void 0) {
553
+ if (typeof position === "number") {
554
+ startTime = position;
555
+ } else if (typeof position === "string") {
556
+ if (position.startsWith("+=")) {
557
+ const val = parseFloat(position.slice(2));
558
+ startTime = this.duration + (isNaN(val) ? 0 : val);
559
+ } else if (position.startsWith("-=")) {
560
+ const val = parseFloat(position.slice(2));
561
+ startTime = this.duration - (isNaN(val) ? 0 : val);
562
+ } else if (position === "<") {
563
+ startTime = prevChild ? prevChild.startTime : 0;
564
+ } else if (position.startsWith("<")) {
565
+ const offsetStr = position.slice(1);
566
+ const baseTime = prevChild ? prevChild.startTime : 0;
567
+ if (offsetStr.startsWith("+=")) {
568
+ const val = parseFloat(offsetStr.slice(2));
569
+ startTime = baseTime + (isNaN(val) ? 0 : val);
570
+ } else if (offsetStr.startsWith("-=")) {
571
+ const val = parseFloat(offsetStr.slice(2));
572
+ startTime = baseTime - (isNaN(val) ? 0 : val);
573
+ }
574
+ }
575
+ }
576
+ }
577
+ return startTime;
578
+ }
579
+ addTween(tween, startTime) {
580
+ const tweenDuration = tween.duration + tween.delay;
581
+ const endTime = startTime + tweenDuration;
582
+ this.children.push({
583
+ tween,
584
+ startTime,
585
+ endTime
586
+ });
587
+ this.duration = Math.max(this.duration, endTime);
588
+ }
589
+ render(time) {
590
+ let activeTime = 0;
591
+ if (time >= this.delay) {
592
+ activeTime = time - this.delay;
593
+ }
594
+ if (!this.started && time >= this.delay) {
595
+ this.started = true;
596
+ if (this.vars.onStart) {
597
+ this.vars.onStart();
598
+ }
599
+ }
600
+ for (const child of this.children) {
601
+ const localTime = activeTime - child.startTime;
602
+ child.tween.render(localTime);
603
+ }
604
+ if (this.vars.onUpdate) {
605
+ this.vars.onUpdate();
606
+ }
607
+ }
608
+ update(totalTime, dt) {
609
+ if (this.completed) return;
610
+ this.playhead += this.reversed ? -dt : dt;
611
+ const maxTime = this.duration + this.delay;
612
+ const repeatOption = this.vars.repeat !== void 0 ? this.vars.repeat : 0;
613
+ const alternateOption = this.vars.alternate === true;
614
+ if (this.reversed) {
615
+ if (this.playhead <= this.delay) {
616
+ if (repeatOption === -1 || this.repeatCount < repeatOption) {
617
+ this.repeatCount++;
618
+ if (this.vars.onRepeat) {
619
+ this.vars.onRepeat();
620
+ }
621
+ if (alternateOption) {
622
+ this.reversed = false;
623
+ this.playhead = this.delay;
624
+ } else {
625
+ this.playhead = maxTime;
626
+ }
627
+ } else {
628
+ this.playhead = 0;
629
+ this.completed = true;
630
+ }
631
+ }
632
+ } else {
633
+ if (this.playhead >= maxTime) {
634
+ if (repeatOption === -1 || this.repeatCount < repeatOption) {
635
+ this.repeatCount++;
636
+ if (this.vars.onRepeat) {
637
+ this.vars.onRepeat();
638
+ }
639
+ if (alternateOption) {
640
+ this.reversed = true;
641
+ this.playhead = maxTime;
642
+ } else {
643
+ this.playhead = this.delay;
644
+ }
645
+ } else {
646
+ this.playhead = maxTime;
647
+ this.completed = true;
648
+ }
649
+ }
650
+ }
651
+ this.render(this.playhead);
652
+ if (this.completed) {
653
+ this.isPlaying = false;
654
+ ticker.remove(this.update);
655
+ if (this.vars.onComplete) {
656
+ this.vars.onComplete();
657
+ }
658
+ }
659
+ }
660
+ };
661
+
662
+ // src/scrollTrigger.ts
663
+ var ScrollTrigger = class _ScrollTrigger {
664
+ static instances = [];
665
+ animation;
666
+ triggerEl;
667
+ vars;
668
+ startScroll = 0;
669
+ endScroll = 0;
670
+ hasTriggered = false;
671
+ constructor(animation, vars) {
672
+ this.animation = animation;
673
+ this.vars = vars;
674
+ _ScrollTrigger.instances.push(this);
675
+ const resolved = resolveTargets(vars.trigger);
676
+ if (resolved.length === 0) {
677
+ throw new Error(
678
+ `ScrollTrigger: Target trigger element not found for "${vars.trigger}"`
679
+ );
680
+ }
681
+ this.triggerEl = resolved[0];
682
+ this.onScroll = this.onScroll.bind(this);
683
+ this.refresh = this.refresh.bind(this);
684
+ this.refresh();
685
+ window.addEventListener("scroll", this.onScroll, { passive: true });
686
+ window.addEventListener("resize", this.refresh, { passive: true });
687
+ this.onScroll();
688
+ }
689
+ refresh() {
690
+ const rect = this.triggerEl.getBoundingClientRect();
691
+ const docScrollY = window.scrollY;
692
+ const elTop = rect.top + docScrollY;
693
+ const elHeight = rect.height;
694
+ const startStr = this.vars.start || "top bottom";
695
+ this.startScroll = this.calculateScrollPos(
696
+ startStr,
697
+ elTop,
698
+ elHeight,
699
+ "top",
700
+ "bottom"
701
+ );
702
+ const endStr = this.vars.end || "bottom top";
703
+ this.endScroll = this.calculateScrollPos(
704
+ endStr,
705
+ elTop,
706
+ elHeight,
707
+ "bottom",
708
+ "top"
709
+ );
710
+ if (this.endScroll <= this.startScroll) {
711
+ this.endScroll = this.startScroll + 1;
712
+ }
713
+ }
714
+ calculateScrollPos(posStr, elTop, elHeight, defaultElPart, defaultVPart) {
715
+ const parts = posStr.split(" ");
716
+ const elPart = parts[0] || defaultElPart;
717
+ const vPart = parts[1] || defaultVPart;
718
+ let elOffset = 0;
719
+ if (elPart === "top") elOffset = 0;
720
+ else if (elPart === "center") elOffset = elHeight / 2;
721
+ else if (elPart === "bottom") elOffset = elHeight;
722
+ else if (elPart.endsWith("%")) {
723
+ elOffset = parseFloat(elPart) / 100 * elHeight;
724
+ } else {
725
+ elOffset = parseFloat(elPart) || 0;
726
+ }
727
+ let vOffset = 0;
728
+ const vh = window.innerHeight;
729
+ if (vPart === "top") vOffset = 0;
730
+ else if (vPart === "center") vOffset = vh / 2;
731
+ else if (vPart === "bottom") vOffset = vh;
732
+ else if (vPart.endsWith("%")) {
733
+ vOffset = parseFloat(vPart) / 100 * vh;
734
+ } else {
735
+ vOffset = parseFloat(vPart) || 0;
736
+ }
737
+ return elTop + elOffset - vOffset;
738
+ }
739
+ onScroll() {
740
+ const scrollY = window.scrollY;
741
+ if (this.vars.scrub) {
742
+ let progress = (scrollY - this.startScroll) / (this.endScroll - this.startScroll);
743
+ progress = Math.max(0, Math.min(1, progress));
744
+ const totalDuration = this.animation.duration + this.animation.delay;
745
+ this.animation.render(progress * totalDuration);
746
+ } else {
747
+ if (scrollY >= this.startScroll) {
748
+ if (!this.hasTriggered) {
749
+ this.hasTriggered = true;
750
+ this.animation.play();
751
+ if (this.vars.once) {
752
+ this.kill();
753
+ }
754
+ }
755
+ } else {
756
+ if (!this.vars.once && this.hasTriggered) {
757
+ this.hasTriggered = false;
758
+ this.animation.pause();
759
+ this.animation.render(0);
760
+ }
761
+ }
762
+ }
763
+ }
764
+ kill() {
765
+ window.removeEventListener("scroll", this.onScroll);
766
+ window.removeEventListener("resize", this.refresh);
767
+ const idx = _ScrollTrigger.instances.indexOf(this);
768
+ if (idx !== -1) {
769
+ _ScrollTrigger.instances.splice(idx, 1);
770
+ }
771
+ }
772
+ static killAll() {
773
+ const list = [..._ScrollTrigger.instances];
774
+ list.forEach((instance) => instance.kill());
775
+ _ScrollTrigger.instances = [];
776
+ }
777
+ };
778
+
779
+ // src/effects.ts
780
+ function slidingMenu(containerSelector, linksSelector, pillSelector, options = {}) {
781
+ const container = resolveTargets(containerSelector)[0];
782
+ const pill = resolveTargets(pillSelector)[0];
783
+ const links = container ? Array.from(container.querySelectorAll(linksSelector)) : [];
784
+ if (!container || !pill || links.length === 0) return;
785
+ const duration = options.duration !== void 0 ? options.duration : 0.35;
786
+ const ease = options.ease || "veryFast.out";
787
+ const movePill = (linkEl) => {
788
+ const rect = linkEl.getBoundingClientRect();
789
+ const parentRect = container.getBoundingClientRect();
790
+ const targetLeft = rect.left - parentRect.left;
791
+ const targetWidth = rect.width;
792
+ const targetTop = rect.top - parentRect.top;
793
+ const targetHeight = rect.height;
794
+ new Tween(pill, {
795
+ left: targetLeft,
796
+ width: targetWidth,
797
+ top: targetTop,
798
+ height: targetHeight,
799
+ opacity: 1,
800
+ duration,
801
+ ease,
802
+ autoPlay: true
803
+ });
804
+ };
805
+ links.forEach((link) => {
806
+ if (!(link instanceof HTMLElement)) return;
807
+ link.addEventListener("mouseenter", () => movePill(link), {
808
+ passive: true
809
+ });
810
+ });
811
+ container.addEventListener(
812
+ "mouseleave",
813
+ () => {
814
+ new Tween(pill, {
815
+ opacity: 0,
816
+ duration: 0.3,
817
+ ease: "medium.out",
818
+ autoPlay: true
819
+ });
820
+ },
821
+ { passive: true }
822
+ );
823
+ }
824
+ function magnetic(target, options = {}) {
825
+ const resolved = resolveTargets(target);
826
+ const strength = options.strength !== void 0 ? options.strength : 0.45;
827
+ const proximity = options.proximity !== void 0 ? options.proximity : 100;
828
+ const duration = options.duration !== void 0 ? options.duration : 0.3;
829
+ const ease = options.ease || "veryFast.out";
830
+ resolved.forEach((el) => {
831
+ if (!(el instanceof HTMLElement)) return;
832
+ let isInside = false;
833
+ const onMouseMove = (e) => {
834
+ const rect = el.getBoundingClientRect();
835
+ const centerX = rect.left + rect.width / 2;
836
+ const centerY = rect.top + rect.height / 2;
837
+ const deltaX = e.clientX - centerX;
838
+ const deltaY = e.clientY - centerY;
839
+ const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
840
+ if (distance < proximity) {
841
+ isInside = true;
842
+ new Tween(el, {
843
+ x: deltaX * strength,
844
+ y: deltaY * strength,
845
+ duration,
846
+ ease,
847
+ autoPlay: true
848
+ });
849
+ } else if (isInside) {
850
+ isInside = false;
851
+ new Tween(el, {
852
+ x: 0,
853
+ y: 0,
854
+ duration: 0.6,
855
+ ease: "medium.out",
856
+ autoPlay: true
857
+ });
858
+ }
859
+ };
860
+ window.addEventListener("mousemove", onMouseMove, { passive: true });
861
+ });
862
+ }
863
+ function tilt(target, options = {}) {
864
+ const resolved = resolveTargets(target);
865
+ const maxTilt = options.maxTilt !== void 0 ? options.maxTilt : 25;
866
+ const perspective = options.perspective !== void 0 ? options.perspective : 800;
867
+ const duration = options.duration !== void 0 ? options.duration : 0.2;
868
+ const ease = options.ease || "veryFast.out";
869
+ resolved.forEach((el) => {
870
+ if (!(el instanceof HTMLElement)) return;
871
+ const onMouseMove = (e) => {
872
+ const rect = el.getBoundingClientRect();
873
+ const mouseX = e.clientX - rect.left;
874
+ const mouseY = e.clientY - rect.top;
875
+ const pctX = mouseX / rect.width * 100;
876
+ const pctY = mouseY / rect.height * 100;
877
+ el.style.setProperty("--mouse-x", `${pctX}%`);
878
+ el.style.setProperty("--mouse-y", `${pctY}%`);
879
+ const tiltX = (mouseY / rect.height - 0.5) * -maxTilt;
880
+ const tiltY = (mouseX / rect.width - 0.5) * maxTilt;
881
+ new Tween(el, {
882
+ transform: `perspective(${perspective}px) rotateX(${tiltX}deg) rotateY(${tiltY}deg)`,
883
+ duration,
884
+ ease,
885
+ autoPlay: true
886
+ });
887
+ };
888
+ const onMouseLeave = () => {
889
+ new Tween(el, {
890
+ transform: `perspective(${perspective}px) rotateX(0deg) rotateY(0deg)`,
891
+ duration: 0.6,
892
+ ease: "medium.out",
893
+ autoPlay: true
894
+ });
895
+ el.style.setProperty("--mouse-x", "50%");
896
+ el.style.setProperty("--mouse-y", "50%");
897
+ };
898
+ el.addEventListener("mousemove", onMouseMove, { passive: true });
899
+ el.addEventListener("mouseleave", onMouseLeave, { passive: true });
900
+ });
901
+ }
902
+ function explodeOnScroll(target, options = {}) {
903
+ const resolved = resolveTargets(target);
904
+ const strength = options.strength !== void 0 ? options.strength : 1;
905
+ const start = options.start || "top 70%";
906
+ const end = options.end || "top 20%";
907
+ const ease = options.ease || "medium.out";
908
+ resolved.forEach((el) => {
909
+ const chars = splitText(el, { type: "chars" });
910
+ const tl = new Timeline({ paused: true });
911
+ chars.forEach((char) => {
912
+ const targetX = (Math.random() - 0.5) * 600 * strength;
913
+ const targetY = (Math.random() - 0.5) * 500 * strength;
914
+ const targetRot = (Math.random() - 0.5) * 360;
915
+ const targetScale = 0.1 + Math.random() * 2.2;
916
+ const tween = new Tween(char, {
917
+ x: targetX,
918
+ y: targetY,
919
+ rotation: targetRot,
920
+ scale: targetScale,
921
+ opacity: 0,
922
+ duration: 1,
923
+ ease,
924
+ autoPlay: false
925
+ });
926
+ tl.add(tween, 0);
927
+ });
928
+ new ScrollTrigger(tl, {
929
+ trigger: el,
930
+ start,
931
+ end,
932
+ scrub: true
933
+ });
934
+ });
935
+ }
936
+ function implodeOnScroll(target, options = {}) {
937
+ const resolved = resolveTargets(target);
938
+ const strength = options.strength !== void 0 ? options.strength : 1;
939
+ const start = options.start || "top 95%";
940
+ const end = options.end || "top 50%";
941
+ const ease = options.ease || "medium.out";
942
+ resolved.forEach((el) => {
943
+ const chars = splitText(el, { type: "chars" });
944
+ const tl = new Timeline({ paused: true });
945
+ chars.forEach((char) => {
946
+ const startX = (Math.random() - 0.5) * 700 * strength;
947
+ const startY = (Math.random() - 0.5) * 600 * strength;
948
+ const startRot = (Math.random() - 0.5) * 360;
949
+ const startScale = 0.1 + Math.random() * 2.5;
950
+ const tween = new Tween(
951
+ char,
952
+ {
953
+ x: 0,
954
+ y: 0,
955
+ rotation: 0,
956
+ scale: 1,
957
+ opacity: 1,
958
+ duration: 1,
959
+ ease,
960
+ autoPlay: false
961
+ },
962
+ {
963
+ x: startX,
964
+ y: startY,
965
+ rotation: startRot,
966
+ scale: startScale,
967
+ opacity: 0
968
+ }
969
+ );
970
+ tl.add(tween, 0);
971
+ });
972
+ new ScrollTrigger(tl, {
973
+ trigger: el,
974
+ start,
975
+ end,
976
+ scrub: true
977
+ });
978
+ });
979
+ }
980
+ function revealText(target, options = {}) {
981
+ const type = options.type || "chars";
982
+ const stagger = options.stagger !== void 0 ? options.stagger : type === "chars" ? 0.03 : 0.12;
983
+ const duration = options.duration !== void 0 ? options.duration : 0.8;
984
+ const ease = options.ease || "veryFast.out";
985
+ const delay = options.delay !== void 0 ? options.delay : 0;
986
+ const resolved = resolveTargets(target);
987
+ resolved.forEach((el) => {
988
+ const spans = splitText(el, { type });
989
+ const tl = new Timeline({ delay, paused: true });
990
+ spans.forEach((span, i) => {
991
+ const tween = new Tween(
992
+ span,
993
+ {
994
+ opacity: 1,
995
+ y: 0,
996
+ scale: 1,
997
+ rotation: 0,
998
+ duration,
999
+ ease,
1000
+ autoPlay: false
1001
+ },
1002
+ {
1003
+ opacity: 0,
1004
+ y: 40,
1005
+ scale: 0.5,
1006
+ rotation: 15
1007
+ }
1008
+ );
1009
+ tl.add(tween, i * stagger);
1010
+ });
1011
+ tl.play();
1012
+ });
1013
+ }
1014
+ function scrollReveal(target, options = {}) {
1015
+ const resolved = resolveTargets(target);
1016
+ const stagger = options.stagger !== void 0 ? options.stagger : 0.1;
1017
+ const y = options.y !== void 0 ? options.y : 50;
1018
+ const duration = options.duration !== void 0 ? options.duration : 0.8;
1019
+ const ease = options.ease || "medium.out";
1020
+ const start = options.start || "top 85%";
1021
+ const once = options.once !== false;
1022
+ if (resolved.length === 0) return;
1023
+ const tl = new Timeline({ paused: true });
1024
+ resolved.forEach((el, i) => {
1025
+ const tween = new Tween(
1026
+ el,
1027
+ {
1028
+ opacity: 1,
1029
+ y: 0,
1030
+ duration,
1031
+ ease,
1032
+ autoPlay: false
1033
+ },
1034
+ {
1035
+ opacity: 0,
1036
+ y
1037
+ }
1038
+ );
1039
+ tl.add(tween, i * stagger);
1040
+ });
1041
+ new ScrollTrigger(tl, {
1042
+ trigger: resolved[0],
1043
+ start,
1044
+ once
1045
+ });
1046
+ }
1047
+
1048
+ // src/index.ts
1049
+ var fluxo = {
1050
+ /**
1051
+ * Creates an animation that goes FROM the current values of the target
1052
+ * TO the values defined in 'vars'. Supports multiple targets, stagger, and scroll animator.
1053
+ */
1054
+ to(target, vars) {
1055
+ const targets = resolveTargets(target);
1056
+ const hasScroll = vars.scroll !== void 0;
1057
+ const varsWithAutoPlay = { ...vars };
1058
+ if (hasScroll) {
1059
+ varsWithAutoPlay.autoPlay = false;
1060
+ }
1061
+ let animation;
1062
+ if (targets.length === 0) {
1063
+ animation = new Tween(null, varsWithAutoPlay);
1064
+ } else if (targets.length === 1) {
1065
+ animation = new Tween(targets[0], varsWithAutoPlay);
1066
+ } else {
1067
+ const tl = new Timeline({
1068
+ delay: varsWithAutoPlay.delay,
1069
+ onStart: varsWithAutoPlay.onStart,
1070
+ onUpdate: varsWithAutoPlay.onUpdate,
1071
+ onComplete: varsWithAutoPlay.onComplete,
1072
+ paused: true
1073
+ });
1074
+ const stagger = varsWithAutoPlay.stagger || 0;
1075
+ const tweenVars = { ...varsWithAutoPlay };
1076
+ delete tweenVars.delay;
1077
+ delete tweenVars.onStart;
1078
+ delete tweenVars.onUpdate;
1079
+ delete tweenVars.onComplete;
1080
+ delete tweenVars.stagger;
1081
+ delete tweenVars.scroll;
1082
+ targets.forEach((t, i) => {
1083
+ tl.to(t, tweenVars, i * stagger);
1084
+ });
1085
+ if (!hasScroll) {
1086
+ tl.play();
1087
+ }
1088
+ animation = tl;
1089
+ }
1090
+ if (hasScroll && vars.scroll) {
1091
+ new ScrollTrigger(animation, vars.scroll);
1092
+ }
1093
+ return animation;
1094
+ },
1095
+ /**
1096
+ * Creates an animation that goes FROM the values defined in 'vars'
1097
+ * TO the current values of the target. Supports multiple targets, stagger, and scroll animator.
1098
+ */
1099
+ from(target, vars) {
1100
+ const targets = resolveTargets(target);
1101
+ const hasScroll = vars.scroll !== void 0;
1102
+ const varsWithAutoPlay = { ...vars };
1103
+ if (hasScroll) {
1104
+ varsWithAutoPlay.autoPlay = false;
1105
+ }
1106
+ let animation;
1107
+ if (targets.length === 0) {
1108
+ animation = new Tween(null, varsWithAutoPlay, void 0, true);
1109
+ } else if (targets.length === 1) {
1110
+ animation = new Tween(targets[0], varsWithAutoPlay, void 0, true);
1111
+ } else {
1112
+ const tl = new Timeline({
1113
+ delay: varsWithAutoPlay.delay,
1114
+ onStart: varsWithAutoPlay.onStart,
1115
+ onUpdate: varsWithAutoPlay.onUpdate,
1116
+ onComplete: varsWithAutoPlay.onComplete,
1117
+ paused: true
1118
+ });
1119
+ const stagger = varsWithAutoPlay.stagger || 0;
1120
+ const tweenVars = { ...varsWithAutoPlay };
1121
+ delete tweenVars.delay;
1122
+ delete tweenVars.onStart;
1123
+ delete tweenVars.onUpdate;
1124
+ delete tweenVars.onComplete;
1125
+ delete tweenVars.stagger;
1126
+ delete tweenVars.scroll;
1127
+ targets.forEach((t, i) => {
1128
+ tl.from(t, tweenVars, i * stagger);
1129
+ });
1130
+ if (!hasScroll) {
1131
+ tl.play();
1132
+ }
1133
+ animation = tl;
1134
+ }
1135
+ if (hasScroll && vars.scroll) {
1136
+ new ScrollTrigger(animation, vars.scroll);
1137
+ }
1138
+ return animation;
1139
+ },
1140
+ /**
1141
+ * Creates an animation that goes FROM the values defined in 'fromVars'
1142
+ * TO the values defined in 'toVars'. Supports multiple targets, stagger, and scroll animator.
1143
+ */
1144
+ fromTo(target, fromVars, toVars) {
1145
+ const targets = resolveTargets(target);
1146
+ const hasScroll = toVars.scroll !== void 0;
1147
+ const toVarsWithAutoPlay = { ...toVars };
1148
+ if (hasScroll) {
1149
+ toVarsWithAutoPlay.autoPlay = false;
1150
+ }
1151
+ let animation;
1152
+ if (targets.length === 0) {
1153
+ animation = new Tween(null, toVarsWithAutoPlay, fromVars);
1154
+ } else if (targets.length === 1) {
1155
+ animation = new Tween(targets[0], toVarsWithAutoPlay, fromVars);
1156
+ } else {
1157
+ const tl = new Timeline({
1158
+ delay: toVarsWithAutoPlay.delay,
1159
+ onStart: toVarsWithAutoPlay.onStart,
1160
+ onUpdate: toVarsWithAutoPlay.onUpdate,
1161
+ onComplete: toVarsWithAutoPlay.onComplete,
1162
+ paused: true
1163
+ });
1164
+ const stagger = toVarsWithAutoPlay.stagger || 0;
1165
+ const tweenVars = { ...toVarsWithAutoPlay };
1166
+ delete tweenVars.delay;
1167
+ delete tweenVars.onStart;
1168
+ delete tweenVars.onUpdate;
1169
+ delete tweenVars.onComplete;
1170
+ delete tweenVars.stagger;
1171
+ delete tweenVars.scroll;
1172
+ targets.forEach((t, i) => {
1173
+ tl.fromTo(t, fromVars, tweenVars, i * stagger);
1174
+ });
1175
+ if (!hasScroll) {
1176
+ tl.play();
1177
+ }
1178
+ animation = tl;
1179
+ }
1180
+ if (hasScroll && toVars.scroll) {
1181
+ new ScrollTrigger(animation, toVars.scroll);
1182
+ }
1183
+ return animation;
1184
+ },
1185
+ /**
1186
+ * Creates a new Timeline instance for sequencing multiple animations.
1187
+ */
1188
+ timeline(vars) {
1189
+ const hasScroll = vars?.scroll !== void 0;
1190
+ const timelineVars = { ...vars };
1191
+ if (hasScroll) {
1192
+ timelineVars.paused = true;
1193
+ }
1194
+ const tl = new Timeline(timelineVars);
1195
+ if (hasScroll && vars?.scroll) {
1196
+ new ScrollTrigger(tl, vars.scroll);
1197
+ }
1198
+ return tl;
1199
+ },
1200
+ /**
1201
+ * Splits text of DOM elements into individual character or word spans,
1202
+ * making them ready for cascaded stagger animations.
1203
+ */
1204
+ splitText(target, options) {
1205
+ return splitText(target, options);
1206
+ },
1207
+ /**
1208
+ * Animates the outline drawing of SVG paths.
1209
+ */
1210
+ drawSVG(target, vars) {
1211
+ const hasScroll = vars.scroll !== void 0;
1212
+ const varsWithAutoPlay = { ...vars };
1213
+ if (hasScroll) {
1214
+ varsWithAutoPlay.autoPlay = false;
1215
+ }
1216
+ const tween = drawSVG(target, varsWithAutoPlay);
1217
+ if (hasScroll && vars.scroll) {
1218
+ new ScrollTrigger(tween, vars.scroll);
1219
+ }
1220
+ return tween;
1221
+ },
1222
+ magnetic(target, options) {
1223
+ magnetic(target, options);
1224
+ },
1225
+ tilt(target, options) {
1226
+ tilt(target, options);
1227
+ },
1228
+ explodeOnScroll(target, options) {
1229
+ explodeOnScroll(target, options);
1230
+ },
1231
+ implodeOnScroll(target, options) {
1232
+ implodeOnScroll(target, options);
1233
+ },
1234
+ revealText(target, options) {
1235
+ revealText(target, options);
1236
+ },
1237
+ scrollReveal(target, options) {
1238
+ scrollReveal(target, options);
1239
+ },
1240
+ slidingMenu(container, links, pill, options) {
1241
+ slidingMenu(container, links, pill, options);
1242
+ },
1243
+ killAllTriggers() {
1244
+ ScrollTrigger.killAll();
1245
+ }
1246
+ };
1247
+ return __toCommonJS(index_exports);
1248
+ })();