framer-motion 11.11.17 → 11.12.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/README.md CHANGED
@@ -38,14 +38,14 @@ It does all this:
38
38
 
39
39
  - [Springs](https://motion.dev/docs/react-transitions#spring)
40
40
  - [Keyframes](https://motion.dev/docs/react-animation#keyframes)
41
- - [Layout animations](https://motion/dev/docs/react-layout-animations)
41
+ - [Layout animations](https://motion.dev/docs/react-layout-animations)
42
42
  - [Shared layout animations](https://motion.dev/docs/react-layout-animations#shared-layout-animations)
43
43
  - [Gestures (drag/tap/hover)](https://motion.dev/docs/react-gestures)
44
44
  - [Scroll animations](https://motion.dev/docs/react-scroll-animations)
45
45
  - [SVG paths](https://motion.dev/docs/react-animation#svg-line-drawing)
46
46
  - [Exit animations](https://motion.dev/docs/react-animation#exit-animations)
47
- - [Server-side rendering](https://motion.dev//docs/react-motion-component#server-side-rendering)
48
- - [Independent transforms](https://motion.dev/docs/react-motion-component#independent-transforms)
47
+ - [Server-side rendering](https://motion.dev/docs/react-motion-component#server-side-rendering)
48
+ - [Independent transforms](https://motion.dev/docs/react-motion-component#style)
49
49
  - [Orchestrate animations across components](https://motion.dev/docs/react-animation#orchestration)
50
50
  - [CSS variables](https://motion.dev/docs/react-animation#css-variables)
51
51
 
@@ -101,6 +101,10 @@ Motion powers Framer animations, the web builder for creative pros. Design and s
101
101
 
102
102
  <a href="https://linear.app"><img alt="Linear" src="https://github.com/user-attachments/assets/a93710bb-d8ed-40e3-b0fb-1c5b3e2b16bb" width="300px" height="200px"></a>
103
103
 
104
+ ### Gold
105
+
106
+ <a href="https://liveblocks.io"><img alt="Liveblocks" src="https://github.com/user-attachments/assets/31436a47-951e-4eab-9a68-bdd54ccf9444" width="225px" height="150px"></a>
107
+
104
108
  ### Silver
105
109
 
106
110
  <a href="https://www.frontend.fyi/?utm_source=motion"><img alt="Frontend.fyi" src="https://github.com/user-attachments/assets/07d23aa5-69db-44a0-849d-90177e6fc817" width="150px" height="100px"></a>
@@ -109,7 +113,16 @@ Motion powers Framer animations, the web builder for creative pros. Design and s
109
113
 
110
114
  <a href="https://firecrawl.dev"><img alt="Firecrawl" src="https://github.com/user-attachments/assets/cba90e54-1329-4353-8fba-85beef4d2ee9" width="150px" height="100px"></a>
111
115
 
116
+ <a href="https://puzzmo.com"><img alt="Puzzmo" src="https://github.com/user-attachments/assets/aa2d5586-e5e2-43b9-8446-db456e4b0758" width="150px" height="100px"></a>
117
+
118
+ <a href="https://buildui.com"><img alt="Build UI" src="https://github.com/user-attachments/assets/024bfcd5-50e8-4b3d-a115-d5c6d6030d1c" width="150px" height="100px"></a>
119
+
120
+ <a href="https://hover.dev"><img alt="Hover" src="https://github.com/user-attachments/assets/4715b555-d2ac-4cb7-9f35-d36d708827b3" width="150px" height="100px"></a>
121
+
112
122
  ### Personal
113
123
 
114
124
  - [Nusu](https://x.com/nusualabuga)
115
125
  - [OlegWock](https://sinja.io)
126
+ - [Lambert Weller](https://github.com/l-mbert)
127
+ - [Jake LeBoeuf](https://jklb.wf)
128
+ - [Han Lee](https://github.com/hahnlee)
@@ -1479,6 +1479,34 @@ class BaseAnimation {
1479
1479
  }
1480
1480
  }
1481
1481
 
1482
+ /*
1483
+ Progress within given range
1484
+
1485
+ Given a lower limit and an upper limit, we return the progress
1486
+ (expressed as a number 0-1) represented by the given value, and
1487
+ limit that progress to within 0-1.
1488
+
1489
+ @param [number]: Lower limit
1490
+ @param [number]: Upper limit
1491
+ @param [number]: Value to find progress within given range
1492
+ @return [number]: Progress of value within range as expressed 0-1
1493
+ */
1494
+ const progress = (from, to, value) => {
1495
+ const toFromDifference = to - from;
1496
+ return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
1497
+ };
1498
+
1499
+ const generateLinearEasing = (easing, duration, // as milliseconds
1500
+ resolution = 10 // as milliseconds
1501
+ ) => {
1502
+ let points = "";
1503
+ const numPoints = Math.max(Math.round(duration / resolution), 2);
1504
+ for (let i = 0; i < numPoints; i++) {
1505
+ points += easing(progress(0, numPoints - 1, i)) + ", ";
1506
+ }
1507
+ return `linear(${points.substring(0, points.length - 2)})`;
1508
+ };
1509
+
1482
1510
  /*
1483
1511
  Convert velocity into velocity per second
1484
1512
 
@@ -1495,21 +1523,43 @@ function calcGeneratorVelocity(resolveValue, t, current) {
1495
1523
  return velocityPerSecond(current - resolveValue(prevT), t - prevT);
1496
1524
  }
1497
1525
 
1526
+ const springDefaults = {
1527
+ // Default spring physics
1528
+ stiffness: 100,
1529
+ damping: 10,
1530
+ mass: 1.0,
1531
+ velocity: 0.0,
1532
+ // Default duration/bounce-based options
1533
+ duration: 800, // in ms
1534
+ bounce: 0.3,
1535
+ visualDuration: 0.3, // in seconds
1536
+ // Rest thresholds
1537
+ restSpeed: {
1538
+ granular: 0.01,
1539
+ default: 2,
1540
+ },
1541
+ restDelta: {
1542
+ granular: 0.005,
1543
+ default: 0.5,
1544
+ },
1545
+ // Limits
1546
+ minDuration: 0.01, // in seconds
1547
+ maxDuration: 10.0, // in seconds
1548
+ minDamping: 0.05,
1549
+ maxDamping: 1,
1550
+ };
1551
+
1498
1552
  const safeMin = 0.001;
1499
- const minDuration = 0.01;
1500
- const maxDuration$1 = 10.0;
1501
- const minDamping = 0.05;
1502
- const maxDamping = 1;
1503
- function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
1553
+ function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
1504
1554
  let envelope;
1505
1555
  let derivative;
1506
- warning(duration <= secondsToMilliseconds(maxDuration$1), "Spring duration must be 10 seconds or less");
1556
+ warning(duration <= secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less");
1507
1557
  let dampingRatio = 1 - bounce;
1508
1558
  /**
1509
1559
  * Restrict dampingRatio and duration to within acceptable ranges.
1510
1560
  */
1511
- dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
1512
- duration = clamp(minDuration, maxDuration$1, millisecondsToSeconds(duration));
1561
+ dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio);
1562
+ duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, millisecondsToSeconds(duration));
1513
1563
  if (dampingRatio < 1) {
1514
1564
  /**
1515
1565
  * Underdamped spring
@@ -1553,8 +1603,8 @@ function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, })
1553
1603
  duration = secondsToMilliseconds(duration);
1554
1604
  if (isNaN(undampedFreq)) {
1555
1605
  return {
1556
- stiffness: 100,
1557
- damping: 10,
1606
+ stiffness: springDefaults.stiffness,
1607
+ damping: springDefaults.damping,
1558
1608
  duration,
1559
1609
  };
1560
1610
  }
@@ -1579,6 +1629,22 @@ function calcAngularFreq(undampedFreq, dampingRatio) {
1579
1629
  return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
1580
1630
  }
1581
1631
 
1632
+ /**
1633
+ * Implement a practical max duration for keyframe generation
1634
+ * to prevent infinite loops
1635
+ */
1636
+ const maxGeneratorDuration = 20000;
1637
+ function calcGeneratorDuration(generator) {
1638
+ let duration = 0;
1639
+ const timeStep = 50;
1640
+ let state = generator.next(duration);
1641
+ while (!state.done && duration < maxGeneratorDuration) {
1642
+ duration += timeStep;
1643
+ state = generator.next(duration);
1644
+ }
1645
+ return duration >= maxGeneratorDuration ? Infinity : duration;
1646
+ }
1647
+
1582
1648
  const durationKeys = ["duration", "bounce"];
1583
1649
  const physicsKeys = ["stiffness", "damping", "mass"];
1584
1650
  function isSpringType(options, keys) {
@@ -1586,29 +1652,51 @@ function isSpringType(options, keys) {
1586
1652
  }
1587
1653
  function getSpringOptions(options) {
1588
1654
  let springOptions = {
1589
- velocity: 0.0,
1590
- stiffness: 100,
1591
- damping: 10,
1592
- mass: 1.0,
1655
+ velocity: springDefaults.velocity,
1656
+ stiffness: springDefaults.stiffness,
1657
+ damping: springDefaults.damping,
1658
+ mass: springDefaults.mass,
1593
1659
  isResolvedFromDuration: false,
1594
1660
  ...options,
1595
1661
  };
1596
1662
  // stiffness/damping/mass overrides duration/bounce
1597
1663
  if (!isSpringType(options, physicsKeys) &&
1598
1664
  isSpringType(options, durationKeys)) {
1599
- const derived = findSpring(options);
1600
- springOptions = {
1601
- ...springOptions,
1602
- ...derived,
1603
- mass: 1.0,
1604
- };
1605
- springOptions.isResolvedFromDuration = true;
1665
+ if (options.visualDuration) {
1666
+ const visualDuration = options.visualDuration;
1667
+ const root = (2 * Math.PI) / (visualDuration * 1.2);
1668
+ const stiffness = root * root;
1669
+ const damping = 2 * clamp(0.05, 1, 1 - options.bounce) * Math.sqrt(stiffness);
1670
+ springOptions = {
1671
+ ...springOptions,
1672
+ mass: springDefaults.mass,
1673
+ stiffness,
1674
+ damping,
1675
+ };
1676
+ }
1677
+ else {
1678
+ const derived = findSpring(options);
1679
+ springOptions = {
1680
+ ...springOptions,
1681
+ ...derived,
1682
+ mass: springDefaults.mass,
1683
+ };
1684
+ springOptions.isResolvedFromDuration = true;
1685
+ }
1606
1686
  }
1607
1687
  return springOptions;
1608
1688
  }
1609
- function spring({ keyframes, restDelta, restSpeed, ...options }) {
1610
- const origin = keyframes[0];
1611
- const target = keyframes[keyframes.length - 1];
1689
+ function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
1690
+ const options = typeof optionsOrVisualDuration !== "object"
1691
+ ? {
1692
+ visualDuration: optionsOrVisualDuration,
1693
+ keyframes: [0, 1],
1694
+ bounce,
1695
+ }
1696
+ : optionsOrVisualDuration;
1697
+ let { restSpeed, restDelta } = options;
1698
+ const origin = options.keyframes[0];
1699
+ const target = options.keyframes[options.keyframes.length - 1];
1612
1700
  /**
1613
1701
  * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
1614
1702
  * to reduce GC during animation.
@@ -1630,8 +1718,12 @@ function spring({ keyframes, restDelta, restSpeed, ...options }) {
1630
1718
  * ratio between feeling good and finishing as soon as changes are imperceptible.
1631
1719
  */
1632
1720
  const isGranularScale = Math.abs(initialDelta) < 5;
1633
- restSpeed || (restSpeed = isGranularScale ? 0.01 : 2);
1634
- restDelta || (restDelta = isGranularScale ? 0.005 : 0.5);
1721
+ restSpeed || (restSpeed = isGranularScale
1722
+ ? springDefaults.restSpeed.granular
1723
+ : springDefaults.restSpeed.default);
1724
+ restDelta || (restDelta = isGranularScale
1725
+ ? springDefaults.restDelta.granular
1726
+ : springDefaults.restDelta.default);
1635
1727
  let resolveSpring;
1636
1728
  if (dampingRatio < 1) {
1637
1729
  const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
@@ -1672,7 +1764,7 @@ function spring({ keyframes, restDelta, restSpeed, ...options }) {
1672
1764
  dampedAngularFreq);
1673
1765
  };
1674
1766
  }
1675
- return {
1767
+ const generator = {
1676
1768
  calculatedDuration: isResolvedFromDuration ? duration || null : null,
1677
1769
  next: (t) => {
1678
1770
  const current = resolveSpring(t);
@@ -1700,7 +1792,13 @@ function spring({ keyframes, restDelta, restSpeed, ...options }) {
1700
1792
  state.value = state.done ? target : current;
1701
1793
  return state;
1702
1794
  },
1795
+ toString: () => {
1796
+ const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
1797
+ const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30);
1798
+ return calculatedDuration + "ms " + easing;
1799
+ },
1703
1800
  };
1801
+ return generator;
1704
1802
  }
1705
1803
 
1706
1804
  function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
@@ -1834,23 +1932,6 @@ const easingDefinitionToFunction = (definition) => {
1834
1932
  const combineFunctions = (a, b) => (v) => b(a(v));
1835
1933
  const pipe = (...transformers) => transformers.reduce(combineFunctions);
1836
1934
 
1837
- /*
1838
- Progress within given range
1839
-
1840
- Given a lower limit and an upper limit, we return the progress
1841
- (expressed as a number 0-1) represented by the given value, and
1842
- limit that progress to within 0-1.
1843
-
1844
- @param [number]: Lower limit
1845
- @param [number]: Upper limit
1846
- @param [number]: Value to find progress within given range
1847
- @return [number]: Progress of value within range as expressed 0-1
1848
- */
1849
- const progress = (from, to, value) => {
1850
- const toFromDifference = to - from;
1851
- return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
1852
- };
1853
-
1854
1935
  /*
1855
1936
  Value in range from progress
1856
1937
 
@@ -2195,22 +2276,6 @@ function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "e
2195
2276
  };
2196
2277
  }
2197
2278
 
2198
- /**
2199
- * Implement a practical max duration for keyframe generation
2200
- * to prevent infinite loops
2201
- */
2202
- const maxGeneratorDuration = 20000;
2203
- function calcGeneratorDuration(generator) {
2204
- let duration = 0;
2205
- const timeStep = 50;
2206
- let state = generator.next(duration);
2207
- while (!state.done && duration < maxGeneratorDuration) {
2208
- duration += timeStep;
2209
- state = generator.next(duration);
2210
- }
2211
- return duration >= maxGeneratorDuration ? Infinity : duration;
2212
- }
2213
-
2214
2279
  const frameloopDriver = (update) => {
2215
2280
  const passTimestamp = ({ timestamp }) => update(timestamp);
2216
2281
  return {
@@ -2608,18 +2673,6 @@ const acceleratedValues = new Set([
2608
2673
  // "background-color"
2609
2674
  ]);
2610
2675
 
2611
- // Create a linear easing point for every 10 ms
2612
- const resolution = 10;
2613
- const generateLinearEasing = (easing, duration // as milliseconds
2614
- ) => {
2615
- let points = "";
2616
- const numPoints = Math.max(Math.round(duration / resolution), 2);
2617
- for (let i = 0; i < numPoints; i++) {
2618
- points += easing(progress(0, numPoints - 1, i)) + ", ";
2619
- }
2620
- return `linear(${points.substring(0, points.length - 2)})`;
2621
- };
2622
-
2623
2676
  function memo(callback) {
2624
2677
  let result;
2625
2678
  return () => {
@@ -3295,7 +3348,7 @@ class MotionValue {
3295
3348
  * This will be replaced by the build step with the latest version number.
3296
3349
  * When MotionValues are provided to motion components, warn if versions are mixed.
3297
3350
  */
3298
- this.version = "11.11.17";
3351
+ this.version = "11.12.0";
3299
3352
  /**
3300
3353
  * Tracks whether this value can output a velocity. Currently this is only true
3301
3354
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -5874,7 +5927,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5874
5927
  * and warn against mismatches.
5875
5928
  */
5876
5929
  if (process.env.NODE_ENV === "development") {
5877
- warnOnce(nextValue.version === "11.11.17", `Attempting to mix Motion versions ${nextValue.version} with 11.11.17 may not work as expected.`);
5930
+ warnOnce(nextValue.version === "11.12.0", `Attempting to mix Motion versions ${nextValue.version} with 11.12.0 may not work as expected.`);
5878
5931
  }
5879
5932
  }
5880
5933
  else if (isMotionValue(prevValue)) {
@@ -163,9 +163,8 @@ const progress = (from, to, value) => {
163
163
  return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
164
164
  };
165
165
 
166
- // Create a linear easing point for every 10 ms
167
- const resolution = 10;
168
- const generateLinearEasing = (easing, duration // as milliseconds
166
+ const generateLinearEasing = (easing, duration, // as milliseconds
167
+ resolution = 10 // as milliseconds
169
168
  ) => {
170
169
  let points = "";
171
170
  const numPoints = Math.max(Math.round(duration / resolution), 2);
@@ -403,6 +402,7 @@ class NativeAnimation {
403
402
  valueKeyframes = [valueKeyframes];
404
403
  }
405
404
  hydrateKeyframes(valueName, valueKeyframes, readInitialKeyframe);
405
+ // TODO: Replace this with toString()?
406
406
  if (isGenerator(options.type)) {
407
407
  const generatorOptions = createGeneratorEasing(options, 100, options.type);
408
408
  options.ease = supportsLinearEasing()
package/dist/cjs/dom.js CHANGED
@@ -279,7 +279,7 @@ class MotionValue {
279
279
  * This will be replaced by the build step with the latest version number.
280
280
  * When MotionValues are provided to motion components, warn if versions are mixed.
281
281
  */
282
- this.version = "11.11.17";
282
+ this.version = "11.12.0";
283
283
  /**
284
284
  * Tracks whether this value can output a velocity. Currently this is only true
285
285
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -647,6 +647,34 @@ class GroupPlaybackControls {
647
647
  }
648
648
  }
649
649
 
650
+ /*
651
+ Progress within given range
652
+
653
+ Given a lower limit and an upper limit, we return the progress
654
+ (expressed as a number 0-1) represented by the given value, and
655
+ limit that progress to within 0-1.
656
+
657
+ @param [number]: Lower limit
658
+ @param [number]: Upper limit
659
+ @param [number]: Value to find progress within given range
660
+ @return [number]: Progress of value within range as expressed 0-1
661
+ */
662
+ const progress = (from, to, value) => {
663
+ const toFromDifference = to - from;
664
+ return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
665
+ };
666
+
667
+ const generateLinearEasing = (easing, duration, // as milliseconds
668
+ resolution = 10 // as milliseconds
669
+ ) => {
670
+ let points = "";
671
+ const numPoints = Math.max(Math.round(duration / resolution), 2);
672
+ for (let i = 0; i < numPoints; i++) {
673
+ points += easing(progress(0, numPoints - 1, i)) + ", ";
674
+ }
675
+ return `linear(${points.substring(0, points.length - 2)})`;
676
+ };
677
+
650
678
  /**
651
679
  * Converts seconds to milliseconds
652
680
  *
@@ -685,21 +713,43 @@ const clamp = (min, max, v) => {
685
713
  return v;
686
714
  };
687
715
 
716
+ const springDefaults = {
717
+ // Default spring physics
718
+ stiffness: 100,
719
+ damping: 10,
720
+ mass: 1.0,
721
+ velocity: 0.0,
722
+ // Default duration/bounce-based options
723
+ duration: 800, // in ms
724
+ bounce: 0.3,
725
+ visualDuration: 0.3, // in seconds
726
+ // Rest thresholds
727
+ restSpeed: {
728
+ granular: 0.01,
729
+ default: 2,
730
+ },
731
+ restDelta: {
732
+ granular: 0.005,
733
+ default: 0.5,
734
+ },
735
+ // Limits
736
+ minDuration: 0.01, // in seconds
737
+ maxDuration: 10.0, // in seconds
738
+ minDamping: 0.05,
739
+ maxDamping: 1,
740
+ };
741
+
688
742
  const safeMin = 0.001;
689
- const minDuration = 0.01;
690
- const maxDuration$1 = 10.0;
691
- const minDamping = 0.05;
692
- const maxDamping = 1;
693
- function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
743
+ function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
694
744
  let envelope;
695
745
  let derivative;
696
- exports.warning(duration <= secondsToMilliseconds(maxDuration$1), "Spring duration must be 10 seconds or less");
746
+ exports.warning(duration <= secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less");
697
747
  let dampingRatio = 1 - bounce;
698
748
  /**
699
749
  * Restrict dampingRatio and duration to within acceptable ranges.
700
750
  */
701
- dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
702
- duration = clamp(minDuration, maxDuration$1, millisecondsToSeconds(duration));
751
+ dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio);
752
+ duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, millisecondsToSeconds(duration));
703
753
  if (dampingRatio < 1) {
704
754
  /**
705
755
  * Underdamped spring
@@ -743,8 +793,8 @@ function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, })
743
793
  duration = secondsToMilliseconds(duration);
744
794
  if (isNaN(undampedFreq)) {
745
795
  return {
746
- stiffness: 100,
747
- damping: 10,
796
+ stiffness: springDefaults.stiffness,
797
+ damping: springDefaults.damping,
748
798
  duration,
749
799
  };
750
800
  }
@@ -769,6 +819,22 @@ function calcAngularFreq(undampedFreq, dampingRatio) {
769
819
  return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
770
820
  }
771
821
 
822
+ /**
823
+ * Implement a practical max duration for keyframe generation
824
+ * to prevent infinite loops
825
+ */
826
+ const maxGeneratorDuration = 20000;
827
+ function calcGeneratorDuration(generator) {
828
+ let duration = 0;
829
+ const timeStep = 50;
830
+ let state = generator.next(duration);
831
+ while (!state.done && duration < maxGeneratorDuration) {
832
+ duration += timeStep;
833
+ state = generator.next(duration);
834
+ }
835
+ return duration >= maxGeneratorDuration ? Infinity : duration;
836
+ }
837
+
772
838
  const durationKeys = ["duration", "bounce"];
773
839
  const physicsKeys = ["stiffness", "damping", "mass"];
774
840
  function isSpringType(options, keys) {
@@ -776,29 +842,51 @@ function isSpringType(options, keys) {
776
842
  }
777
843
  function getSpringOptions(options) {
778
844
  let springOptions = {
779
- velocity: 0.0,
780
- stiffness: 100,
781
- damping: 10,
782
- mass: 1.0,
845
+ velocity: springDefaults.velocity,
846
+ stiffness: springDefaults.stiffness,
847
+ damping: springDefaults.damping,
848
+ mass: springDefaults.mass,
783
849
  isResolvedFromDuration: false,
784
850
  ...options,
785
851
  };
786
852
  // stiffness/damping/mass overrides duration/bounce
787
853
  if (!isSpringType(options, physicsKeys) &&
788
854
  isSpringType(options, durationKeys)) {
789
- const derived = findSpring(options);
790
- springOptions = {
791
- ...springOptions,
792
- ...derived,
793
- mass: 1.0,
794
- };
795
- springOptions.isResolvedFromDuration = true;
855
+ if (options.visualDuration) {
856
+ const visualDuration = options.visualDuration;
857
+ const root = (2 * Math.PI) / (visualDuration * 1.2);
858
+ const stiffness = root * root;
859
+ const damping = 2 * clamp(0.05, 1, 1 - options.bounce) * Math.sqrt(stiffness);
860
+ springOptions = {
861
+ ...springOptions,
862
+ mass: springDefaults.mass,
863
+ stiffness,
864
+ damping,
865
+ };
866
+ }
867
+ else {
868
+ const derived = findSpring(options);
869
+ springOptions = {
870
+ ...springOptions,
871
+ ...derived,
872
+ mass: springDefaults.mass,
873
+ };
874
+ springOptions.isResolvedFromDuration = true;
875
+ }
796
876
  }
797
877
  return springOptions;
798
878
  }
799
- function spring({ keyframes, restDelta, restSpeed, ...options }) {
800
- const origin = keyframes[0];
801
- const target = keyframes[keyframes.length - 1];
879
+ function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
880
+ const options = typeof optionsOrVisualDuration !== "object"
881
+ ? {
882
+ visualDuration: optionsOrVisualDuration,
883
+ keyframes: [0, 1],
884
+ bounce,
885
+ }
886
+ : optionsOrVisualDuration;
887
+ let { restSpeed, restDelta } = options;
888
+ const origin = options.keyframes[0];
889
+ const target = options.keyframes[options.keyframes.length - 1];
802
890
  /**
803
891
  * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
804
892
  * to reduce GC during animation.
@@ -820,8 +908,12 @@ function spring({ keyframes, restDelta, restSpeed, ...options }) {
820
908
  * ratio between feeling good and finishing as soon as changes are imperceptible.
821
909
  */
822
910
  const isGranularScale = Math.abs(initialDelta) < 5;
823
- restSpeed || (restSpeed = isGranularScale ? 0.01 : 2);
824
- restDelta || (restDelta = isGranularScale ? 0.005 : 0.5);
911
+ restSpeed || (restSpeed = isGranularScale
912
+ ? springDefaults.restSpeed.granular
913
+ : springDefaults.restSpeed.default);
914
+ restDelta || (restDelta = isGranularScale
915
+ ? springDefaults.restDelta.granular
916
+ : springDefaults.restDelta.default);
825
917
  let resolveSpring;
826
918
  if (dampingRatio < 1) {
827
919
  const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
@@ -862,7 +954,7 @@ function spring({ keyframes, restDelta, restSpeed, ...options }) {
862
954
  dampedAngularFreq);
863
955
  };
864
956
  }
865
- return {
957
+ const generator = {
866
958
  calculatedDuration: isResolvedFromDuration ? duration || null : null,
867
959
  next: (t) => {
868
960
  const current = resolveSpring(t);
@@ -890,23 +982,13 @@ function spring({ keyframes, restDelta, restSpeed, ...options }) {
890
982
  state.value = state.done ? target : current;
891
983
  return state;
892
984
  },
985
+ toString: () => {
986
+ const calculatedDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
987
+ const easing = generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30);
988
+ return calculatedDuration + "ms " + easing;
989
+ },
893
990
  };
894
- }
895
-
896
- /**
897
- * Implement a practical max duration for keyframe generation
898
- * to prevent infinite loops
899
- */
900
- const maxGeneratorDuration = 20000;
901
- function calcGeneratorDuration(generator) {
902
- let duration = 0;
903
- const timeStep = 50;
904
- let state = generator.next(duration);
905
- while (!state.done && duration < maxGeneratorDuration) {
906
- duration += timeStep;
907
- state = generator.next(duration);
908
- }
909
- return duration >= maxGeneratorDuration ? Infinity : duration;
991
+ return generator;
910
992
  }
911
993
 
912
994
  /**
@@ -947,23 +1029,6 @@ const mixNumber$1 = (from, to, progress) => {
947
1029
  return from + (to - from) * progress;
948
1030
  };
949
1031
 
950
- /*
951
- Progress within given range
952
-
953
- Given a lower limit and an upper limit, we return the progress
954
- (expressed as a number 0-1) represented by the given value, and
955
- limit that progress to within 0-1.
956
-
957
- @param [number]: Lower limit
958
- @param [number]: Upper limit
959
- @param [number]: Value to find progress within given range
960
- @return [number]: Progress of value within range as expressed 0-1
961
- */
962
- const progress = (from, to, value) => {
963
- const toFromDifference = to - from;
964
- return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
965
- };
966
-
967
1032
  function fillOffset(offset, remaining) {
968
1033
  const min = offset[offset.length - 1];
969
1034
  for (let i = 1; i <= remaining; i++) {
@@ -3322,18 +3387,6 @@ const acceleratedValues = new Set([
3322
3387
  // "background-color"
3323
3388
  ]);
3324
3389
 
3325
- // Create a linear easing point for every 10 ms
3326
- const resolution = 10;
3327
- const generateLinearEasing = (easing, duration // as milliseconds
3328
- ) => {
3329
- let points = "";
3330
- const numPoints = Math.max(Math.round(duration / resolution), 2);
3331
- for (let i = 0; i < numPoints; i++) {
3332
- points += easing(progress(0, numPoints - 1, i)) + ", ";
3333
- }
3334
- return `linear(${points.substring(0, points.length - 2)})`;
3335
- };
3336
-
3337
3390
  /**
3338
3391
  * Add the ability for test suites to manually set support flags
3339
3392
  * to better test more environments.
@@ -4103,7 +4156,7 @@ function updateMotionValuesFromProps(element, next, prev) {
4103
4156
  * and warn against mismatches.
4104
4157
  */
4105
4158
  if (process.env.NODE_ENV === "development") {
4106
- warnOnce(nextValue.version === "11.11.17", `Attempting to mix Motion versions ${nextValue.version} with 11.11.17 may not work as expected.`);
4159
+ warnOnce(nextValue.version === "11.12.0", `Attempting to mix Motion versions ${nextValue.version} with 11.12.0 may not work as expected.`);
4107
4160
  }
4108
4161
  }
4109
4162
  else if (isMotionValue(prevValue)) {
@@ -5251,6 +5304,7 @@ class NativeAnimation {
5251
5304
  valueKeyframes = [valueKeyframes];
5252
5305
  }
5253
5306
  hydrateKeyframes(valueName, valueKeyframes, readInitialKeyframe);
5307
+ // TODO: Replace this with toString()?
5254
5308
  if (isGenerator(options.type)) {
5255
5309
  const generatorOptions = createGeneratorEasing(options, 100, options.type);
5256
5310
  options.ease = supportsLinearEasing()