framer-motion 7.7.3 → 7.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2309,176 +2309,6 @@
2309
2309
  current: false,
2310
2310
  };
2311
2311
 
2312
- /*
2313
- Detect and load appropriate clock setting for the execution environment
2314
- */
2315
- const defaultTimestep = (1 / 60) * 1000;
2316
- const getCurrentTime = typeof performance !== "undefined"
2317
- ? () => performance.now()
2318
- : () => Date.now();
2319
- const onNextFrame = typeof window !== "undefined"
2320
- ? (callback) => window.requestAnimationFrame(callback)
2321
- : (callback) => setTimeout(() => callback(getCurrentTime()), defaultTimestep);
2322
-
2323
- function createRenderStep(runNextFrame) {
2324
- /**
2325
- * We create and reuse two arrays, one to queue jobs for the current frame
2326
- * and one for the next. We reuse to avoid triggering GC after x frames.
2327
- */
2328
- let toRun = [];
2329
- let toRunNextFrame = [];
2330
- /**
2331
- *
2332
- */
2333
- let numToRun = 0;
2334
- /**
2335
- * Track whether we're currently processing jobs in this step. This way
2336
- * we can decide whether to schedule new jobs for this frame or next.
2337
- */
2338
- let isProcessing = false;
2339
- let flushNextFrame = false;
2340
- /**
2341
- * A set of processes which were marked keepAlive when scheduled.
2342
- */
2343
- const toKeepAlive = new WeakSet();
2344
- const step = {
2345
- /**
2346
- * Schedule a process to run on the next frame.
2347
- */
2348
- schedule: (callback, keepAlive = false, immediate = false) => {
2349
- const addToCurrentFrame = immediate && isProcessing;
2350
- const buffer = addToCurrentFrame ? toRun : toRunNextFrame;
2351
- if (keepAlive)
2352
- toKeepAlive.add(callback);
2353
- // If the buffer doesn't already contain this callback, add it
2354
- if (buffer.indexOf(callback) === -1) {
2355
- buffer.push(callback);
2356
- // If we're adding it to the currently running buffer, update its measured size
2357
- if (addToCurrentFrame && isProcessing)
2358
- numToRun = toRun.length;
2359
- }
2360
- return callback;
2361
- },
2362
- /**
2363
- * Cancel the provided callback from running on the next frame.
2364
- */
2365
- cancel: (callback) => {
2366
- const index = toRunNextFrame.indexOf(callback);
2367
- if (index !== -1)
2368
- toRunNextFrame.splice(index, 1);
2369
- toKeepAlive.delete(callback);
2370
- },
2371
- /**
2372
- * Execute all schedule callbacks.
2373
- */
2374
- process: (frameData) => {
2375
- /**
2376
- * If we're already processing we've probably been triggered by a flushSync
2377
- * inside an existing process. Instead of executing, mark flushNextFrame
2378
- * as true and ensure we flush the following frame at the end of this one.
2379
- */
2380
- if (isProcessing) {
2381
- flushNextFrame = true;
2382
- return;
2383
- }
2384
- isProcessing = true;
2385
- [toRun, toRunNextFrame] = [toRunNextFrame, toRun];
2386
- // Clear the next frame list
2387
- toRunNextFrame.length = 0;
2388
- // Execute this frame
2389
- numToRun = toRun.length;
2390
- if (numToRun) {
2391
- for (let i = 0; i < numToRun; i++) {
2392
- const callback = toRun[i];
2393
- callback(frameData);
2394
- if (toKeepAlive.has(callback)) {
2395
- step.schedule(callback);
2396
- runNextFrame();
2397
- }
2398
- }
2399
- }
2400
- isProcessing = false;
2401
- if (flushNextFrame) {
2402
- flushNextFrame = false;
2403
- step.process(frameData);
2404
- }
2405
- },
2406
- };
2407
- return step;
2408
- }
2409
-
2410
- const frameData = {
2411
- delta: 0,
2412
- timestamp: 0,
2413
- };
2414
-
2415
- const maxElapsed$1 = 40;
2416
- let useDefaultElapsed = true;
2417
- let runNextFrame = false;
2418
- let isProcessing = false;
2419
- const stepsOrder = [
2420
- "read",
2421
- "update",
2422
- "preRender",
2423
- "render",
2424
- "postRender",
2425
- ];
2426
- const steps = stepsOrder.reduce((acc, key) => {
2427
- acc[key] = createRenderStep(() => (runNextFrame = true));
2428
- return acc;
2429
- }, {});
2430
- const sync = stepsOrder.reduce((acc, key) => {
2431
- const step = steps[key];
2432
- acc[key] = (process, keepAlive = false, immediate = false) => {
2433
- if (!runNextFrame)
2434
- startLoop();
2435
- return step.schedule(process, keepAlive, immediate);
2436
- };
2437
- return acc;
2438
- }, {});
2439
- const cancelSync = stepsOrder.reduce((acc, key) => {
2440
- acc[key] = steps[key].cancel;
2441
- return acc;
2442
- }, {});
2443
- const flushSync = stepsOrder.reduce((acc, key) => {
2444
- acc[key] = () => steps[key].process(frameData);
2445
- return acc;
2446
- }, {});
2447
- const processStep = (stepId) => steps[stepId].process(frameData);
2448
- const processFrame = (timestamp) => {
2449
- runNextFrame = false;
2450
- frameData.delta = useDefaultElapsed
2451
- ? defaultTimestep
2452
- : Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed$1), 1);
2453
- frameData.timestamp = timestamp;
2454
- isProcessing = true;
2455
- stepsOrder.forEach(processStep);
2456
- isProcessing = false;
2457
- if (runNextFrame) {
2458
- useDefaultElapsed = false;
2459
- onNextFrame(processFrame);
2460
- }
2461
- };
2462
- const startLoop = () => {
2463
- runNextFrame = true;
2464
- useDefaultElapsed = true;
2465
- if (!isProcessing)
2466
- onNextFrame(processFrame);
2467
- };
2468
-
2469
- function delay(callback, timeout) {
2470
- const start = performance.now();
2471
- const checkElapsed = ({ timestamp }) => {
2472
- const elapsed = timestamp - start;
2473
- if (elapsed >= timeout) {
2474
- cancelSync.read(checkElapsed);
2475
- callback(elapsed - timeout);
2476
- }
2477
- };
2478
- sync.read(checkElapsed, true);
2479
- return () => cancelSync.read(checkElapsed);
2480
- }
2481
-
2482
2312
  /*
2483
2313
  Value in range from progress
2484
2314
 
@@ -2857,6 +2687,16 @@
2857
2687
  return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
2858
2688
  }
2859
2689
 
2690
+ /*
2691
+ Convert velocity into velocity per second
2692
+
2693
+ @param [number]: Unit per frame
2694
+ @param [number]: Frame duration in ms
2695
+ */
2696
+ function velocityPerSecond$1(velocity, frameDuration) {
2697
+ return frameDuration ? velocity * (1000 / frameDuration) : 0;
2698
+ }
2699
+
2860
2700
  const durationKeys = ["duration", "bounce"];
2861
2701
  const physicsKeys = ["stiffness", "damping", "mass"];
2862
2702
  function isSpringType(options, keys) {
@@ -2885,6 +2725,7 @@
2885
2725
  }
2886
2726
  return springOptions;
2887
2727
  }
2728
+ const velocitySampleDuration = 5;
2888
2729
  /**
2889
2730
  * This is based on the spring implementation of Wobble https://github.com/skevy/wobble
2890
2731
  */
@@ -2896,11 +2737,10 @@
2896
2737
  const state = { done: false, value: from };
2897
2738
  let { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options);
2898
2739
  let resolveSpring = zero;
2899
- let resolveVelocity = zero;
2740
+ let initialVelocity = velocity ? -(velocity / 1000) : 0.0;
2741
+ const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
2900
2742
  function createSpring() {
2901
- const initialVelocity = velocity ? -(velocity / 1000) : 0.0;
2902
2743
  const initialDelta = to - from;
2903
- const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
2904
2744
  const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000;
2905
2745
  /**
2906
2746
  * If we're working within what looks like a 0-1 range, change the default restDelta
@@ -2922,29 +2762,6 @@
2922
2762
  Math.sin(angularFreq * t) +
2923
2763
  initialDelta * Math.cos(angularFreq * t)));
2924
2764
  };
2925
- resolveVelocity = (t) => {
2926
- // TODO Resolve these calculations with the above
2927
- const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
2928
- return (dampingRatio *
2929
- undampedAngularFreq *
2930
- envelope *
2931
- ((Math.sin(angularFreq * t) *
2932
- (initialVelocity +
2933
- dampingRatio *
2934
- undampedAngularFreq *
2935
- initialDelta)) /
2936
- angularFreq +
2937
- initialDelta * Math.cos(angularFreq * t)) -
2938
- envelope *
2939
- (Math.cos(angularFreq * t) *
2940
- (initialVelocity +
2941
- dampingRatio *
2942
- undampedAngularFreq *
2943
- initialDelta) -
2944
- angularFreq *
2945
- initialDelta *
2946
- Math.sin(angularFreq * t)));
2947
- };
2948
2765
  }
2949
2766
  else if (dampingRatio === 1) {
2950
2767
  // Critically damped spring
@@ -2978,7 +2795,21 @@
2978
2795
  next: (t) => {
2979
2796
  const current = resolveSpring(t);
2980
2797
  if (!isResolvedFromDuration) {
2981
- const currentVelocity = resolveVelocity(t) * 1000;
2798
+ let currentVelocity = initialVelocity;
2799
+ if (t !== 0) {
2800
+ /**
2801
+ * We only need to calculate velocity for under-damped springs
2802
+ * as over- and critically-damped springs can't overshoot, so
2803
+ * checking only for displacement is enough.
2804
+ */
2805
+ if (dampingRatio < 1) {
2806
+ const prevT = Math.max(0, t - velocitySampleDuration);
2807
+ currentVelocity = velocityPerSecond$1(current - resolveSpring(prevT), t - prevT);
2808
+ }
2809
+ else {
2810
+ currentVelocity = 0;
2811
+ }
2812
+ }
2982
2813
  const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
2983
2814
  const isBelowDisplacementThreshold = Math.abs(to - current) <= restDelta;
2984
2815
  state.done =
@@ -2991,7 +2822,7 @@
2991
2822
  return state;
2992
2823
  },
2993
2824
  flipTarget: () => {
2994
- velocity = -velocity;
2825
+ initialVelocity = -initialVelocity;
2995
2826
  [from, to] = [to, from];
2996
2827
  createSpring();
2997
2828
  },
@@ -3026,6 +2857,163 @@
3026
2857
  };
3027
2858
  }
3028
2859
 
2860
+ /*
2861
+ Detect and load appropriate clock setting for the execution environment
2862
+ */
2863
+ const defaultTimestep = (1 / 60) * 1000;
2864
+ const getCurrentTime = typeof performance !== "undefined"
2865
+ ? () => performance.now()
2866
+ : () => Date.now();
2867
+ const onNextFrame = typeof window !== "undefined"
2868
+ ? (callback) => window.requestAnimationFrame(callback)
2869
+ : (callback) => setTimeout(() => callback(getCurrentTime()), defaultTimestep);
2870
+
2871
+ function createRenderStep(runNextFrame) {
2872
+ /**
2873
+ * We create and reuse two arrays, one to queue jobs for the current frame
2874
+ * and one for the next. We reuse to avoid triggering GC after x frames.
2875
+ */
2876
+ let toRun = [];
2877
+ let toRunNextFrame = [];
2878
+ /**
2879
+ *
2880
+ */
2881
+ let numToRun = 0;
2882
+ /**
2883
+ * Track whether we're currently processing jobs in this step. This way
2884
+ * we can decide whether to schedule new jobs for this frame or next.
2885
+ */
2886
+ let isProcessing = false;
2887
+ let flushNextFrame = false;
2888
+ /**
2889
+ * A set of processes which were marked keepAlive when scheduled.
2890
+ */
2891
+ const toKeepAlive = new WeakSet();
2892
+ const step = {
2893
+ /**
2894
+ * Schedule a process to run on the next frame.
2895
+ */
2896
+ schedule: (callback, keepAlive = false, immediate = false) => {
2897
+ const addToCurrentFrame = immediate && isProcessing;
2898
+ const buffer = addToCurrentFrame ? toRun : toRunNextFrame;
2899
+ if (keepAlive)
2900
+ toKeepAlive.add(callback);
2901
+ // If the buffer doesn't already contain this callback, add it
2902
+ if (buffer.indexOf(callback) === -1) {
2903
+ buffer.push(callback);
2904
+ // If we're adding it to the currently running buffer, update its measured size
2905
+ if (addToCurrentFrame && isProcessing)
2906
+ numToRun = toRun.length;
2907
+ }
2908
+ return callback;
2909
+ },
2910
+ /**
2911
+ * Cancel the provided callback from running on the next frame.
2912
+ */
2913
+ cancel: (callback) => {
2914
+ const index = toRunNextFrame.indexOf(callback);
2915
+ if (index !== -1)
2916
+ toRunNextFrame.splice(index, 1);
2917
+ toKeepAlive.delete(callback);
2918
+ },
2919
+ /**
2920
+ * Execute all schedule callbacks.
2921
+ */
2922
+ process: (frameData) => {
2923
+ /**
2924
+ * If we're already processing we've probably been triggered by a flushSync
2925
+ * inside an existing process. Instead of executing, mark flushNextFrame
2926
+ * as true and ensure we flush the following frame at the end of this one.
2927
+ */
2928
+ if (isProcessing) {
2929
+ flushNextFrame = true;
2930
+ return;
2931
+ }
2932
+ isProcessing = true;
2933
+ [toRun, toRunNextFrame] = [toRunNextFrame, toRun];
2934
+ // Clear the next frame list
2935
+ toRunNextFrame.length = 0;
2936
+ // Execute this frame
2937
+ numToRun = toRun.length;
2938
+ if (numToRun) {
2939
+ for (let i = 0; i < numToRun; i++) {
2940
+ const callback = toRun[i];
2941
+ callback(frameData);
2942
+ if (toKeepAlive.has(callback)) {
2943
+ step.schedule(callback);
2944
+ runNextFrame();
2945
+ }
2946
+ }
2947
+ }
2948
+ isProcessing = false;
2949
+ if (flushNextFrame) {
2950
+ flushNextFrame = false;
2951
+ step.process(frameData);
2952
+ }
2953
+ },
2954
+ };
2955
+ return step;
2956
+ }
2957
+
2958
+ const frameData = {
2959
+ delta: 0,
2960
+ timestamp: 0,
2961
+ };
2962
+
2963
+ const maxElapsed$1 = 40;
2964
+ let useDefaultElapsed = true;
2965
+ let runNextFrame = false;
2966
+ let isProcessing = false;
2967
+ const stepsOrder = [
2968
+ "read",
2969
+ "update",
2970
+ "preRender",
2971
+ "render",
2972
+ "postRender",
2973
+ ];
2974
+ const steps = stepsOrder.reduce((acc, key) => {
2975
+ acc[key] = createRenderStep(() => (runNextFrame = true));
2976
+ return acc;
2977
+ }, {});
2978
+ const sync = stepsOrder.reduce((acc, key) => {
2979
+ const step = steps[key];
2980
+ acc[key] = (process, keepAlive = false, immediate = false) => {
2981
+ if (!runNextFrame)
2982
+ startLoop();
2983
+ return step.schedule(process, keepAlive, immediate);
2984
+ };
2985
+ return acc;
2986
+ }, {});
2987
+ const cancelSync = stepsOrder.reduce((acc, key) => {
2988
+ acc[key] = steps[key].cancel;
2989
+ return acc;
2990
+ }, {});
2991
+ const flushSync = stepsOrder.reduce((acc, key) => {
2992
+ acc[key] = () => steps[key].process(frameData);
2993
+ return acc;
2994
+ }, {});
2995
+ const processStep = (stepId) => steps[stepId].process(frameData);
2996
+ const processFrame = (timestamp) => {
2997
+ runNextFrame = false;
2998
+ frameData.delta = useDefaultElapsed
2999
+ ? defaultTimestep
3000
+ : Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed$1), 1);
3001
+ frameData.timestamp = timestamp;
3002
+ isProcessing = true;
3003
+ stepsOrder.forEach(processStep);
3004
+ isProcessing = false;
3005
+ if (runNextFrame) {
3006
+ useDefaultElapsed = false;
3007
+ onNextFrame(processFrame);
3008
+ }
3009
+ };
3010
+ const startLoop = () => {
3011
+ runNextFrame = true;
3012
+ useDefaultElapsed = true;
3013
+ if (!isProcessing)
3014
+ onNextFrame(processFrame);
3015
+ };
3016
+
3029
3017
  const types = { decay, keyframes, spring };
3030
3018
  function loopElapsed(elapsed, duration, delay = 0) {
3031
3019
  return elapsed - duration - delay;
@@ -3094,7 +3082,7 @@
3094
3082
  latest = interpolateFromNumber(latest);
3095
3083
  isComplete = isForwardPlayback ? state.done : elapsed <= 0;
3096
3084
  }
3097
- onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(latest);
3085
+ onUpdate && onUpdate(latest);
3098
3086
  if (isComplete) {
3099
3087
  if (repeatCount === 0) {
3100
3088
  computedDuration =
@@ -3109,29 +3097,19 @@
3109
3097
  }
3110
3098
  }
3111
3099
  function play() {
3112
- onPlay === null || onPlay === void 0 ? void 0 : onPlay();
3100
+ onPlay && onPlay();
3113
3101
  driverControls = driver(update);
3114
3102
  driverControls.start();
3115
3103
  }
3116
3104
  autoplay && play();
3117
3105
  return {
3118
3106
  stop: () => {
3119
- onStop === null || onStop === void 0 ? void 0 : onStop();
3107
+ onStop && onStop();
3120
3108
  driverControls.stop();
3121
3109
  },
3122
3110
  };
3123
3111
  }
3124
3112
 
3125
- /*
3126
- Convert velocity into velocity per second
3127
-
3128
- @param [number]: Unit per frame
3129
- @param [number]: Frame duration in ms
3130
- */
3131
- function velocityPerSecond$1(velocity, frameDuration) {
3132
- return frameDuration ? velocity * (1000 / frameDuration) : 0;
3133
- }
3134
-
3135
3113
  function inertia({ from = 0, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
3136
3114
  let currentAnimation;
3137
3115
  function isOutOfBounds(v) {
@@ -3212,6 +3190,22 @@
3212
3190
  };
3213
3191
  }
3214
3192
 
3193
+ /**
3194
+ * Timeout defined in ms
3195
+ */
3196
+ function delay(callback, timeout) {
3197
+ const start = performance.now();
3198
+ const checkElapsed = ({ timestamp }) => {
3199
+ const elapsed = timestamp - start;
3200
+ if (elapsed >= timeout) {
3201
+ cancelSync.read(checkElapsed);
3202
+ callback(elapsed - timeout);
3203
+ }
3204
+ };
3205
+ sync.read(checkElapsed, true);
3206
+ return () => cancelSync.read(checkElapsed);
3207
+ }
3208
+
3215
3209
  /**
3216
3210
  * Decide whether a transition is defined on a given Transition.
3217
3211
  * This filters out orchestration options and returns true
@@ -3298,6 +3292,9 @@
3298
3292
  */
3299
3293
  function getAnimation(key, value, target, transition, onComplete) {
3300
3294
  const valueTransition = getValueTransition(transition, key) || {};
3295
+ const { elapsed = 0 } = transition;
3296
+ valueTransition.elapsed =
3297
+ elapsed - secondsToMilliseconds(transition.delay || 0);
3301
3298
  let origin = valueTransition.from !== undefined ? valueTransition.from : value.get();
3302
3299
  const isTargetAnimatable = isAnimatable(key, target);
3303
3300
  if (origin === "none" && isTargetAnimatable && typeof target === "string") {
@@ -3325,20 +3322,23 @@
3325
3322
  onComplete,
3326
3323
  onUpdate: (v) => value.set(v),
3327
3324
  };
3328
- return valueTransition.type === "inertia" ||
3325
+ const animation = valueTransition.type === "inertia" ||
3329
3326
  valueTransition.type === "decay"
3330
3327
  ? inertia({ ...options, ...valueTransition })
3331
3328
  : animate$1({
3332
3329
  ...getPopmotionAnimationOptions(valueTransition, options, key),
3333
3330
  onUpdate: (v) => {
3334
3331
  options.onUpdate(v);
3335
- valueTransition.onUpdate && valueTransition.onUpdate(v);
3332
+ valueTransition.onUpdate &&
3333
+ valueTransition.onUpdate(v);
3336
3334
  },
3337
3335
  onComplete: () => {
3338
3336
  options.onComplete();
3339
- valueTransition.onComplete && valueTransition.onComplete();
3337
+ valueTransition.onComplete &&
3338
+ valueTransition.onComplete();
3340
3339
  },
3341
3340
  });
3341
+ return () => animation.stop();
3342
3342
  }
3343
3343
  function set() {
3344
3344
  const finalTarget = resolveFinalValueInKeyframes(target);
@@ -3346,13 +3346,16 @@
3346
3346
  onComplete();
3347
3347
  valueTransition.onUpdate && valueTransition.onUpdate(finalTarget);
3348
3348
  valueTransition.onComplete && valueTransition.onComplete();
3349
- return { stop: () => { } };
3349
+ return () => { };
3350
3350
  }
3351
- return !isOriginAnimatable ||
3351
+ const useInstantAnimation = !isOriginAnimatable ||
3352
3352
  !isTargetAnimatable ||
3353
- valueTransition.type === false
3354
- ? set
3355
- : start;
3353
+ valueTransition.type === false;
3354
+ return useInstantAnimation
3355
+ ? valueTransition.elapsed
3356
+ ? () => delay(set, -valueTransition.elapsed)
3357
+ : set()
3358
+ : start();
3356
3359
  }
3357
3360
  function isZero(value) {
3358
3361
  return (value === 0 ||
@@ -3377,21 +3380,7 @@
3377
3380
  transition = { type: false };
3378
3381
  }
3379
3382
  return value.start((onComplete) => {
3380
- let controls;
3381
- const animation = getAnimation(key, value, target, transition, onComplete);
3382
- const delayBy = getDelayFromTransition(transition, key);
3383
- const start = () => (controls = animation());
3384
- let cancelDelay;
3385
- if (delayBy) {
3386
- cancelDelay = delay(start, secondsToMilliseconds(delayBy));
3387
- }
3388
- else {
3389
- start();
3390
- }
3391
- return () => {
3392
- cancelDelay && cancelDelay();
3393
- controls && controls.stop();
3394
- };
3383
+ return getAnimation(key, value, target, { ...transition, delay: getDelayFromTransition(transition, key) }, onComplete);
3395
3384
  });
3396
3385
  }
3397
3386
 
@@ -3484,7 +3473,7 @@
3484
3473
  * This will be replaced by the build step with the latest version number.
3485
3474
  * When MotionValues are provided to motion components, warn if versions are mixed.
3486
3475
  */
3487
- this.version = "7.7.3";
3476
+ this.version = "7.8.0";
3488
3477
  /**
3489
3478
  * Duration, in milliseconds, since last updating frame.
3490
3479
  *
@@ -3927,6 +3916,40 @@
3927
3916
  return Boolean(isMotionValue(value) && value.add);
3928
3917
  }
3929
3918
 
3919
+ const appearStoreId = (id, value) => `${id}: ${value}`;
3920
+
3921
+ function handoffOptimizedAppearAnimation(id, name) {
3922
+ const { MotionAppearAnimations } = window;
3923
+ const animationId = appearStoreId(id, transformProps.has(name) ? "transform" : name);
3924
+ const animation = MotionAppearAnimations && MotionAppearAnimations.get(animationId);
3925
+ if (animation) {
3926
+ /**
3927
+ * We allow the animation to persist until the next frame:
3928
+ * 1. So it continues to play until Framer Motion is ready to render
3929
+ * (avoiding a potential flash of the element's original state)
3930
+ * 2. As all independent transforms share a single transform animation, stopping
3931
+ * it synchronously would prevent subsequent transforms from handing off.
3932
+ */
3933
+ sync.render(() => {
3934
+ /**
3935
+ * Animation.cancel() throws so it needs to be wrapped in a try/catch
3936
+ */
3937
+ try {
3938
+ animation.cancel();
3939
+ MotionAppearAnimations.delete(animationId);
3940
+ }
3941
+ catch (e) { }
3942
+ });
3943
+ return animation.currentTime || 0;
3944
+ }
3945
+ else {
3946
+ return 0;
3947
+ }
3948
+ }
3949
+
3950
+ const optimizedAppearDataId = "framerAppearId";
3951
+ const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
3952
+
3930
3953
  function animateVisualElement(visualElement, definition, options = {}) {
3931
3954
  visualElement.notify("AnimationStart", definition);
3932
3955
  let animation;
@@ -4004,7 +4027,7 @@
4004
4027
  shouldBlockAnimation(animationTypeState, key))) {
4005
4028
  continue;
4006
4029
  }
4007
- let valueTransition = { delay, ...transition };
4030
+ let valueTransition = { delay, elapsed: 0, ...transition };
4008
4031
  /**
4009
4032
  * Make animation instant if this is a transform prop and we should reduce motion.
4010
4033
  */
@@ -4015,6 +4038,16 @@
4015
4038
  delay: 0,
4016
4039
  };
4017
4040
  }
4041
+ /**
4042
+ * If this is the first time a value is being animated, check
4043
+ * to see if we're handling off from an existing animation.
4044
+ */
4045
+ if (!value.hasAnimated) {
4046
+ const appearId = visualElement.getProps()[optimizedAppearDataAttribute];
4047
+ if (appearId) {
4048
+ valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key);
4049
+ }
4050
+ }
4018
4051
  let animation = startAnimation(key, value, valueTarget, valueTransition);
4019
4052
  if (isWillChangeMotionValue(willChange)) {
4020
4053
  willChange.add(key);
@@ -5766,7 +5799,7 @@
5766
5799
  * and warn against mismatches.
5767
5800
  */
5768
5801
  {
5769
- warnOnce(nextValue.version === "7.7.3", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.7.3 may not work as expected.`);
5802
+ warnOnce(nextValue.version === "7.8.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.8.0 may not work as expected.`);
5770
5803
  }
5771
5804
  }
5772
5805
  else if (isMotionValue(prevValue)) {
@@ -10194,6 +10227,46 @@
10194
10227
  return reset;
10195
10228
  }
10196
10229
 
10230
+ const featureTests = {
10231
+ waapi: () => Object.hasOwnProperty.call(Element.prototype, "animate"),
10232
+ };
10233
+ const results = {};
10234
+ const supports = {};
10235
+ /**
10236
+ * Generate features tests that cache their results.
10237
+ */
10238
+ for (const key in featureTests) {
10239
+ supports[key] = () => {
10240
+ if (results[key] === undefined)
10241
+ results[key] = featureTests[key]();
10242
+ return results[key];
10243
+ };
10244
+ }
10245
+
10246
+ const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
10247
+
10248
+ function animateStyle(element, valueName, keyframes, { delay, duration, ease }) {
10249
+ if (!supports.waapi())
10250
+ return undefined;
10251
+ const animation = element.animate({ [valueName]: keyframes }, {
10252
+ delay,
10253
+ duration,
10254
+ easing: Array.isArray(ease) ? cubicBezierAsString(ease) : ease,
10255
+ fill: "both",
10256
+ });
10257
+ return animation;
10258
+ }
10259
+
10260
+ function startOptimizedAppearAnimation(element, name, keyframes, options) {
10261
+ window.MotionAppearAnimations || (window.MotionAppearAnimations = new Map());
10262
+ const id = element.dataset[optimizedAppearDataId];
10263
+ const animation = animateStyle(element, name, keyframes, options);
10264
+ if (id && animation) {
10265
+ window.MotionAppearAnimations.set(appearStoreId(id, name), animation);
10266
+ }
10267
+ return animation;
10268
+ }
10269
+
10197
10270
  const createObject = () => ({});
10198
10271
  class StateVisualElement extends VisualElement {
10199
10272
  build() { }
@@ -10343,8 +10416,11 @@
10343
10416
  exports.mix = mix$1;
10344
10417
  exports.motion = motion;
10345
10418
  exports.motionValue = motionValue;
10419
+ exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute;
10346
10420
  exports.pipe = pipe;
10347
10421
  exports.resolveMotionValue = resolveMotionValue;
10422
+ exports.spring = spring;
10423
+ exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation;
10348
10424
  exports.transform = transform;
10349
10425
  exports.unwrapMotionComponent = unwrapMotionComponent;
10350
10426
  exports.useAnimation = useAnimation;