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.
package/dist/cjs/index.js CHANGED
@@ -2296,176 +2296,6 @@ const instantAnimationState = {
2296
2296
  current: false,
2297
2297
  };
2298
2298
 
2299
- /*
2300
- Detect and load appropriate clock setting for the execution environment
2301
- */
2302
- const defaultTimestep = (1 / 60) * 1000;
2303
- const getCurrentTime = typeof performance !== "undefined"
2304
- ? () => performance.now()
2305
- : () => Date.now();
2306
- const onNextFrame = typeof window !== "undefined"
2307
- ? (callback) => window.requestAnimationFrame(callback)
2308
- : (callback) => setTimeout(() => callback(getCurrentTime()), defaultTimestep);
2309
-
2310
- function createRenderStep(runNextFrame) {
2311
- /**
2312
- * We create and reuse two arrays, one to queue jobs for the current frame
2313
- * and one for the next. We reuse to avoid triggering GC after x frames.
2314
- */
2315
- let toRun = [];
2316
- let toRunNextFrame = [];
2317
- /**
2318
- *
2319
- */
2320
- let numToRun = 0;
2321
- /**
2322
- * Track whether we're currently processing jobs in this step. This way
2323
- * we can decide whether to schedule new jobs for this frame or next.
2324
- */
2325
- let isProcessing = false;
2326
- let flushNextFrame = false;
2327
- /**
2328
- * A set of processes which were marked keepAlive when scheduled.
2329
- */
2330
- const toKeepAlive = new WeakSet();
2331
- const step = {
2332
- /**
2333
- * Schedule a process to run on the next frame.
2334
- */
2335
- schedule: (callback, keepAlive = false, immediate = false) => {
2336
- const addToCurrentFrame = immediate && isProcessing;
2337
- const buffer = addToCurrentFrame ? toRun : toRunNextFrame;
2338
- if (keepAlive)
2339
- toKeepAlive.add(callback);
2340
- // If the buffer doesn't already contain this callback, add it
2341
- if (buffer.indexOf(callback) === -1) {
2342
- buffer.push(callback);
2343
- // If we're adding it to the currently running buffer, update its measured size
2344
- if (addToCurrentFrame && isProcessing)
2345
- numToRun = toRun.length;
2346
- }
2347
- return callback;
2348
- },
2349
- /**
2350
- * Cancel the provided callback from running on the next frame.
2351
- */
2352
- cancel: (callback) => {
2353
- const index = toRunNextFrame.indexOf(callback);
2354
- if (index !== -1)
2355
- toRunNextFrame.splice(index, 1);
2356
- toKeepAlive.delete(callback);
2357
- },
2358
- /**
2359
- * Execute all schedule callbacks.
2360
- */
2361
- process: (frameData) => {
2362
- /**
2363
- * If we're already processing we've probably been triggered by a flushSync
2364
- * inside an existing process. Instead of executing, mark flushNextFrame
2365
- * as true and ensure we flush the following frame at the end of this one.
2366
- */
2367
- if (isProcessing) {
2368
- flushNextFrame = true;
2369
- return;
2370
- }
2371
- isProcessing = true;
2372
- [toRun, toRunNextFrame] = [toRunNextFrame, toRun];
2373
- // Clear the next frame list
2374
- toRunNextFrame.length = 0;
2375
- // Execute this frame
2376
- numToRun = toRun.length;
2377
- if (numToRun) {
2378
- for (let i = 0; i < numToRun; i++) {
2379
- const callback = toRun[i];
2380
- callback(frameData);
2381
- if (toKeepAlive.has(callback)) {
2382
- step.schedule(callback);
2383
- runNextFrame();
2384
- }
2385
- }
2386
- }
2387
- isProcessing = false;
2388
- if (flushNextFrame) {
2389
- flushNextFrame = false;
2390
- step.process(frameData);
2391
- }
2392
- },
2393
- };
2394
- return step;
2395
- }
2396
-
2397
- const frameData = {
2398
- delta: 0,
2399
- timestamp: 0,
2400
- };
2401
-
2402
- const maxElapsed = 40;
2403
- let useDefaultElapsed = true;
2404
- let runNextFrame = false;
2405
- let isProcessing = false;
2406
- const stepsOrder = [
2407
- "read",
2408
- "update",
2409
- "preRender",
2410
- "render",
2411
- "postRender",
2412
- ];
2413
- const steps = stepsOrder.reduce((acc, key) => {
2414
- acc[key] = createRenderStep(() => (runNextFrame = true));
2415
- return acc;
2416
- }, {});
2417
- const sync = stepsOrder.reduce((acc, key) => {
2418
- const step = steps[key];
2419
- acc[key] = (process, keepAlive = false, immediate = false) => {
2420
- if (!runNextFrame)
2421
- startLoop();
2422
- return step.schedule(process, keepAlive, immediate);
2423
- };
2424
- return acc;
2425
- }, {});
2426
- const cancelSync = stepsOrder.reduce((acc, key) => {
2427
- acc[key] = steps[key].cancel;
2428
- return acc;
2429
- }, {});
2430
- const flushSync = stepsOrder.reduce((acc, key) => {
2431
- acc[key] = () => steps[key].process(frameData);
2432
- return acc;
2433
- }, {});
2434
- const processStep = (stepId) => steps[stepId].process(frameData);
2435
- const processFrame = (timestamp) => {
2436
- runNextFrame = false;
2437
- frameData.delta = useDefaultElapsed
2438
- ? defaultTimestep
2439
- : Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed), 1);
2440
- frameData.timestamp = timestamp;
2441
- isProcessing = true;
2442
- stepsOrder.forEach(processStep);
2443
- isProcessing = false;
2444
- if (runNextFrame) {
2445
- useDefaultElapsed = false;
2446
- onNextFrame(processFrame);
2447
- }
2448
- };
2449
- const startLoop = () => {
2450
- runNextFrame = true;
2451
- useDefaultElapsed = true;
2452
- if (!isProcessing)
2453
- onNextFrame(processFrame);
2454
- };
2455
-
2456
- function delay(callback, timeout) {
2457
- const start = performance.now();
2458
- const checkElapsed = ({ timestamp }) => {
2459
- const elapsed = timestamp - start;
2460
- if (elapsed >= timeout) {
2461
- cancelSync.read(checkElapsed);
2462
- callback(elapsed - timeout);
2463
- }
2464
- };
2465
- sync.read(checkElapsed, true);
2466
- return () => cancelSync.read(checkElapsed);
2467
- }
2468
-
2469
2299
  /*
2470
2300
  Value in range from progress
2471
2301
 
@@ -2844,6 +2674,16 @@ function calcAngularFreq(undampedFreq, dampingRatio) {
2844
2674
  return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
2845
2675
  }
2846
2676
 
2677
+ /*
2678
+ Convert velocity into velocity per second
2679
+
2680
+ @param [number]: Unit per frame
2681
+ @param [number]: Frame duration in ms
2682
+ */
2683
+ function velocityPerSecond(velocity, frameDuration) {
2684
+ return frameDuration ? velocity * (1000 / frameDuration) : 0;
2685
+ }
2686
+
2847
2687
  const durationKeys = ["duration", "bounce"];
2848
2688
  const physicsKeys = ["stiffness", "damping", "mass"];
2849
2689
  function isSpringType(options, keys) {
@@ -2872,6 +2712,7 @@ function getSpringOptions(options) {
2872
2712
  }
2873
2713
  return springOptions;
2874
2714
  }
2715
+ const velocitySampleDuration = 5;
2875
2716
  /**
2876
2717
  * This is based on the spring implementation of Wobble https://github.com/skevy/wobble
2877
2718
  */
@@ -2883,11 +2724,10 @@ function spring({ from = 0.0, to = 1.0, restSpeed = 2, restDelta = 0.01, ...opti
2883
2724
  const state = { done: false, value: from };
2884
2725
  let { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options);
2885
2726
  let resolveSpring = zero;
2886
- let resolveVelocity = zero;
2727
+ let initialVelocity = velocity ? -(velocity / 1000) : 0.0;
2728
+ const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
2887
2729
  function createSpring() {
2888
- const initialVelocity = velocity ? -(velocity / 1000) : 0.0;
2889
2730
  const initialDelta = to - from;
2890
- const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
2891
2731
  const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000;
2892
2732
  /**
2893
2733
  * If we're working within what looks like a 0-1 range, change the default restDelta
@@ -2909,29 +2749,6 @@ function spring({ from = 0.0, to = 1.0, restSpeed = 2, restDelta = 0.01, ...opti
2909
2749
  Math.sin(angularFreq * t) +
2910
2750
  initialDelta * Math.cos(angularFreq * t)));
2911
2751
  };
2912
- resolveVelocity = (t) => {
2913
- // TODO Resolve these calculations with the above
2914
- const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
2915
- return (dampingRatio *
2916
- undampedAngularFreq *
2917
- envelope *
2918
- ((Math.sin(angularFreq * t) *
2919
- (initialVelocity +
2920
- dampingRatio *
2921
- undampedAngularFreq *
2922
- initialDelta)) /
2923
- angularFreq +
2924
- initialDelta * Math.cos(angularFreq * t)) -
2925
- envelope *
2926
- (Math.cos(angularFreq * t) *
2927
- (initialVelocity +
2928
- dampingRatio *
2929
- undampedAngularFreq *
2930
- initialDelta) -
2931
- angularFreq *
2932
- initialDelta *
2933
- Math.sin(angularFreq * t)));
2934
- };
2935
2752
  }
2936
2753
  else if (dampingRatio === 1) {
2937
2754
  // Critically damped spring
@@ -2965,7 +2782,21 @@ function spring({ from = 0.0, to = 1.0, restSpeed = 2, restDelta = 0.01, ...opti
2965
2782
  next: (t) => {
2966
2783
  const current = resolveSpring(t);
2967
2784
  if (!isResolvedFromDuration) {
2968
- const currentVelocity = resolveVelocity(t) * 1000;
2785
+ let currentVelocity = initialVelocity;
2786
+ if (t !== 0) {
2787
+ /**
2788
+ * We only need to calculate velocity for under-damped springs
2789
+ * as over- and critically-damped springs can't overshoot, so
2790
+ * checking only for displacement is enough.
2791
+ */
2792
+ if (dampingRatio < 1) {
2793
+ const prevT = Math.max(0, t - velocitySampleDuration);
2794
+ currentVelocity = velocityPerSecond(current - resolveSpring(prevT), t - prevT);
2795
+ }
2796
+ else {
2797
+ currentVelocity = 0;
2798
+ }
2799
+ }
2969
2800
  const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
2970
2801
  const isBelowDisplacementThreshold = Math.abs(to - current) <= restDelta;
2971
2802
  state.done =
@@ -2978,7 +2809,7 @@ function spring({ from = 0.0, to = 1.0, restSpeed = 2, restDelta = 0.01, ...opti
2978
2809
  return state;
2979
2810
  },
2980
2811
  flipTarget: () => {
2981
- velocity = -velocity;
2812
+ initialVelocity = -initialVelocity;
2982
2813
  [from, to] = [to, from];
2983
2814
  createSpring();
2984
2815
  },
@@ -3013,6 +2844,163 @@ function decay({ velocity = 0, from = 0, power = 0.8, timeConstant = 350, restDe
3013
2844
  };
3014
2845
  }
3015
2846
 
2847
+ /*
2848
+ Detect and load appropriate clock setting for the execution environment
2849
+ */
2850
+ const defaultTimestep = (1 / 60) * 1000;
2851
+ const getCurrentTime = typeof performance !== "undefined"
2852
+ ? () => performance.now()
2853
+ : () => Date.now();
2854
+ const onNextFrame = typeof window !== "undefined"
2855
+ ? (callback) => window.requestAnimationFrame(callback)
2856
+ : (callback) => setTimeout(() => callback(getCurrentTime()), defaultTimestep);
2857
+
2858
+ function createRenderStep(runNextFrame) {
2859
+ /**
2860
+ * We create and reuse two arrays, one to queue jobs for the current frame
2861
+ * and one for the next. We reuse to avoid triggering GC after x frames.
2862
+ */
2863
+ let toRun = [];
2864
+ let toRunNextFrame = [];
2865
+ /**
2866
+ *
2867
+ */
2868
+ let numToRun = 0;
2869
+ /**
2870
+ * Track whether we're currently processing jobs in this step. This way
2871
+ * we can decide whether to schedule new jobs for this frame or next.
2872
+ */
2873
+ let isProcessing = false;
2874
+ let flushNextFrame = false;
2875
+ /**
2876
+ * A set of processes which were marked keepAlive when scheduled.
2877
+ */
2878
+ const toKeepAlive = new WeakSet();
2879
+ const step = {
2880
+ /**
2881
+ * Schedule a process to run on the next frame.
2882
+ */
2883
+ schedule: (callback, keepAlive = false, immediate = false) => {
2884
+ const addToCurrentFrame = immediate && isProcessing;
2885
+ const buffer = addToCurrentFrame ? toRun : toRunNextFrame;
2886
+ if (keepAlive)
2887
+ toKeepAlive.add(callback);
2888
+ // If the buffer doesn't already contain this callback, add it
2889
+ if (buffer.indexOf(callback) === -1) {
2890
+ buffer.push(callback);
2891
+ // If we're adding it to the currently running buffer, update its measured size
2892
+ if (addToCurrentFrame && isProcessing)
2893
+ numToRun = toRun.length;
2894
+ }
2895
+ return callback;
2896
+ },
2897
+ /**
2898
+ * Cancel the provided callback from running on the next frame.
2899
+ */
2900
+ cancel: (callback) => {
2901
+ const index = toRunNextFrame.indexOf(callback);
2902
+ if (index !== -1)
2903
+ toRunNextFrame.splice(index, 1);
2904
+ toKeepAlive.delete(callback);
2905
+ },
2906
+ /**
2907
+ * Execute all schedule callbacks.
2908
+ */
2909
+ process: (frameData) => {
2910
+ /**
2911
+ * If we're already processing we've probably been triggered by a flushSync
2912
+ * inside an existing process. Instead of executing, mark flushNextFrame
2913
+ * as true and ensure we flush the following frame at the end of this one.
2914
+ */
2915
+ if (isProcessing) {
2916
+ flushNextFrame = true;
2917
+ return;
2918
+ }
2919
+ isProcessing = true;
2920
+ [toRun, toRunNextFrame] = [toRunNextFrame, toRun];
2921
+ // Clear the next frame list
2922
+ toRunNextFrame.length = 0;
2923
+ // Execute this frame
2924
+ numToRun = toRun.length;
2925
+ if (numToRun) {
2926
+ for (let i = 0; i < numToRun; i++) {
2927
+ const callback = toRun[i];
2928
+ callback(frameData);
2929
+ if (toKeepAlive.has(callback)) {
2930
+ step.schedule(callback);
2931
+ runNextFrame();
2932
+ }
2933
+ }
2934
+ }
2935
+ isProcessing = false;
2936
+ if (flushNextFrame) {
2937
+ flushNextFrame = false;
2938
+ step.process(frameData);
2939
+ }
2940
+ },
2941
+ };
2942
+ return step;
2943
+ }
2944
+
2945
+ const frameData = {
2946
+ delta: 0,
2947
+ timestamp: 0,
2948
+ };
2949
+
2950
+ const maxElapsed = 40;
2951
+ let useDefaultElapsed = true;
2952
+ let runNextFrame = false;
2953
+ let isProcessing = false;
2954
+ const stepsOrder = [
2955
+ "read",
2956
+ "update",
2957
+ "preRender",
2958
+ "render",
2959
+ "postRender",
2960
+ ];
2961
+ const steps = stepsOrder.reduce((acc, key) => {
2962
+ acc[key] = createRenderStep(() => (runNextFrame = true));
2963
+ return acc;
2964
+ }, {});
2965
+ const sync = stepsOrder.reduce((acc, key) => {
2966
+ const step = steps[key];
2967
+ acc[key] = (process, keepAlive = false, immediate = false) => {
2968
+ if (!runNextFrame)
2969
+ startLoop();
2970
+ return step.schedule(process, keepAlive, immediate);
2971
+ };
2972
+ return acc;
2973
+ }, {});
2974
+ const cancelSync = stepsOrder.reduce((acc, key) => {
2975
+ acc[key] = steps[key].cancel;
2976
+ return acc;
2977
+ }, {});
2978
+ const flushSync = stepsOrder.reduce((acc, key) => {
2979
+ acc[key] = () => steps[key].process(frameData);
2980
+ return acc;
2981
+ }, {});
2982
+ const processStep = (stepId) => steps[stepId].process(frameData);
2983
+ const processFrame = (timestamp) => {
2984
+ runNextFrame = false;
2985
+ frameData.delta = useDefaultElapsed
2986
+ ? defaultTimestep
2987
+ : Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed), 1);
2988
+ frameData.timestamp = timestamp;
2989
+ isProcessing = true;
2990
+ stepsOrder.forEach(processStep);
2991
+ isProcessing = false;
2992
+ if (runNextFrame) {
2993
+ useDefaultElapsed = false;
2994
+ onNextFrame(processFrame);
2995
+ }
2996
+ };
2997
+ const startLoop = () => {
2998
+ runNextFrame = true;
2999
+ useDefaultElapsed = true;
3000
+ if (!isProcessing)
3001
+ onNextFrame(processFrame);
3002
+ };
3003
+
3016
3004
  const types = { decay, keyframes, spring };
3017
3005
  function loopElapsed(elapsed, duration, delay = 0) {
3018
3006
  return elapsed - duration - delay;
@@ -3081,7 +3069,7 @@ function animate$1({ from, autoplay = true, driver = framesync, elapsed = 0, rep
3081
3069
  latest = interpolateFromNumber(latest);
3082
3070
  isComplete = isForwardPlayback ? state.done : elapsed <= 0;
3083
3071
  }
3084
- onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(latest);
3072
+ onUpdate && onUpdate(latest);
3085
3073
  if (isComplete) {
3086
3074
  if (repeatCount === 0) {
3087
3075
  computedDuration =
@@ -3096,29 +3084,19 @@ function animate$1({ from, autoplay = true, driver = framesync, elapsed = 0, rep
3096
3084
  }
3097
3085
  }
3098
3086
  function play() {
3099
- onPlay === null || onPlay === void 0 ? void 0 : onPlay();
3087
+ onPlay && onPlay();
3100
3088
  driverControls = driver(update);
3101
3089
  driverControls.start();
3102
3090
  }
3103
3091
  autoplay && play();
3104
3092
  return {
3105
3093
  stop: () => {
3106
- onStop === null || onStop === void 0 ? void 0 : onStop();
3094
+ onStop && onStop();
3107
3095
  driverControls.stop();
3108
3096
  },
3109
3097
  };
3110
3098
  }
3111
3099
 
3112
- /*
3113
- Convert velocity into velocity per second
3114
-
3115
- @param [number]: Unit per frame
3116
- @param [number]: Frame duration in ms
3117
- */
3118
- function velocityPerSecond(velocity, frameDuration) {
3119
- return frameDuration ? velocity * (1000 / frameDuration) : 0;
3120
- }
3121
-
3122
3100
  function inertia({ from = 0, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
3123
3101
  let currentAnimation;
3124
3102
  function isOutOfBounds(v) {
@@ -3199,6 +3177,22 @@ function inertia({ from = 0, velocity = 0, min, max, power = 0.8, timeConstant =
3199
3177
  };
3200
3178
  }
3201
3179
 
3180
+ /**
3181
+ * Timeout defined in ms
3182
+ */
3183
+ function delay(callback, timeout) {
3184
+ const start = performance.now();
3185
+ const checkElapsed = ({ timestamp }) => {
3186
+ const elapsed = timestamp - start;
3187
+ if (elapsed >= timeout) {
3188
+ cancelSync.read(checkElapsed);
3189
+ callback(elapsed - timeout);
3190
+ }
3191
+ };
3192
+ sync.read(checkElapsed, true);
3193
+ return () => cancelSync.read(checkElapsed);
3194
+ }
3195
+
3202
3196
  /**
3203
3197
  * Decide whether a transition is defined on a given Transition.
3204
3198
  * This filters out orchestration options and returns true
@@ -3285,6 +3279,9 @@ function getPopmotionAnimationOptions(transition, options, key) {
3285
3279
  */
3286
3280
  function getAnimation(key, value, target, transition, onComplete) {
3287
3281
  const valueTransition = getValueTransition(transition, key) || {};
3282
+ const { elapsed = 0 } = transition;
3283
+ valueTransition.elapsed =
3284
+ elapsed - secondsToMilliseconds(transition.delay || 0);
3288
3285
  let origin = valueTransition.from !== undefined ? valueTransition.from : value.get();
3289
3286
  const isTargetAnimatable = isAnimatable(key, target);
3290
3287
  if (origin === "none" && isTargetAnimatable && typeof target === "string") {
@@ -3312,20 +3309,23 @@ function getAnimation(key, value, target, transition, onComplete) {
3312
3309
  onComplete,
3313
3310
  onUpdate: (v) => value.set(v),
3314
3311
  };
3315
- return valueTransition.type === "inertia" ||
3312
+ const animation = valueTransition.type === "inertia" ||
3316
3313
  valueTransition.type === "decay"
3317
3314
  ? inertia({ ...options, ...valueTransition })
3318
3315
  : animate$1({
3319
3316
  ...getPopmotionAnimationOptions(valueTransition, options, key),
3320
3317
  onUpdate: (v) => {
3321
3318
  options.onUpdate(v);
3322
- valueTransition.onUpdate && valueTransition.onUpdate(v);
3319
+ valueTransition.onUpdate &&
3320
+ valueTransition.onUpdate(v);
3323
3321
  },
3324
3322
  onComplete: () => {
3325
3323
  options.onComplete();
3326
- valueTransition.onComplete && valueTransition.onComplete();
3324
+ valueTransition.onComplete &&
3325
+ valueTransition.onComplete();
3327
3326
  },
3328
3327
  });
3328
+ return () => animation.stop();
3329
3329
  }
3330
3330
  function set() {
3331
3331
  const finalTarget = resolveFinalValueInKeyframes(target);
@@ -3333,13 +3333,16 @@ function getAnimation(key, value, target, transition, onComplete) {
3333
3333
  onComplete();
3334
3334
  valueTransition.onUpdate && valueTransition.onUpdate(finalTarget);
3335
3335
  valueTransition.onComplete && valueTransition.onComplete();
3336
- return { stop: () => { } };
3336
+ return () => { };
3337
3337
  }
3338
- return !isOriginAnimatable ||
3338
+ const useInstantAnimation = !isOriginAnimatable ||
3339
3339
  !isTargetAnimatable ||
3340
- valueTransition.type === false
3341
- ? set
3342
- : start;
3340
+ valueTransition.type === false;
3341
+ return useInstantAnimation
3342
+ ? valueTransition.elapsed
3343
+ ? () => delay(set, -valueTransition.elapsed)
3344
+ : set()
3345
+ : start();
3343
3346
  }
3344
3347
  function isZero(value) {
3345
3348
  return (value === 0 ||
@@ -3364,21 +3367,7 @@ function startAnimation(key, value, target, transition = {}) {
3364
3367
  transition = { type: false };
3365
3368
  }
3366
3369
  return value.start((onComplete) => {
3367
- let controls;
3368
- const animation = getAnimation(key, value, target, transition, onComplete);
3369
- const delayBy = getDelayFromTransition(transition, key);
3370
- const start = () => (controls = animation());
3371
- let cancelDelay;
3372
- if (delayBy) {
3373
- cancelDelay = delay(start, secondsToMilliseconds(delayBy));
3374
- }
3375
- else {
3376
- start();
3377
- }
3378
- return () => {
3379
- cancelDelay && cancelDelay();
3380
- controls && controls.stop();
3381
- };
3370
+ return getAnimation(key, value, target, { ...transition, delay: getDelayFromTransition(transition, key) }, onComplete);
3382
3371
  });
3383
3372
  }
3384
3373
 
@@ -3471,7 +3460,7 @@ class MotionValue {
3471
3460
  * This will be replaced by the build step with the latest version number.
3472
3461
  * When MotionValues are provided to motion components, warn if versions are mixed.
3473
3462
  */
3474
- this.version = "7.7.3";
3463
+ this.version = "7.8.0";
3475
3464
  /**
3476
3465
  * Duration, in milliseconds, since last updating frame.
3477
3466
  *
@@ -3914,6 +3903,40 @@ function isWillChangeMotionValue(value) {
3914
3903
  return Boolean(isMotionValue(value) && value.add);
3915
3904
  }
3916
3905
 
3906
+ const appearStoreId = (id, value) => `${id}: ${value}`;
3907
+
3908
+ function handoffOptimizedAppearAnimation(id, name) {
3909
+ const { MotionAppearAnimations } = window;
3910
+ const animationId = appearStoreId(id, transformProps.has(name) ? "transform" : name);
3911
+ const animation = MotionAppearAnimations && MotionAppearAnimations.get(animationId);
3912
+ if (animation) {
3913
+ /**
3914
+ * We allow the animation to persist until the next frame:
3915
+ * 1. So it continues to play until Framer Motion is ready to render
3916
+ * (avoiding a potential flash of the element's original state)
3917
+ * 2. As all independent transforms share a single transform animation, stopping
3918
+ * it synchronously would prevent subsequent transforms from handing off.
3919
+ */
3920
+ sync.render(() => {
3921
+ /**
3922
+ * Animation.cancel() throws so it needs to be wrapped in a try/catch
3923
+ */
3924
+ try {
3925
+ animation.cancel();
3926
+ MotionAppearAnimations.delete(animationId);
3927
+ }
3928
+ catch (e) { }
3929
+ });
3930
+ return animation.currentTime || 0;
3931
+ }
3932
+ else {
3933
+ return 0;
3934
+ }
3935
+ }
3936
+
3937
+ const optimizedAppearDataId = "framerAppearId";
3938
+ const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
3939
+
3917
3940
  function animateVisualElement(visualElement, definition, options = {}) {
3918
3941
  visualElement.notify("AnimationStart", definition);
3919
3942
  let animation;
@@ -3991,7 +4014,7 @@ function animateTarget(visualElement, definition, { delay = 0, transitionOverrid
3991
4014
  shouldBlockAnimation(animationTypeState, key))) {
3992
4015
  continue;
3993
4016
  }
3994
- let valueTransition = { delay, ...transition };
4017
+ let valueTransition = { delay, elapsed: 0, ...transition };
3995
4018
  /**
3996
4019
  * Make animation instant if this is a transform prop and we should reduce motion.
3997
4020
  */
@@ -4002,6 +4025,16 @@ function animateTarget(visualElement, definition, { delay = 0, transitionOverrid
4002
4025
  delay: 0,
4003
4026
  };
4004
4027
  }
4028
+ /**
4029
+ * If this is the first time a value is being animated, check
4030
+ * to see if we're handling off from an existing animation.
4031
+ */
4032
+ if (!value.hasAnimated) {
4033
+ const appearId = visualElement.getProps()[optimizedAppearDataAttribute];
4034
+ if (appearId) {
4035
+ valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key);
4036
+ }
4037
+ }
4005
4038
  let animation = startAnimation(key, value, valueTarget, valueTransition);
4006
4039
  if (isWillChangeMotionValue(willChange)) {
4007
4040
  willChange.add(key);
@@ -5753,7 +5786,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5753
5786
  * and warn against mismatches.
5754
5787
  */
5755
5788
  if (process.env.NODE_ENV === "development") {
5756
- warnOnce(nextValue.version === "7.7.3", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.7.3 may not work as expected.`);
5789
+ warnOnce(nextValue.version === "7.8.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.8.0 may not work as expected.`);
5757
5790
  }
5758
5791
  }
5759
5792
  else if (isMotionValue(prevValue)) {
@@ -9573,6 +9606,46 @@ function useResetProjection() {
9573
9606
  return reset;
9574
9607
  }
9575
9608
 
9609
+ const featureTests = {
9610
+ waapi: () => Object.hasOwnProperty.call(Element.prototype, "animate"),
9611
+ };
9612
+ const results = {};
9613
+ const supports = {};
9614
+ /**
9615
+ * Generate features tests that cache their results.
9616
+ */
9617
+ for (const key in featureTests) {
9618
+ supports[key] = () => {
9619
+ if (results[key] === undefined)
9620
+ results[key] = featureTests[key]();
9621
+ return results[key];
9622
+ };
9623
+ }
9624
+
9625
+ const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
9626
+
9627
+ function animateStyle(element, valueName, keyframes, { delay, duration, ease }) {
9628
+ if (!supports.waapi())
9629
+ return undefined;
9630
+ const animation = element.animate({ [valueName]: keyframes }, {
9631
+ delay,
9632
+ duration,
9633
+ easing: Array.isArray(ease) ? cubicBezierAsString(ease) : ease,
9634
+ fill: "both",
9635
+ });
9636
+ return animation;
9637
+ }
9638
+
9639
+ function startOptimizedAppearAnimation(element, name, keyframes, options) {
9640
+ window.MotionAppearAnimations || (window.MotionAppearAnimations = new Map());
9641
+ const id = element.dataset[optimizedAppearDataId];
9642
+ const animation = animateStyle(element, name, keyframes, options);
9643
+ if (id && animation) {
9644
+ window.MotionAppearAnimations.set(appearStoreId(id, name), animation);
9645
+ }
9646
+ return animation;
9647
+ }
9648
+
9576
9649
  const createObject = () => ({});
9577
9650
  class StateVisualElement extends VisualElement {
9578
9651
  build() { }
@@ -9722,8 +9795,11 @@ exports.makeUseVisualState = makeUseVisualState;
9722
9795
  exports.mix = mix;
9723
9796
  exports.motion = motion;
9724
9797
  exports.motionValue = motionValue;
9798
+ exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute;
9725
9799
  exports.pipe = pipe;
9726
9800
  exports.resolveMotionValue = resolveMotionValue;
9801
+ exports.spring = spring;
9802
+ exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation;
9727
9803
  exports.transform = transform;
9728
9804
  exports.unwrapMotionComponent = unwrapMotionComponent;
9729
9805
  exports.useAnimation = useAnimation;