@kitlangton/solid-motion 0.1.0 → 0.2.1

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/README.md CHANGED
@@ -48,7 +48,8 @@ Motion values update the DOM directly without re-running components.
48
48
 
49
49
  ## Notes
50
50
 
51
- - Specify `bounce` whenever you use `visualDuration` (the pinned engine,
52
- `motion-dom@12.30`, ignores `visualDuration` on its own).
51
+ - As in Motion for React, `visualDuration` takes effect only alongside `bounce`
52
+ (use `bounce: 0` for no overshoot; negative values overdamp).
53
53
  - Layout animations (`layout`, `layoutId`) are not implemented yet.
54
- - Gesture implementations are adapted from `framer-motion@12.30.0` (MIT).
54
+ - Built on `motion-dom@14`. Gesture implementations are adapted from
55
+ `framer-motion@14.0.0` (MIT).
@@ -64,8 +64,6 @@ export declare class VisualElementDragControls {
64
64
  private startAnimation;
65
65
  private startAxisValueAnimation;
66
66
  private stopAnimation;
67
- private pauseAnimation;
68
- private getAnimationState;
69
67
  /**
70
68
  * Drag works differently depending on which props are provided.
71
69
  *
@@ -73,6 +71,10 @@ export declare class VisualElementDragControls {
73
71
  * - Otherwise, we apply the delta to the x/y motion values.
74
72
  */
75
73
  private getAxisMotionValue;
74
+ /**
75
+ * Measure the live box - projection.layout may or may not include the
76
+ * drag transform, depending on when it was measured.
77
+ */
76
78
  private snapToCursor;
77
79
  /**
78
80
  * When the viewport resizes we want to check if the measured constraints
@@ -48,7 +48,7 @@ export declare function calcViewportConstraints(layoutBox: Box, constraintsBox:
48
48
  };
49
49
  /**
50
50
  * Calculate a transform origin relative to the source axis, between 0-1, that results
51
- * in an asthetically pleasing scale/transform needed to project from source to target.
51
+ * in an aesthetically pleasing scale/transform needed to project from source to target.
52
52
  */
53
53
  export declare function calcOrigin(source: Axis, target: Axis): number;
54
54
  /**
@@ -11,7 +11,7 @@ interface PanSessionHandlers {
11
11
  }
12
12
  interface PanSessionOptions {
13
13
  transformPagePoint?: TransformPoint;
14
- dragSnapToOrigin?: boolean;
14
+ dragSnapToOrigin?: boolean | "x" | "y";
15
15
  distanceThreshold?: number;
16
16
  contextWindow?: (Window & typeof globalThis) | null;
17
17
  /**
@@ -41,6 +41,17 @@ export declare class PanSession {
41
41
  * @internal
42
42
  */
43
43
  private lastMoveEventInfo;
44
+ /**
45
+ * Raw (untransformed) event info, re-transformed each frame
46
+ * so transformPagePoint sees the current parent matrix.
47
+ * @internal
48
+ */
49
+ private lastRawMoveEventInfo;
50
+ /**
51
+ * Whether a pointermove has arrived since the last updatePoint.
52
+ * @internal
53
+ */
54
+ private hasPendingMove?;
44
55
  /**
45
56
  * @internal
46
57
  */
@@ -54,7 +65,7 @@ export declare class PanSession {
54
65
  */
55
66
  private removeListeners;
56
67
  /**
57
- * For determining if an animation should resume after it is interupted
68
+ * For determining if an animation should resume after it is interrupted
58
69
  *
59
70
  * @internal
60
71
  */
@@ -6,7 +6,7 @@ export declare class PanGesture extends Feature<Element> {
6
6
  createPanHandlers(): {
7
7
  onSessionStart: (event: PointerEvent, info: PanInfo) => void;
8
8
  onStart: (event: PointerEvent, info: PanInfo) => void;
9
- onMove: ((event: PointerEvent, info: PanInfo) => void) | undefined;
9
+ onMove: (event: PointerEvent, info: PanInfo) => void;
10
10
  onEnd: (event: PointerEvent, info: PanInfo) => void;
11
11
  };
12
12
  mount(): void;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createComponent, Dynamic, mergeProps, getNextElement, use, insert, effect, style, template } from "solid-js/web";
2
2
  import { createContext, useContext, splitProps, createSignal, createEffect, createMemo, onMount, onCleanup, mergeProps as mergeProps$1, on, untrack, For, batch, Index, children } from "solid-js";
3
- import { isControllingVariants, isVariantLabel, SVGVisualElement, HTMLVisualElement, isMotionValue, isForcedMotionValue, buildHTMLStyles, buildSVGAttrs, isSVGTag, resolveMotionValue, isVariantNode, isAnimationControls, resolveVariantFromProps, getFeatureDefinitions, setFeatureDefinitions, Feature, createAnimationState, isPrimaryPointer, addDomEvent, frameData, frame, cancelFrame, mixNumber, calcLength, createBox, eachAxis, measurePageBox, convertBoxToBoundingBox, convertBoundingBoxToBox, addValueToWillChange, animateMotionValue, setDragLock, percent, hover, press, HTMLProjectionNode, scrapeSVGMotionValuesFromProps, scrapeHTMLMotionValuesFromProps, motionValue, transformValue, transform, mapValue, JSAnimation, resolveElements, defaultOffset, isGenerator, createGeneratorEasing, fillOffset, isSVGElement, isSVGSVGElement, visualElementStore, ObjectVisualElement, animateSingleValue, animateTarget, spring, GroupAnimationWithThen } from "motion-dom";
3
+ import { isControllingVariants, isVariantLabel, SVGVisualElement, HTMLVisualElement, isMotionValue, isForcedMotionValue, buildHTMLStyles, buildSVGAttrs, isSVGTag, resolveMotionValue, isVariantNode, isAnimationControls, resolveVariantFromProps, getFeatureDefinitions, setFeatureDefinitions, Feature, createAnimationState, isPrimaryPointer, addDomEvent, time, frame, frameData, cancelFrame, mixNumber, calcLength, createBox, eachAxis, measurePageBox, convertBoxToBoundingBox, convertBoundingBoxToBox, addValueToWillChange, animateMotionValue, setDragLock, percent, resize, isElementTextInput, hover, press, HTMLProjectionNode, setTarget, scrapeSVGMotionValuesFromProps, scrapeHTMLMotionValuesFromProps, motionValue, transformValue, transform, mapValue, JSAnimation, resolveElements, defaultOffset, isGenerator, createGeneratorEasing, fillOffset, isSVGElement, isSVGSVGElement, visualElementStore, ObjectVisualElement, animateSingleValue, animateTarget, spring, GroupAnimationWithThen } from "motion-dom";
4
4
  export * from "motion-dom";
5
5
  import { pipe, secondsToMilliseconds, millisecondsToSeconds, progress, clamp, invariant, noop, getEasingForSegment, removeItem } from "motion-utils";
6
6
  const defaultContext = {
@@ -408,6 +408,7 @@ class PanSession {
408
408
  this.startEvent = null;
409
409
  this.lastMoveEvent = null;
410
410
  this.lastMoveEventInfo = null;
411
+ this.lastRawMoveEventInfo = null;
411
412
  this.handlers = {};
412
413
  this.removeListeners = () => {
413
414
  };
@@ -424,13 +425,19 @@ class PanSession {
424
425
  };
425
426
  this.updatePoint = () => {
426
427
  if (!(this.lastMoveEvent && this.lastMoveEventInfo)) return;
428
+ this.hasPendingMove = false;
429
+ if (this.lastRawMoveEventInfo) {
430
+ this.lastMoveEventInfo = transformPoint(
431
+ this.lastRawMoveEventInfo,
432
+ this.transformPagePoint
433
+ );
434
+ }
427
435
  const info2 = getPanInfo(this.lastMoveEventInfo, this.history);
428
436
  const isPanStarted = this.startEvent !== null;
429
437
  const isDistancePastThreshold = distance2D(info2.offset, { x: 0, y: 0 }) >= this.distanceThreshold;
430
438
  if (!isPanStarted && !isDistancePastThreshold) return;
431
439
  const { point: point2 } = info2;
432
- const { timestamp: timestamp2 } = frameData;
433
- this.history.push({ ...point2, timestamp: timestamp2 });
440
+ this.history.push({ ...point2, timestamp: time.now() });
434
441
  const { onStart, onMove } = this.handlers;
435
442
  if (!isPanStarted) {
436
443
  onStart && onStart(this.lastMoveEvent, info2);
@@ -440,10 +447,13 @@ class PanSession {
440
447
  };
441
448
  this.handlePointerMove = (event2, info2) => {
442
449
  this.lastMoveEvent = event2;
450
+ this.lastRawMoveEventInfo = info2;
443
451
  this.lastMoveEventInfo = transformPoint(info2, this.transformPagePoint);
452
+ this.hasPendingMove = true;
444
453
  frame.update(this.updatePoint, true);
445
454
  };
446
455
  this.handlePointerUp = (event2, info2) => {
456
+ this.hasPendingMove && this.updatePoint();
447
457
  this.end();
448
458
  const { onEnd, onSessionEnd, resumeAnimation } = this.handlers;
449
459
  if (this.dragSnapToOrigin || !this.startEvent) {
@@ -472,21 +482,25 @@ class PanSession {
472
482
  this.history = [{ ...point, timestamp }];
473
483
  const { onSessionStart } = handlers;
474
484
  onSessionStart && onSessionStart(event, getPanInfo(initialInfo, this.history));
485
+ const eventOptions = { passive: true, capture: true };
475
486
  this.removeListeners = pipe(
476
487
  addPointerEvent(
477
488
  this.contextWindow,
478
489
  "pointermove",
479
- this.handlePointerMove
490
+ this.handlePointerMove,
491
+ eventOptions
480
492
  ),
481
493
  addPointerEvent(
482
494
  this.contextWindow,
483
495
  "pointerup",
484
- this.handlePointerUp
496
+ this.handlePointerUp,
497
+ eventOptions
485
498
  ),
486
499
  addPointerEvent(
487
500
  this.contextWindow,
488
501
  "pointercancel",
489
- this.handlePointerUp
502
+ this.handlePointerUp,
503
+ eventOptions
490
504
  )
491
505
  );
492
506
  if (element) {
@@ -513,12 +527,9 @@ class PanSession {
513
527
  y: window.scrollY
514
528
  });
515
529
  window.addEventListener("scroll", this.onElementScroll, {
516
- capture: true,
517
- passive: true
518
- });
519
- window.addEventListener("scroll", this.onWindowScroll, {
520
- passive: true
530
+ capture: true
521
531
  });
532
+ window.addEventListener("scroll", this.onWindowScroll);
522
533
  this.removeScrollListeners = () => {
523
534
  window.removeEventListener("scroll", this.onElementScroll, {
524
535
  capture: true
@@ -603,15 +614,18 @@ function getVelocity(history, timeDelta) {
603
614
  if (!timestampedPoint) {
604
615
  return { x: 0, y: 0 };
605
616
  }
606
- const time = millisecondsToSeconds(
617
+ if (timestampedPoint === history[0] && history.length > 2 && lastPoint.timestamp - timestampedPoint.timestamp > secondsToMilliseconds(timeDelta) * 2) {
618
+ timestampedPoint = history[1];
619
+ }
620
+ const seconds = millisecondsToSeconds(
607
621
  lastPoint.timestamp - timestampedPoint.timestamp
608
622
  );
609
- if (time === 0) {
623
+ if (seconds === 0) {
610
624
  return { x: 0, y: 0 };
611
625
  }
612
626
  const currentVelocity = {
613
- x: (lastPoint.x - timestampedPoint.x) / time,
614
- y: (lastPoint.y - timestampedPoint.y) / time
627
+ x: (lastPoint.x - timestampedPoint.x) / seconds,
628
+ y: (lastPoint.y - timestampedPoint.y) / seconds
615
629
  };
616
630
  if (currentVelocity.x === Infinity) {
617
631
  currentVelocity.x = 0;
@@ -715,15 +729,10 @@ class VisualElementDragControls {
715
729
  const { presenceContext } = this.visualElement;
716
730
  if (presenceContext && presenceContext.isPresent === false) return;
717
731
  const onSessionStart = (event) => {
718
- if (snapToCursor) {
719
- this.stopAnimation();
720
- this.snapToCursor(extractEventInfo(event).point);
721
- } else {
722
- this.pauseAnimation();
723
- }
732
+ if (snapToCursor) this.snapToCursor(event);
733
+ this.stopAnimation();
724
734
  };
725
735
  const onStart = (event, info) => {
726
- this.stopAnimation();
727
736
  const { drag, dragPropagation, onDragStart } = this.getProps();
728
737
  if (drag && !dragPropagation) {
729
738
  if (this.openDragLock) this.openDragLock();
@@ -754,7 +763,7 @@ class VisualElementDragControls {
754
763
  this.originPoint[axis] = current;
755
764
  });
756
765
  if (onDragStart) {
757
- frame.postRender(() => onDragStart(event, info));
766
+ frame.update(() => onDragStart(event, info), false, true);
758
767
  }
759
768
  addValueToWillChange(this.visualElement, "transform");
760
769
  const { animationState } = this.visualElement;
@@ -781,7 +790,9 @@ class VisualElementDragControls {
781
790
  this.updateAxis("x", info.point, offset);
782
791
  this.updateAxis("y", info.point, offset);
783
792
  this.visualElement.render();
784
- onDrag && onDrag(event, info);
793
+ if (onDrag) {
794
+ frame.update(() => onDrag(event, info), false, true);
795
+ }
785
796
  };
786
797
  const onSessionEnd = (event, info) => {
787
798
  this.latestPointerEvent = event;
@@ -790,9 +801,12 @@ class VisualElementDragControls {
790
801
  this.latestPointerEvent = null;
791
802
  this.latestPanInfo = null;
792
803
  };
793
- const resumeAnimation = () => eachAxis(
794
- (axis) => this.getAnimationState(axis) === "paused" && this.getAxisMotionValue(axis).animation?.play()
795
- );
804
+ const resumeAnimation = () => {
805
+ const { dragSnapToOrigin: snap } = this.getProps();
806
+ if (snap || this.constraints) {
807
+ this.startAnimation({ x: 0, y: 0 });
808
+ }
809
+ };
796
810
  const { dragSnapToOrigin } = this.getProps();
797
811
  this.panSession = new PanSession(
798
812
  originEvent,
@@ -894,7 +908,7 @@ class VisualElementDragControls {
894
908
  }
895
909
  }
896
910
  this.elastic = resolveDragElastic(dragElastic);
897
- if (prevConstraints !== this.constraints && layout && this.constraints && !this.hasMutatedConstraints) {
911
+ if (prevConstraints !== this.constraints && !isRefObject(dragConstraints) && layout && this.constraints && !this.hasMutatedConstraints) {
898
912
  eachAxis((axis) => {
899
913
  if (this.constraints !== false && this.getAxisMotionValue(axis)) {
900
914
  this.constraints[axis] = rebaseAxisConstraints(
@@ -916,6 +930,10 @@ class VisualElementDragControls {
916
930
  );
917
931
  const { projection } = this.visualElement;
918
932
  if (!projection || !projection.layout) return false;
933
+ if (projection.root) {
934
+ projection.root.scroll = void 0;
935
+ projection.root.updateScroll();
936
+ }
919
937
  const constraintsBox = measurePageBox(
920
938
  constraintsElement,
921
939
  projection.root,
@@ -951,7 +969,8 @@ class VisualElementDragControls {
951
969
  return;
952
970
  }
953
971
  let transition = constraints && constraints[axis] || {};
954
- if (dragSnapToOrigin) transition = { min: 0, max: 0 };
972
+ if (dragSnapToOrigin === true || dragSnapToOrigin === axis)
973
+ transition = { min: 0, max: 0 };
955
974
  const bounceStiffness = dragElastic ? 200 : 1e6;
956
975
  const bounceDamping = dragElastic ? 40 : 1e7;
957
976
  const inertia = {
@@ -986,12 +1005,6 @@ class VisualElementDragControls {
986
1005
  stopAnimation() {
987
1006
  eachAxis((axis) => this.getAxisMotionValue(axis).stop());
988
1007
  }
989
- pauseAnimation() {
990
- eachAxis((axis) => this.getAxisMotionValue(axis).animation?.pause());
991
- }
992
- getAnimationState(axis) {
993
- return this.getAxisMotionValue(axis).animation?.state;
994
- }
995
1008
  /**
996
1009
  * Drag works differently depending on which props are provided.
997
1010
  *
@@ -1004,20 +1017,25 @@ class VisualElementDragControls {
1004
1017
  const externalMotionValue = props[dragKey];
1005
1018
  return externalMotionValue ? externalMotionValue : this.visualElement.getValue(
1006
1019
  axis,
1007
- (props.initial ? props.initial[axis] : void 0) || 0
1020
+ this.visualElement.latestValues[axis] ?? 0
1008
1021
  );
1009
1022
  }
1010
- snapToCursor(point) {
1023
+ /**
1024
+ * Measure the live box - projection.layout may or may not include the
1025
+ * drag transform, depending on when it was measured.
1026
+ */
1027
+ snapToCursor({ clientX, clientY }) {
1028
+ const { drag } = this.getProps();
1029
+ const point = { x: clientX, y: clientY };
1030
+ const cursor = this.visualElement.getTransformPagePoint()?.(point) || point;
1031
+ const box = this.visualElement.measureViewportBox();
1011
1032
  eachAxis((axis) => {
1012
- const { drag } = this.getProps();
1013
1033
  if (!shouldDrag(axis, drag, this.currentDirection)) return;
1014
- const { projection } = this.visualElement;
1015
1034
  const axisValue = this.getAxisMotionValue(axis);
1016
- if (projection && projection.layout) {
1017
- const { min, max } = projection.layout.layoutBox[axis];
1018
- const current = axisValue.get() || 0;
1019
- axisValue.set(point[axis] - mixNumber(min, max, 0.5) + current);
1020
- }
1035
+ const { min, max } = box[axis];
1036
+ axisValue.set(
1037
+ (axisValue.get() || 0) + cursor[axis] - mixNumber(min, max, 0.5)
1038
+ );
1021
1039
  });
1022
1040
  }
1023
1041
  /**
@@ -1032,28 +1050,28 @@ class VisualElementDragControls {
1032
1050
  if (!isRefObject(dragConstraints) || !projection || !this.constraints)
1033
1051
  return;
1034
1052
  this.stopAnimation();
1053
+ const constraints = this.constraints;
1035
1054
  const boxProgress = { x: 0, y: 0 };
1036
1055
  eachAxis((axis) => {
1037
- const axisValue = this.getAxisMotionValue(axis);
1038
- if (axisValue && this.constraints !== false) {
1039
- const latest = axisValue.get();
1040
- boxProgress[axis] = calcOrigin(
1041
- { min: latest, max: latest },
1042
- this.constraints[axis]
1043
- );
1044
- }
1056
+ const latest = this.getAxisMotionValue(axis).get();
1057
+ boxProgress[axis] = calcOrigin(
1058
+ { min: latest, max: latest },
1059
+ constraints[axis]
1060
+ );
1045
1061
  });
1046
1062
  const { transformTemplate } = this.visualElement.getProps();
1047
1063
  this.visualElement.current.style.transform = transformTemplate ? transformTemplate({}, "") : "none";
1048
1064
  projection.root && projection.root.updateScroll();
1049
1065
  projection.updateLayout();
1066
+ this.constraints = false;
1050
1067
  this.resolveConstraints();
1051
1068
  eachAxis((axis) => {
1052
- if (!shouldDrag(axis, drag, null)) return;
1053
1069
  const axisValue = this.getAxisMotionValue(axis);
1070
+ if (!shouldDrag(axis, drag, null) || !axisValue.get()) return;
1054
1071
  const { min, max } = this.constraints[axis];
1055
1072
  axisValue.set(mixNumber(min, max, boxProgress[axis]));
1056
1073
  });
1074
+ this.visualElement.render();
1057
1075
  }
1058
1076
  addListeners() {
1059
1077
  if (!this.visualElement.current) return;
@@ -1065,16 +1083,24 @@ class VisualElementDragControls {
1065
1083
  (event) => {
1066
1084
  const { drag, dragListener = true } = this.getProps();
1067
1085
  const target = event.target;
1068
- const isClickingTextInputChild = target !== element && (Boolean(target.closest("input, textarea, select")) || target instanceof HTMLElement && target.isContentEditable);
1086
+ const isClickingTextInputChild = target !== element && isElementTextInput(target);
1069
1087
  if (drag && dragListener && !isClickingTextInputChild) {
1070
1088
  this.start(event);
1071
1089
  }
1072
1090
  }
1073
1091
  );
1092
+ let stopResizeObservers;
1074
1093
  const measureDragConstraints = () => {
1075
1094
  const { dragConstraints } = this.getProps();
1076
1095
  if (isRefObject(dragConstraints) && dragConstraints.current) {
1077
1096
  this.constraints = this.resolveRefConstraints();
1097
+ if (!stopResizeObservers) {
1098
+ stopResizeObservers = startResizeObservers(
1099
+ element,
1100
+ dragConstraints.current,
1101
+ () => this.scalePositionWithinConstraints()
1102
+ );
1103
+ }
1078
1104
  }
1079
1105
  };
1080
1106
  const { projection } = this.visualElement;
@@ -1114,6 +1140,7 @@ class VisualElementDragControls {
1114
1140
  stopPointerListener();
1115
1141
  stopMeasureLayoutListener();
1116
1142
  stopLayoutUpdateListener && stopLayoutUpdateListener();
1143
+ stopResizeObservers && stopResizeObservers();
1117
1144
  };
1118
1145
  }
1119
1146
  getProps() {
@@ -1137,6 +1164,24 @@ class VisualElementDragControls {
1137
1164
  };
1138
1165
  }
1139
1166
  }
1167
+ function skipFirstCall(callback) {
1168
+ let isFirst = true;
1169
+ return () => {
1170
+ if (isFirst) {
1171
+ isFirst = false;
1172
+ return;
1173
+ }
1174
+ callback();
1175
+ };
1176
+ }
1177
+ function startResizeObservers(element, constraintsElement, onResize) {
1178
+ const stopElement = resize(element, skipFirstCall(onResize));
1179
+ const stopContainer = resize(constraintsElement, skipFirstCall(onResize));
1180
+ return () => {
1181
+ stopElement();
1182
+ stopContainer();
1183
+ };
1184
+ }
1140
1185
  function shouldDrag(direction, drag, currentDirection) {
1141
1186
  return (drag === true || drag === direction) && (currentDirection === null || currentDirection === direction);
1142
1187
  }
@@ -1183,7 +1228,7 @@ let DragGesture$1 = class DragGesture extends Feature {
1183
1228
  };
1184
1229
  const asyncHandler = (handler) => (event, info) => {
1185
1230
  if (handler) {
1186
- frame.postRender(() => handler(event, info));
1231
+ frame.update(() => handler(event, info), false, true);
1187
1232
  }
1188
1233
  };
1189
1234
  class PanGesture extends Feature {
@@ -1206,7 +1251,7 @@ class PanGesture extends Feature {
1206
1251
  return {
1207
1252
  onSessionStart: asyncHandler(onPanSessionStart),
1208
1253
  onStart: asyncHandler(onPanStart),
1209
- onMove: onPan,
1254
+ onMove: asyncHandler(onPan),
1210
1255
  onEnd: (event, info) => {
1211
1256
  delete this.session;
1212
1257
  if (onPanEnd) {
@@ -1271,6 +1316,7 @@ class PressGesture extends Feature {
1271
1316
  mount() {
1272
1317
  const { current } = this.node;
1273
1318
  if (!current) return;
1319
+ const { globalTapTarget, propagate } = this.node.props;
1274
1320
  this.unmount = press(
1275
1321
  current,
1276
1322
  (_element, startEvent) => {
@@ -1281,7 +1327,10 @@ class PressGesture extends Feature {
1281
1327
  success ? "End" : "Cancel"
1282
1328
  );
1283
1329
  },
1284
- { useGlobalTarget: this.node.props.globalTapTarget }
1330
+ {
1331
+ useGlobalTarget: globalTapTarget,
1332
+ stopPropagation: propagate?.tap === false
1333
+ }
1285
1334
  );
1286
1335
  }
1287
1336
  unmount() {
@@ -1429,6 +1478,7 @@ function createMotionComponent(Component, {
1429
1478
  if (!visualElement) return;
1430
1479
  visualElement.scheduleRenderMicrotask();
1431
1480
  });
1481
+ let mounted = false;
1432
1482
  createEffect(() => {
1433
1483
  if (!visualElement) return;
1434
1484
  const nextProps = getLatestProps();
@@ -1436,9 +1486,18 @@ function createMotionComponent(Component, {
1436
1486
  visualElement.update(nextProps, nextPresenceContext);
1437
1487
  prepareDragProjection(visualElement, nextProps);
1438
1488
  visualElement.updateFeatures();
1439
- if (visualElement.current && visualElement.animationState) {
1440
- visualElement.animationState.animateChanges();
1489
+ if (!visualElement.current || !visualElement.animationState) return;
1490
+ if (!mounted) {
1491
+ mounted = true;
1492
+ const {
1493
+ animate: animate2
1494
+ } = nextProps;
1495
+ const blocked = nextProps.initial === false || nextPresenceContext?.initial === false;
1496
+ if (blocked && animate2 && typeof animate2 !== "boolean" && !isAnimationControls(animate2)) {
1497
+ setTarget(visualElement, animate2);
1498
+ }
1441
1499
  }
1500
+ visualElement.animationState.animateChanges();
1442
1501
  });
1443
1502
  onCleanup(() => {
1444
1503
  visualElement?.unmount();