@hua-labs/motion-core 2.2.1 → 2.2.3

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/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
3
4
 
4
5
  // src/core/MotionEngine.ts
5
6
  var MotionEngine = class {
@@ -198,39 +199,42 @@ var TransitionEffects = class _TransitionEffects {
198
199
  */
199
200
  async fade(element, options) {
200
201
  const transitionId = this.generateTransitionId();
201
- return new Promise(async (resolve) => {
202
- const initialOpacity = parseFloat(getComputedStyle(element).opacity) || 1;
203
- const targetOpacity = options.direction === "reverse" ? 0 : 1;
204
- if (options.direction === "reverse") {
205
- element.style.opacity = initialOpacity.toString();
206
- } else {
207
- element.style.opacity = "0";
208
- }
209
- this.enableGPUAcceleration(element);
210
- const motionId = await motionEngine.motion(
211
- element,
212
- [
213
- { progress: 0, properties: { opacity: options.direction === "reverse" ? initialOpacity : 0 } },
214
- { progress: 1, properties: { opacity: targetOpacity } }
215
- ],
216
- {
217
- duration: options.duration,
218
- easing: options.easing || this.getDefaultEasing(),
219
- delay: options.delay,
220
- onStart: options.onTransitionStart,
221
- onUpdate: (progress) => {
222
- const currentOpacity = options.direction === "reverse" ? initialOpacity * (1 - progress) : targetOpacity * progress;
223
- element.style.opacity = currentOpacity.toString();
224
- },
225
- onComplete: () => {
226
- options.onTransitionComplete?.();
227
- this.activeTransitions.delete(transitionId);
228
- resolve();
229
- }
230
- }
231
- );
232
- this.activeTransitions.set(transitionId, motionId);
202
+ const initialOpacity = parseFloat(getComputedStyle(element).opacity) || 1;
203
+ const targetOpacity = options.direction === "reverse" ? 0 : 1;
204
+ if (options.direction === "reverse") {
205
+ element.style.opacity = initialOpacity.toString();
206
+ } else {
207
+ element.style.opacity = "0";
208
+ }
209
+ this.enableGPUAcceleration(element);
210
+ let resolveTransition;
211
+ const completed = new Promise((resolve) => {
212
+ resolveTransition = resolve;
233
213
  });
214
+ const motionId = await motionEngine.motion(
215
+ element,
216
+ [
217
+ { progress: 0, properties: { opacity: options.direction === "reverse" ? initialOpacity : 0 } },
218
+ { progress: 1, properties: { opacity: targetOpacity } }
219
+ ],
220
+ {
221
+ duration: options.duration,
222
+ easing: options.easing || this.getDefaultEasing(),
223
+ delay: options.delay,
224
+ onStart: options.onTransitionStart,
225
+ onUpdate: (progress) => {
226
+ const currentOpacity = options.direction === "reverse" ? initialOpacity * (1 - progress) : targetOpacity * progress;
227
+ element.style.opacity = currentOpacity.toString();
228
+ },
229
+ onComplete: () => {
230
+ options.onTransitionComplete?.();
231
+ this.activeTransitions.delete(transitionId);
232
+ resolveTransition();
233
+ }
234
+ }
235
+ );
236
+ this.activeTransitions.set(transitionId, motionId);
237
+ return completed;
234
238
  }
235
239
  /**
236
240
  * 슬라이드 전환
@@ -238,38 +242,41 @@ var TransitionEffects = class _TransitionEffects {
238
242
  async slide(element, options) {
239
243
  const transitionId = this.generateTransitionId();
240
244
  const distance = options.distance || 100;
241
- return new Promise(async (resolve) => {
242
- const initialTransform = getComputedStyle(element).transform;
243
- const isReverse = options.direction === "reverse";
244
- if (!isReverse) {
245
- element.style.transform = `translateX(${distance}px)`;
246
- }
247
- this.enableGPUAcceleration(element);
248
- const motionId = await motionEngine.motion(
249
- element,
250
- [
251
- { progress: 0, properties: { translateX: isReverse ? 0 : distance } },
252
- { progress: 1, properties: { translateX: isReverse ? distance : 0 } }
253
- ],
254
- {
255
- duration: options.duration,
256
- easing: options.easing || this.getDefaultEasing(),
257
- delay: options.delay,
258
- onStart: options.onTransitionStart,
259
- onUpdate: (progress) => {
260
- const currentTranslateX = isReverse ? distance * progress : distance * (1 - progress);
261
- element.style.transform = `translateX(${currentTranslateX}px)`;
262
- },
263
- onComplete: () => {
264
- element.style.transform = initialTransform;
265
- options.onTransitionComplete?.();
266
- this.activeTransitions.delete(transitionId);
267
- resolve();
268
- }
269
- }
270
- );
271
- this.activeTransitions.set(transitionId, motionId);
245
+ const initialTransform = getComputedStyle(element).transform;
246
+ const isReverse = options.direction === "reverse";
247
+ if (!isReverse) {
248
+ element.style.transform = `translateX(${distance}px)`;
249
+ }
250
+ this.enableGPUAcceleration(element);
251
+ let resolveTransition;
252
+ const completed = new Promise((resolve) => {
253
+ resolveTransition = resolve;
272
254
  });
255
+ const motionId = await motionEngine.motion(
256
+ element,
257
+ [
258
+ { progress: 0, properties: { translateX: isReverse ? 0 : distance } },
259
+ { progress: 1, properties: { translateX: isReverse ? distance : 0 } }
260
+ ],
261
+ {
262
+ duration: options.duration,
263
+ easing: options.easing || this.getDefaultEasing(),
264
+ delay: options.delay,
265
+ onStart: options.onTransitionStart,
266
+ onUpdate: (progress) => {
267
+ const currentTranslateX = isReverse ? distance * progress : distance * (1 - progress);
268
+ element.style.transform = `translateX(${currentTranslateX}px)`;
269
+ },
270
+ onComplete: () => {
271
+ element.style.transform = initialTransform;
272
+ options.onTransitionComplete?.();
273
+ this.activeTransitions.delete(transitionId);
274
+ resolveTransition();
275
+ }
276
+ }
277
+ );
278
+ this.activeTransitions.set(transitionId, motionId);
279
+ return completed;
273
280
  }
274
281
  /**
275
282
  * 스케일 전환
@@ -277,38 +284,41 @@ var TransitionEffects = class _TransitionEffects {
277
284
  async scale(element, options) {
278
285
  const transitionId = this.generateTransitionId();
279
286
  const scaleValue = options.scale || 0.8;
280
- return new Promise(async (resolve) => {
281
- const initialTransform = getComputedStyle(element).transform;
282
- const isReverse = options.direction === "reverse";
283
- if (!isReverse) {
284
- element.style.transform = `scale(${scaleValue})`;
285
- }
286
- this.enableGPUAcceleration(element);
287
- const motionId = await motionEngine.motion(
288
- element,
289
- [
290
- { progress: 0, properties: { scale: isReverse ? 1 : scaleValue } },
291
- { progress: 1, properties: { scale: isReverse ? scaleValue : 1 } }
292
- ],
293
- {
294
- duration: options.duration,
295
- easing: options.easing || this.getDefaultEasing(),
296
- delay: options.delay,
297
- onStart: options.onTransitionStart,
298
- onUpdate: (progress) => {
299
- const currentScale = isReverse ? 1 - (1 - scaleValue) * progress : scaleValue + (1 - scaleValue) * progress;
300
- element.style.transform = `scale(${currentScale})`;
301
- },
302
- onComplete: () => {
303
- element.style.transform = initialTransform;
304
- options.onTransitionComplete?.();
305
- this.activeTransitions.delete(transitionId);
306
- resolve();
307
- }
308
- }
309
- );
310
- this.activeTransitions.set(transitionId, motionId);
287
+ const initialTransform = getComputedStyle(element).transform;
288
+ const isReverse = options.direction === "reverse";
289
+ if (!isReverse) {
290
+ element.style.transform = `scale(${scaleValue})`;
291
+ }
292
+ this.enableGPUAcceleration(element);
293
+ let resolveTransition;
294
+ const completed = new Promise((resolve) => {
295
+ resolveTransition = resolve;
311
296
  });
297
+ const motionId = await motionEngine.motion(
298
+ element,
299
+ [
300
+ { progress: 0, properties: { scale: isReverse ? 1 : scaleValue } },
301
+ { progress: 1, properties: { scale: isReverse ? scaleValue : 1 } }
302
+ ],
303
+ {
304
+ duration: options.duration,
305
+ easing: options.easing || this.getDefaultEasing(),
306
+ delay: options.delay,
307
+ onStart: options.onTransitionStart,
308
+ onUpdate: (progress) => {
309
+ const currentScale = isReverse ? 1 - (1 - scaleValue) * progress : scaleValue + (1 - scaleValue) * progress;
310
+ element.style.transform = `scale(${currentScale})`;
311
+ },
312
+ onComplete: () => {
313
+ element.style.transform = initialTransform;
314
+ options.onTransitionComplete?.();
315
+ this.activeTransitions.delete(transitionId);
316
+ resolveTransition();
317
+ }
318
+ }
319
+ );
320
+ this.activeTransitions.set(transitionId, motionId);
321
+ return completed;
312
322
  }
313
323
  /**
314
324
  * 플립 전환 (3D 회전)
@@ -316,42 +326,45 @@ var TransitionEffects = class _TransitionEffects {
316
326
  async flip(element, options) {
317
327
  const transitionId = this.generateTransitionId();
318
328
  const perspective = options.perspective || 1e3;
319
- return new Promise(async (resolve) => {
320
- const initialTransform = getComputedStyle(element).transform;
321
- const isReverse = options.direction === "reverse";
322
- element.style.perspective = `${perspective}px`;
323
- element.style.transformStyle = "preserve-3d";
324
- if (!isReverse) {
325
- element.style.transform = `rotateY(90deg)`;
326
- }
327
- this.enableGPUAcceleration(element);
328
- const motionId = await motionEngine.motion(
329
- element,
330
- [
331
- { progress: 0, properties: { rotateY: isReverse ? 0 : 90 } },
332
- { progress: 1, properties: { rotateY: isReverse ? 90 : 0 } }
333
- ],
334
- {
335
- duration: options.duration,
336
- easing: options.easing || this.getDefaultEasing(),
337
- delay: options.delay,
338
- onStart: options.onTransitionStart,
339
- onUpdate: (progress) => {
340
- const currentRotateY = isReverse ? 90 * progress : 90 * (1 - progress);
341
- element.style.transform = `rotateY(${currentRotateY}deg)`;
342
- },
343
- onComplete: () => {
344
- element.style.transform = initialTransform;
345
- element.style.perspective = "";
346
- element.style.transformStyle = "";
347
- options.onTransitionComplete?.();
348
- this.activeTransitions.delete(transitionId);
349
- resolve();
350
- }
351
- }
352
- );
353
- this.activeTransitions.set(transitionId, motionId);
329
+ const initialTransform = getComputedStyle(element).transform;
330
+ const isReverse = options.direction === "reverse";
331
+ element.style.perspective = `${perspective}px`;
332
+ element.style.transformStyle = "preserve-3d";
333
+ if (!isReverse) {
334
+ element.style.transform = `rotateY(90deg)`;
335
+ }
336
+ this.enableGPUAcceleration(element);
337
+ let resolveTransition;
338
+ const completed = new Promise((resolve) => {
339
+ resolveTransition = resolve;
354
340
  });
341
+ const motionId = await motionEngine.motion(
342
+ element,
343
+ [
344
+ { progress: 0, properties: { rotateY: isReverse ? 0 : 90 } },
345
+ { progress: 1, properties: { rotateY: isReverse ? 90 : 0 } }
346
+ ],
347
+ {
348
+ duration: options.duration,
349
+ easing: options.easing || this.getDefaultEasing(),
350
+ delay: options.delay,
351
+ onStart: options.onTransitionStart,
352
+ onUpdate: (progress) => {
353
+ const currentRotateY = isReverse ? 90 * progress : 90 * (1 - progress);
354
+ element.style.transform = `rotateY(${currentRotateY}deg)`;
355
+ },
356
+ onComplete: () => {
357
+ element.style.transform = initialTransform;
358
+ element.style.perspective = "";
359
+ element.style.transformStyle = "";
360
+ options.onTransitionComplete?.();
361
+ this.activeTransitions.delete(transitionId);
362
+ resolveTransition();
363
+ }
364
+ }
365
+ );
366
+ this.activeTransitions.set(transitionId, motionId);
367
+ return completed;
355
368
  }
356
369
  /**
357
370
  * 큐브 전환 (3D 큐브 회전)
@@ -359,82 +372,88 @@ var TransitionEffects = class _TransitionEffects {
359
372
  async cube(element, options) {
360
373
  const transitionId = this.generateTransitionId();
361
374
  const perspective = options.perspective || 1200;
362
- return new Promise(async (resolve) => {
363
- const initialTransform = getComputedStyle(element).transform;
364
- const isReverse = options.direction === "reverse";
365
- element.style.perspective = `${perspective}px`;
366
- element.style.transformStyle = "preserve-3d";
367
- if (!isReverse) {
368
- element.style.transform = `rotateX(90deg) rotateY(45deg)`;
369
- }
370
- this.enableGPUAcceleration(element);
371
- const motionId = await motionEngine.motion(
372
- element,
373
- [
374
- { progress: 0, properties: { rotateX: isReverse ? 0 : 90, rotateY: isReverse ? 0 : 45 } },
375
- { progress: 1, properties: { rotateX: isReverse ? 90 : 0, rotateY: isReverse ? 45 : 0 } }
376
- ],
377
- {
378
- duration: options.duration,
379
- easing: options.easing || this.getDefaultEasing(),
380
- delay: options.delay,
381
- onStart: options.onTransitionStart,
382
- onUpdate: (progress) => {
383
- const currentRotateX = isReverse ? 90 * progress : 90 * (1 - progress);
384
- const currentRotateY = isReverse ? 45 * progress : 45 * (1 - progress);
385
- element.style.transform = `rotateX(${currentRotateX}deg) rotateY(${currentRotateY}deg)`;
386
- },
387
- onComplete: () => {
388
- element.style.transform = initialTransform;
389
- element.style.perspective = "";
390
- element.style.transformStyle = "";
391
- options.onTransitionComplete?.();
392
- this.activeTransitions.delete(transitionId);
393
- resolve();
394
- }
395
- }
396
- );
397
- this.activeTransitions.set(transitionId, motionId);
375
+ const initialTransform = getComputedStyle(element).transform;
376
+ const isReverse = options.direction === "reverse";
377
+ element.style.perspective = `${perspective}px`;
378
+ element.style.transformStyle = "preserve-3d";
379
+ if (!isReverse) {
380
+ element.style.transform = `rotateX(90deg) rotateY(45deg)`;
381
+ }
382
+ this.enableGPUAcceleration(element);
383
+ let resolveTransition;
384
+ const completed = new Promise((resolve) => {
385
+ resolveTransition = resolve;
398
386
  });
387
+ const motionId = await motionEngine.motion(
388
+ element,
389
+ [
390
+ { progress: 0, properties: { rotateX: isReverse ? 0 : 90, rotateY: isReverse ? 0 : 45 } },
391
+ { progress: 1, properties: { rotateX: isReverse ? 90 : 0, rotateY: isReverse ? 45 : 0 } }
392
+ ],
393
+ {
394
+ duration: options.duration,
395
+ easing: options.easing || this.getDefaultEasing(),
396
+ delay: options.delay,
397
+ onStart: options.onTransitionStart,
398
+ onUpdate: (progress) => {
399
+ const currentRotateX = isReverse ? 90 * progress : 90 * (1 - progress);
400
+ const currentRotateY = isReverse ? 45 * progress : 45 * (1 - progress);
401
+ element.style.transform = `rotateX(${currentRotateX}deg) rotateY(${currentRotateY}deg)`;
402
+ },
403
+ onComplete: () => {
404
+ element.style.transform = initialTransform;
405
+ element.style.perspective = "";
406
+ element.style.transformStyle = "";
407
+ options.onTransitionComplete?.();
408
+ this.activeTransitions.delete(transitionId);
409
+ resolveTransition();
410
+ }
411
+ }
412
+ );
413
+ this.activeTransitions.set(transitionId, motionId);
414
+ return completed;
399
415
  }
400
416
  /**
401
417
  * 모프 전환 (복합 변형)
402
418
  */
403
419
  async morph(element, options) {
404
420
  const transitionId = this.generateTransitionId();
405
- return new Promise(async (resolve) => {
406
- const initialTransform = getComputedStyle(element).transform;
407
- const isReverse = options.direction === "reverse";
408
- if (!isReverse) {
409
- element.style.transform = `scale(0.9) rotate(5deg)`;
410
- }
411
- this.enableGPUAcceleration(element);
412
- const motionId = await motionEngine.motion(
413
- element,
414
- [
415
- { progress: 0, properties: { scale: isReverse ? 1 : 0.9, rotate: isReverse ? 0 : 5 } },
416
- { progress: 1, properties: { scale: isReverse ? 0.9 : 1, rotate: isReverse ? 5 : 0 } }
417
- ],
418
- {
419
- duration: options.duration,
420
- easing: options.easing || this.getDefaultEasing(),
421
- delay: options.delay,
422
- onStart: options.onTransitionStart,
423
- onUpdate: (progress) => {
424
- const currentScale = isReverse ? 1 - 0.1 * progress : 0.9 + 0.1 * progress;
425
- const currentRotate = isReverse ? 5 * progress : 5 * (1 - progress);
426
- element.style.transform = `scale(${currentScale}) rotate(${currentRotate}deg)`;
427
- },
428
- onComplete: () => {
429
- element.style.transform = initialTransform;
430
- options.onTransitionComplete?.();
431
- this.activeTransitions.delete(transitionId);
432
- resolve();
433
- }
434
- }
435
- );
436
- this.activeTransitions.set(transitionId, motionId);
421
+ const initialTransform = getComputedStyle(element).transform;
422
+ const isReverse = options.direction === "reverse";
423
+ if (!isReverse) {
424
+ element.style.transform = `scale(0.9) rotate(5deg)`;
425
+ }
426
+ this.enableGPUAcceleration(element);
427
+ let resolveTransition;
428
+ const completed = new Promise((resolve) => {
429
+ resolveTransition = resolve;
437
430
  });
431
+ const motionId = await motionEngine.motion(
432
+ element,
433
+ [
434
+ { progress: 0, properties: { scale: isReverse ? 1 : 0.9, rotate: isReverse ? 0 : 5 } },
435
+ { progress: 1, properties: { scale: isReverse ? 0.9 : 1, rotate: isReverse ? 5 : 0 } }
436
+ ],
437
+ {
438
+ duration: options.duration,
439
+ easing: options.easing || this.getDefaultEasing(),
440
+ delay: options.delay,
441
+ onStart: options.onTransitionStart,
442
+ onUpdate: (progress) => {
443
+ const currentScale = isReverse ? 1 - 0.1 * progress : 0.9 + 0.1 * progress;
444
+ const currentRotate = isReverse ? 5 * progress : 5 * (1 - progress);
445
+ element.style.transform = `scale(${currentScale}) rotate(${currentRotate}deg)`;
446
+ },
447
+ onComplete: () => {
448
+ element.style.transform = initialTransform;
449
+ options.onTransitionComplete?.();
450
+ this.activeTransitions.delete(transitionId);
451
+ resolveTransition();
452
+ }
453
+ }
454
+ );
455
+ this.activeTransitions.set(transitionId, motionId);
456
+ return completed;
438
457
  }
439
458
  /**
440
459
  * 전환 중지
@@ -906,10 +925,10 @@ function useSimplePageMotions(config) {
906
925
  const calculateMotionValues = useCallback((isVisible, elementConfig) => {
907
926
  const preset = getMotionPreset(elementConfig.type);
908
927
  mergeWithPreset(preset, elementConfig);
909
- let opacity = isVisible ? 1 : 0;
910
- let translateY = isVisible ? 0 : 20;
911
- let translateX = 0;
912
- let scale = isVisible ? 1 : 0.95;
928
+ const opacity = isVisible ? 1 : 0;
929
+ const translateY = isVisible ? 0 : 20;
930
+ const translateX = 0;
931
+ const scale = isVisible ? 1 : 0.95;
913
932
  return { opacity, translateY, translateX, scale };
914
933
  }, []);
915
934
  useEffect(() => {
@@ -1169,7 +1188,7 @@ function usePageMotions(config) {
1169
1188
  const mergedConfig = mergeWithPreset(preset, elementConfig);
1170
1189
  let opacity = state.finalVisibility ? 1 : 0;
1171
1190
  let translateY = state.finalVisibility ? 0 : 20;
1172
- let translateX = 0;
1191
+ const translateX = 0;
1173
1192
  let scale = state.finalVisibility ? 1 : 0.95;
1174
1193
  if (mergedConfig.hover && state.isHovered) {
1175
1194
  scale *= 1.1;
@@ -1666,9 +1685,77 @@ function getEasingForType(type, easing2) {
1666
1685
  if (type === "bounceIn") return "cubic-bezier(0.34, 1.56, 0.64, 1)";
1667
1686
  return "ease-out";
1668
1687
  }
1688
+ function getMultiEffectInitialStyle(effects, defaultDistance) {
1689
+ const style = {};
1690
+ const transforms = [];
1691
+ if (effects.fade) {
1692
+ style.opacity = 0;
1693
+ }
1694
+ if (effects.slide) {
1695
+ const config = typeof effects.slide === "object" ? effects.slide : {};
1696
+ const direction = config.direction ?? "up";
1697
+ const distance = config.distance ?? defaultDistance;
1698
+ switch (direction) {
1699
+ case "up":
1700
+ transforms.push(`translateY(${distance}px)`);
1701
+ break;
1702
+ case "down":
1703
+ transforms.push(`translateY(-${distance}px)`);
1704
+ break;
1705
+ case "left":
1706
+ transforms.push(`translateX(${distance}px)`);
1707
+ break;
1708
+ case "right":
1709
+ transforms.push(`translateX(-${distance}px)`);
1710
+ break;
1711
+ }
1712
+ if (!effects.fade) style.opacity = 0;
1713
+ }
1714
+ if (effects.scale) {
1715
+ const config = typeof effects.scale === "object" ? effects.scale : {};
1716
+ const from = config.from ?? 0.95;
1717
+ transforms.push(`scale(${from})`);
1718
+ if (!effects.fade && !effects.slide) style.opacity = 0;
1719
+ }
1720
+ if (effects.bounce) {
1721
+ transforms.push("scale(0)");
1722
+ if (!effects.fade && !effects.slide && !effects.scale) style.opacity = 0;
1723
+ }
1724
+ if (transforms.length > 0) {
1725
+ style.transform = transforms.join(" ");
1726
+ } else {
1727
+ style.transform = "none";
1728
+ }
1729
+ if (effects.fade && transforms.length === 0) {
1730
+ style.transform = "none";
1731
+ }
1732
+ return style;
1733
+ }
1734
+ function getMultiEffectVisibleStyle(effects) {
1735
+ const style = {};
1736
+ if (effects.fade) {
1737
+ const config = typeof effects.fade === "object" ? effects.fade : {};
1738
+ style.opacity = config.targetOpacity ?? 1;
1739
+ } else {
1740
+ style.opacity = 1;
1741
+ }
1742
+ if (effects.scale) {
1743
+ const config = typeof effects.scale === "object" ? effects.scale : {};
1744
+ style.transform = `scale(${config.to ?? 1})`;
1745
+ } else {
1746
+ style.transform = "none";
1747
+ }
1748
+ return style;
1749
+ }
1750
+ function getMultiEffectEasing(effects, easing2) {
1751
+ if (easing2) return easing2;
1752
+ if (effects.bounce) return "cubic-bezier(0.34, 1.56, 0.64, 1)";
1753
+ return "ease-out";
1754
+ }
1669
1755
  function useUnifiedMotion(options) {
1670
1756
  const {
1671
1757
  type,
1758
+ effects,
1672
1759
  duration = 600,
1673
1760
  autoStart = true,
1674
1761
  delay = 0,
@@ -1681,7 +1768,8 @@ function useUnifiedMotion(options) {
1681
1768
  onStop,
1682
1769
  onReset
1683
1770
  } = options;
1684
- const resolvedEasing = getEasingForType(type, easing2);
1771
+ const resolvedType = type ?? "fadeIn";
1772
+ const resolvedEasing = getEasingForType(resolvedType, easing2);
1685
1773
  const ref = useRef(null);
1686
1774
  const [isVisible, setIsVisible] = useState(false);
1687
1775
  const [isAnimating, setIsAnimating] = useState(false);
@@ -1741,7 +1829,19 @@ function useUnifiedMotion(options) {
1741
1829
  return () => stop();
1742
1830
  }, [stop]);
1743
1831
  const style = useMemo(() => {
1744
- const base = isVisible ? getVisibleStyle() : getInitialStyle(type, distance);
1832
+ if (effects) {
1833
+ const base2 = isVisible ? getMultiEffectVisibleStyle(effects) : getMultiEffectInitialStyle(effects, distance);
1834
+ const resolvedEasingMulti = getMultiEffectEasing(effects, easing2);
1835
+ return {
1836
+ ...base2,
1837
+ transition: `all ${duration}ms ${resolvedEasingMulti}`,
1838
+ "--motion-delay": `${delay}ms`,
1839
+ "--motion-duration": `${duration}ms`,
1840
+ "--motion-easing": resolvedEasingMulti,
1841
+ "--motion-progress": `${progress}`
1842
+ };
1843
+ }
1844
+ const base = isVisible ? getVisibleStyle() : getInitialStyle(resolvedType, distance);
1745
1845
  return {
1746
1846
  ...base,
1747
1847
  transition: `all ${duration}ms ${resolvedEasing}`,
@@ -1750,7 +1850,7 @@ function useUnifiedMotion(options) {
1750
1850
  "--motion-easing": resolvedEasing,
1751
1851
  "--motion-progress": `${progress}`
1752
1852
  };
1753
- }, [isVisible, type, distance, duration, resolvedEasing, delay, progress]);
1853
+ }, [isVisible, type, effects, distance, duration, resolvedEasing, easing2, delay, progress, resolvedType]);
1754
1854
  return {
1755
1855
  ref,
1756
1856
  isVisible,
@@ -1928,7 +2028,7 @@ function useSlideUp(options = {}) {
1928
2028
  }, 50);
1929
2029
  return () => clearInterval(id);
1930
2030
  }, [nodeReady]);
1931
- const getInitialTransform = useCallback(() => {
2031
+ const getInitialTransform2 = useCallback(() => {
1932
2032
  switch (direction) {
1933
2033
  case "up":
1934
2034
  return `translateY(${distance}px)`;
@@ -2008,7 +2108,7 @@ function useSlideUp(options = {}) {
2008
2108
  stop();
2009
2109
  };
2010
2110
  }, [stop]);
2011
- const initialTransform = useMemo(() => getInitialTransform(), [getInitialTransform]);
2111
+ const initialTransform = useMemo(() => getInitialTransform2(), [getInitialTransform2]);
2012
2112
  const finalTransform = useMemo(() => {
2013
2113
  return direction === "left" || direction === "right" ? "translateX(0)" : "translateY(0)";
2014
2114
  }, [direction]);
@@ -4188,7 +4288,408 @@ function useGestureMotion(options) {
4188
4288
  isActive: gestureState.isActive
4189
4289
  };
4190
4290
  }
4291
+ function useTypewriter(options) {
4292
+ const { text, speed = 50, delay = 0, enabled = true, onComplete } = options;
4293
+ const [index, setIndex] = useState(0);
4294
+ const [started, setStarted] = useState(false);
4295
+ const timerRef = useRef(null);
4296
+ const restart = useCallback(() => {
4297
+ setIndex(0);
4298
+ setStarted(false);
4299
+ }, []);
4300
+ useEffect(() => {
4301
+ if (!enabled) return;
4302
+ const id = setTimeout(() => setStarted(true), delay);
4303
+ return () => clearTimeout(id);
4304
+ }, [enabled, delay]);
4305
+ useEffect(() => {
4306
+ if (!started || !enabled) return;
4307
+ if (index >= text.length) {
4308
+ onComplete?.();
4309
+ return;
4310
+ }
4311
+ timerRef.current = setTimeout(() => {
4312
+ setIndex((prev) => prev + 1);
4313
+ }, speed);
4314
+ return () => {
4315
+ if (timerRef.current) clearTimeout(timerRef.current);
4316
+ };
4317
+ }, [started, enabled, index, text.length, speed, onComplete]);
4318
+ useEffect(() => {
4319
+ setIndex(0);
4320
+ setStarted(false);
4321
+ }, [text]);
4322
+ return {
4323
+ displayText: text.slice(0, index),
4324
+ isTyping: started && index < text.length,
4325
+ progress: text.length > 0 ? index / text.length : 0,
4326
+ restart
4327
+ };
4328
+ }
4329
+ function useCustomCursor(options = {}) {
4330
+ const {
4331
+ enabled = true,
4332
+ size = 32,
4333
+ smoothing = 0.15,
4334
+ hoverScale = 1.5,
4335
+ detectLabels = true
4336
+ } = options;
4337
+ const [isVisible, setIsVisible] = useState(false);
4338
+ const [label, setLabel] = useState(null);
4339
+ const [isHovering, setIsHovering] = useState(false);
4340
+ const targetRef = useRef({ x: 0, y: 0 });
4341
+ const currentRef = useRef({ x: 0, y: 0 });
4342
+ const [pos, setPos] = useState({ x: 0, y: 0 });
4343
+ const rafRef = useRef(null);
4344
+ const animate = useCallback(() => {
4345
+ const dx = targetRef.current.x - currentRef.current.x;
4346
+ const dy = targetRef.current.y - currentRef.current.y;
4347
+ currentRef.current.x += dx * smoothing;
4348
+ currentRef.current.y += dy * smoothing;
4349
+ setPos({ x: currentRef.current.x, y: currentRef.current.y });
4350
+ if (Math.abs(dx) > 0.1 || Math.abs(dy) > 0.1) {
4351
+ rafRef.current = requestAnimationFrame(animate);
4352
+ }
4353
+ }, [smoothing]);
4354
+ useEffect(() => {
4355
+ if (!enabled || typeof window === "undefined") return;
4356
+ const handleMouseMove = (e) => {
4357
+ targetRef.current = { x: e.clientX, y: e.clientY };
4358
+ setIsVisible(true);
4359
+ if (!rafRef.current) {
4360
+ rafRef.current = requestAnimationFrame(animate);
4361
+ }
4362
+ if (detectLabels) {
4363
+ const target = e.target;
4364
+ const cursorEl = target.closest("[data-cursor]");
4365
+ if (cursorEl) {
4366
+ setLabel(cursorEl.dataset.cursor || null);
4367
+ setIsHovering(true);
4368
+ } else {
4369
+ setLabel(null);
4370
+ setIsHovering(false);
4371
+ }
4372
+ }
4373
+ };
4374
+ const handleMouseLeave = () => {
4375
+ setIsVisible(false);
4376
+ setLabel(null);
4377
+ setIsHovering(false);
4378
+ };
4379
+ document.addEventListener("mousemove", handleMouseMove);
4380
+ document.addEventListener("mouseleave", handleMouseLeave);
4381
+ return () => {
4382
+ document.removeEventListener("mousemove", handleMouseMove);
4383
+ document.removeEventListener("mouseleave", handleMouseLeave);
4384
+ if (rafRef.current) cancelAnimationFrame(rafRef.current);
4385
+ };
4386
+ }, [enabled, detectLabels, animate]);
4387
+ const scale = isHovering ? hoverScale : 1;
4388
+ const style = useMemo(() => ({
4389
+ "--cursor-x": `${pos.x}px`,
4390
+ "--cursor-y": `${pos.y}px`,
4391
+ "--cursor-size": `${size}px`,
4392
+ "--cursor-scale": `${scale}`,
4393
+ position: "fixed",
4394
+ left: pos.x - size * scale / 2,
4395
+ top: pos.y - size * scale / 2,
4396
+ width: size * scale,
4397
+ height: size * scale,
4398
+ pointerEvents: "none",
4399
+ zIndex: 9999,
4400
+ transition: "width 0.2s, height 0.2s, left 0.05s, top 0.05s"
4401
+ }), [pos.x, pos.y, size, scale]);
4402
+ return { x: pos.x, y: pos.y, label, isHovering, style, isVisible };
4403
+ }
4404
+ function useMagneticCursor(options = {}) {
4405
+ const { strength = 0.3, radius = 100, enabled = true } = options;
4406
+ const ref = useRef(null);
4407
+ const transformRef = useRef({ x: 0, y: 0 });
4408
+ const styleRef = useRef({
4409
+ transition: "transform 0.3s cubic-bezier(0.22, 1, 0.36, 1)",
4410
+ transform: "translate(0px, 0px)"
4411
+ });
4412
+ const onMouseMove = useCallback((e) => {
4413
+ if (!enabled || !ref.current) return;
4414
+ const rect = ref.current.getBoundingClientRect();
4415
+ const centerX = rect.left + rect.width / 2;
4416
+ const centerY = rect.top + rect.height / 2;
4417
+ const dx = e.clientX - centerX;
4418
+ const dy = e.clientY - centerY;
4419
+ const dist = Math.sqrt(dx * dx + dy * dy);
4420
+ if (dist < radius) {
4421
+ const pull = (1 - dist / radius) * strength;
4422
+ transformRef.current = { x: dx * pull, y: dy * pull };
4423
+ } else {
4424
+ transformRef.current = { x: 0, y: 0 };
4425
+ }
4426
+ ref.current.style.transform = `translate(${transformRef.current.x}px, ${transformRef.current.y}px)`;
4427
+ }, [enabled, strength, radius]);
4428
+ const onMouseLeave = useCallback(() => {
4429
+ if (!ref.current) return;
4430
+ transformRef.current = { x: 0, y: 0 };
4431
+ ref.current.style.transform = "translate(0px, 0px)";
4432
+ }, []);
4433
+ const handlers = useMemo(() => ({ onMouseMove, onMouseLeave }), [onMouseMove, onMouseLeave]);
4434
+ return { ref, handlers, style: styleRef.current };
4435
+ }
4436
+ function useSmoothScroll(options = {}) {
4437
+ const {
4438
+ enabled = true,
4439
+ lerp = 0.1,
4440
+ wheelMultiplier = 1,
4441
+ touchMultiplier = 2,
4442
+ direction = "vertical",
4443
+ onScroll
4444
+ } = options;
4445
+ const [scroll, setScroll] = useState(0);
4446
+ const [progress, setProgress] = useState(0);
4447
+ const targetRef = useRef(0);
4448
+ const currentRef = useRef(0);
4449
+ const rafRef = useRef(null);
4450
+ const isRunningRef = useRef(false);
4451
+ const touchStartRef = useRef(0);
4452
+ const getMaxScroll = useCallback(() => {
4453
+ if (typeof document === "undefined") return 0;
4454
+ return direction === "vertical" ? document.documentElement.scrollHeight - window.innerHeight : document.documentElement.scrollWidth - window.innerWidth;
4455
+ }, [direction]);
4456
+ const clamp = useCallback((val) => {
4457
+ return Math.max(0, Math.min(val, getMaxScroll()));
4458
+ }, [getMaxScroll]);
4459
+ const animate = useCallback(() => {
4460
+ const dx = targetRef.current - currentRef.current;
4461
+ if (Math.abs(dx) < 0.5) {
4462
+ currentRef.current = targetRef.current;
4463
+ setScroll(currentRef.current);
4464
+ isRunningRef.current = false;
4465
+ return;
4466
+ }
4467
+ currentRef.current += dx * lerp;
4468
+ setScroll(currentRef.current);
4469
+ const max = getMaxScroll();
4470
+ setProgress(max > 0 ? currentRef.current / max : 0);
4471
+ onScroll?.(currentRef.current);
4472
+ if (direction === "vertical") {
4473
+ window.scrollTo(0, currentRef.current);
4474
+ } else {
4475
+ window.scrollTo(currentRef.current, 0);
4476
+ }
4477
+ rafRef.current = requestAnimationFrame(animate);
4478
+ }, [lerp, direction, getMaxScroll, onScroll]);
4479
+ const startAnimation = useCallback(() => {
4480
+ if (isRunningRef.current) return;
4481
+ isRunningRef.current = true;
4482
+ rafRef.current = requestAnimationFrame(animate);
4483
+ }, [animate]);
4484
+ useEffect(() => {
4485
+ if (!enabled || typeof window === "undefined") return;
4486
+ document.documentElement.style.scrollBehavior = "auto";
4487
+ currentRef.current = direction === "vertical" ? window.scrollY : window.scrollX;
4488
+ targetRef.current = currentRef.current;
4489
+ const handleWheel = (e) => {
4490
+ e.preventDefault();
4491
+ const delta = direction === "vertical" ? e.deltaY : e.deltaX;
4492
+ targetRef.current = clamp(targetRef.current + delta * wheelMultiplier);
4493
+ startAnimation();
4494
+ };
4495
+ const handleTouchStart = (e) => {
4496
+ touchStartRef.current = direction === "vertical" ? e.touches[0].clientY : e.touches[0].clientX;
4497
+ };
4498
+ const handleTouchMove = (e) => {
4499
+ const current = direction === "vertical" ? e.touches[0].clientY : e.touches[0].clientX;
4500
+ const delta = (touchStartRef.current - current) * touchMultiplier;
4501
+ touchStartRef.current = current;
4502
+ targetRef.current = clamp(targetRef.current + delta);
4503
+ startAnimation();
4504
+ };
4505
+ window.addEventListener("wheel", handleWheel, { passive: false });
4506
+ window.addEventListener("touchstart", handleTouchStart, { passive: true });
4507
+ window.addEventListener("touchmove", handleTouchMove, { passive: true });
4508
+ return () => {
4509
+ window.removeEventListener("wheel", handleWheel);
4510
+ window.removeEventListener("touchstart", handleTouchStart);
4511
+ window.removeEventListener("touchmove", handleTouchMove);
4512
+ if (rafRef.current) cancelAnimationFrame(rafRef.current);
4513
+ document.documentElement.style.scrollBehavior = "";
4514
+ };
4515
+ }, [enabled, direction, wheelMultiplier, touchMultiplier, clamp, startAnimation]);
4516
+ const scrollTo = useCallback((target, opts) => {
4517
+ const offset = opts?.offset ?? 0;
4518
+ if (typeof target === "number") {
4519
+ targetRef.current = clamp(target + offset);
4520
+ } else {
4521
+ const rect = target.getBoundingClientRect();
4522
+ const pos = direction === "vertical" ? rect.top + currentRef.current + offset : rect.left + currentRef.current + offset;
4523
+ targetRef.current = clamp(pos);
4524
+ }
4525
+ startAnimation();
4526
+ }, [clamp, direction, startAnimation]);
4527
+ const stop = useCallback(() => {
4528
+ if (rafRef.current) {
4529
+ cancelAnimationFrame(rafRef.current);
4530
+ rafRef.current = null;
4531
+ }
4532
+ isRunningRef.current = false;
4533
+ targetRef.current = currentRef.current;
4534
+ }, []);
4535
+ return {
4536
+ scroll,
4537
+ targetScroll: targetRef.current,
4538
+ progress,
4539
+ scrollTo,
4540
+ stop
4541
+ };
4542
+ }
4543
+ function useElementProgress(options = {}) {
4544
+ const { start = 0, end = 1, clamp = true } = options;
4545
+ const ref = useRef(null);
4546
+ const [progress, setProgress] = useState(0);
4547
+ const [isInView, setIsInView] = useState(false);
4548
+ useEffect(() => {
4549
+ const el = ref.current;
4550
+ if (!el || typeof window === "undefined") return;
4551
+ const calculate = () => {
4552
+ const rect = el.getBoundingClientRect();
4553
+ const vh = window.innerHeight;
4554
+ const elementTop = rect.top;
4555
+ const elementBottom = rect.bottom;
4556
+ const trackStart = vh * (1 - start);
4557
+ const trackEnd = vh * end * -1 + vh;
4558
+ const range = trackStart - trackEnd;
4559
+ const raw = range > 0 ? (trackStart - elementTop) / range : 0;
4560
+ const clamped = clamp ? Math.max(0, Math.min(1, raw)) : raw;
4561
+ setProgress(clamped);
4562
+ setIsInView(elementBottom > 0 && elementTop < vh);
4563
+ };
4564
+ calculate();
4565
+ window.addEventListener("scroll", calculate, { passive: true });
4566
+ window.addEventListener("resize", calculate, { passive: true });
4567
+ return () => {
4568
+ window.removeEventListener("scroll", calculate);
4569
+ window.removeEventListener("resize", calculate);
4570
+ };
4571
+ }, [start, end, clamp]);
4572
+ return { ref, progress, isInView };
4573
+ }
4574
+ function Motion({
4575
+ as: Component = "div",
4576
+ type,
4577
+ effects,
4578
+ scroll,
4579
+ delay,
4580
+ duration,
4581
+ children,
4582
+ className,
4583
+ style: userStyle,
4584
+ ...rest
4585
+ }) {
4586
+ const scrollOptions = useMemo(() => {
4587
+ if (!scroll) return null;
4588
+ const base = typeof scroll === "object" ? scroll : {};
4589
+ return {
4590
+ ...base,
4591
+ ...delay != null && { delay },
4592
+ ...duration != null && { duration },
4593
+ ...type != null && { motionType: type }
4594
+ };
4595
+ }, [scroll, delay, duration, type]);
4596
+ const scrollMotion = useScrollReveal(scrollOptions ?? { delay: 0 });
4597
+ const unifiedMotion = useUnifiedMotion({
4598
+ type: type ?? "fadeIn",
4599
+ effects,
4600
+ delay,
4601
+ duration,
4602
+ autoStart: true
4603
+ });
4604
+ const isScroll = scroll != null && scroll !== false;
4605
+ const motion = isScroll ? scrollMotion : unifiedMotion;
4606
+ const mergedStyle = useMemo(() => {
4607
+ if (!userStyle) return motion.style;
4608
+ return { ...motion.style, ...userStyle };
4609
+ }, [motion.style, userStyle]);
4610
+ return /* @__PURE__ */ jsx(
4611
+ Component,
4612
+ {
4613
+ ref: motion.ref,
4614
+ className,
4615
+ style: mergedStyle,
4616
+ ...rest,
4617
+ children
4618
+ }
4619
+ );
4620
+ }
4621
+ function getInitialTransform(motionType) {
4622
+ switch (motionType) {
4623
+ case "slideUp":
4624
+ return "translateY(32px)";
4625
+ case "slideLeft":
4626
+ return "translateX(-32px)";
4627
+ case "slideRight":
4628
+ return "translateX(32px)";
4629
+ case "scaleIn":
4630
+ return "scale(0.95)";
4631
+ case "bounceIn":
4632
+ return "scale(0.75)";
4633
+ case "fadeIn":
4634
+ default:
4635
+ return "none";
4636
+ }
4637
+ }
4638
+ function useStagger(options) {
4639
+ const {
4640
+ count,
4641
+ staggerDelay = 100,
4642
+ baseDelay = 0,
4643
+ duration = 700,
4644
+ motionType = "fadeIn",
4645
+ threshold = 0.1,
4646
+ easing: easing2 = "ease-out"
4647
+ } = options;
4648
+ const containerRef = useRef(null);
4649
+ const [isVisible, setIsVisible] = useState(false);
4650
+ useEffect(() => {
4651
+ if (!containerRef.current) return;
4652
+ const observer = new IntersectionObserver(
4653
+ (entries) => {
4654
+ entries.forEach((entry) => {
4655
+ if (entry.isIntersecting) {
4656
+ setIsVisible(true);
4657
+ observer.disconnect();
4658
+ }
4659
+ });
4660
+ },
4661
+ { threshold }
4662
+ );
4663
+ observer.observe(containerRef.current);
4664
+ return () => {
4665
+ observer.disconnect();
4666
+ };
4667
+ }, [threshold]);
4668
+ const initialTransform = useMemo(() => getInitialTransform(motionType), [motionType]);
4669
+ const styles = useMemo(() => {
4670
+ return Array.from({ length: count }, (_, i) => {
4671
+ const itemDelay = baseDelay + i * staggerDelay;
4672
+ if (!isVisible) {
4673
+ return {
4674
+ opacity: 0,
4675
+ transform: initialTransform,
4676
+ transition: `opacity ${duration}ms ${easing2} ${itemDelay}ms, transform ${duration}ms ${easing2} ${itemDelay}ms`
4677
+ };
4678
+ }
4679
+ return {
4680
+ opacity: 1,
4681
+ transform: "none",
4682
+ transition: `opacity ${duration}ms ${easing2} ${itemDelay}ms, transform ${duration}ms ${easing2} ${itemDelay}ms`
4683
+ };
4684
+ });
4685
+ }, [count, isVisible, staggerDelay, baseDelay, duration, motionType, easing2, initialTransform]);
4686
+ return {
4687
+ containerRef,
4688
+ styles,
4689
+ isVisible
4690
+ };
4691
+ }
4191
4692
 
4192
- export { MOTION_PRESETS, MotionEngine, PAGE_MOTIONS, PerformanceOptimizer, TransitionEffects, applyEasing, easeIn, easeInOut, easeInOutQuad, easeInQuad, easeOut, easeOutQuad, easingPresets, getAvailableEasings, getEasing, getMotionPreset, getPagePreset, getPresetEasing, isEasingFunction, isValidEasing, linear, mergeWithPreset, motionEngine, performanceOptimizer, safeApplyEasing, transitionEffects, useBounceIn, useClickToggle, useFadeIn, useFocusToggle, useGesture, useGestureMotion, useGradient, useHoverMotion, useInView, useMotionState, useMouse, usePageMotions, usePulse, useReducedMotion, useRepeat, useScaleIn, useScrollProgress, useScrollReveal, useSimplePageMotion, useSlideDown, useSlideLeft, useSlideRight, useSlideUp, useSmartMotion, useSpringMotion, useToggleMotion, useUnifiedMotion, useWindowSize };
4693
+ export { MOTION_PRESETS, Motion, MotionEngine, PAGE_MOTIONS, PerformanceOptimizer, TransitionEffects, applyEasing, easeIn, easeInOut, easeInOutQuad, easeInQuad, easeOut, easeOutQuad, easingPresets, getAvailableEasings, getEasing, getMotionPreset, getPagePreset, getPresetEasing, isEasingFunction, isValidEasing, linear, mergeWithPreset, motionEngine, performanceOptimizer, safeApplyEasing, transitionEffects, useBounceIn, useClickToggle, useCustomCursor, useElementProgress, useFadeIn, useFocusToggle, useGesture, useGestureMotion, useGradient, useHoverMotion, useInView, useMagneticCursor, useMotionState, useMouse, usePageMotions, usePulse, useReducedMotion, useRepeat, useScaleIn, useScrollProgress, useScrollReveal, useSimplePageMotion, useSlideDown, useSlideLeft, useSlideRight, useSlideUp, useSmartMotion, useSmoothScroll, useSpringMotion, useStagger, useToggleMotion, useTypewriter, useUnifiedMotion, useWindowSize };
4193
4694
  //# sourceMappingURL=index.mjs.map
4194
4695
  //# sourceMappingURL=index.mjs.map