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