framer-motion 7.8.0 → 7.9.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.
Files changed (39) hide show
  1. package/README.md +6 -5
  2. package/dist/cjs/index.js +2030 -1920
  3. package/dist/es/animation/animate.mjs +2 -2
  4. package/dist/es/animation/create-instant-animation.mjs +12 -0
  5. package/dist/es/animation/{animation-controls.mjs → hooks/animation-controls.mjs} +2 -2
  6. package/dist/es/animation/{use-animated-state.mjs → hooks/use-animated-state.mjs} +6 -6
  7. package/dist/es/animation/{use-animation.mjs → hooks/use-animation.mjs} +1 -1
  8. package/dist/es/animation/index.mjs +124 -0
  9. package/dist/es/animation/legacy-popmotion/decay.mjs +11 -4
  10. package/dist/es/animation/legacy-popmotion/index.mjs +22 -11
  11. package/dist/es/animation/legacy-popmotion/inertia.mjs +14 -8
  12. package/dist/es/animation/legacy-popmotion/keyframes.mjs +21 -13
  13. package/dist/es/animation/legacy-popmotion/spring.mjs +13 -11
  14. package/dist/es/animation/utils/default-transitions.mjs +9 -14
  15. package/dist/es/animation/utils/keyframes.mjs +41 -0
  16. package/dist/es/animation/utils/transitions.mjs +1 -166
  17. package/dist/es/animation/waapi/create-accelerated-animation.mjs +82 -0
  18. package/dist/es/animation/waapi/index.mjs +4 -6
  19. package/dist/es/gestures/drag/VisualElementDragControls.mjs +2 -2
  20. package/dist/es/index.mjs +3 -3
  21. package/dist/es/render/VisualElement.mjs +1 -1
  22. package/dist/es/render/utils/animation.mjs +2 -2
  23. package/dist/es/render/utils/motion-values.mjs +2 -2
  24. package/dist/es/render/utils/setters.mjs +1 -1
  25. package/dist/es/value/index.mjs +11 -5
  26. package/dist/es/value/use-spring.mjs +1 -2
  27. package/dist/framer-motion.dev.js +2051 -1941
  28. package/dist/framer-motion.js +1 -1
  29. package/dist/index.d.ts +409 -348
  30. package/dist/projection.dev.js +1672 -1535
  31. package/dist/size-rollup-dom-animation-assets.js +1 -1
  32. package/dist/size-rollup-dom-animation.js +1 -1
  33. package/dist/size-rollup-dom-max-assets.js +1 -1
  34. package/dist/size-rollup-dom-max.js +1 -1
  35. package/dist/size-rollup-motion.js +1 -1
  36. package/dist/size-webpack-dom-animation.js +1 -1
  37. package/dist/size-webpack-dom-max.js +1 -1
  38. package/dist/three-entry.d.ts +287 -281
  39. package/package.json +8 -8
@@ -1,6 +1,6 @@
1
+ import { createMotionValueAnimation } from './index.mjs';
1
2
  import { motionValue } from '../value/index.mjs';
2
3
  import { isMotionValue } from '../value/utils/is-motion-value.mjs';
3
- import { startAnimation } from './utils/transitions.mjs';
4
4
 
5
5
  /**
6
6
  * Animate a single value or a `MotionValue`.
@@ -31,7 +31,7 @@ import { startAnimation } from './utils/transitions.mjs';
31
31
  */
32
32
  function animate(from, to, transition = {}) {
33
33
  const value = isMotionValue(from) ? from : motionValue(from);
34
- startAnimation("", value, to, transition);
34
+ value.start(createMotionValueAnimation("", value, to, transition));
35
35
  return {
36
36
  stop: () => value.stop(),
37
37
  isAnimating: () => value.isAnimating(),
@@ -0,0 +1,12 @@
1
+ import { delay } from '../utils/delay.mjs';
2
+
3
+ function createInstantAnimation({ keyframes, elapsed, onUpdate, onComplete, }) {
4
+ const setValue = () => {
5
+ onUpdate && onUpdate(keyframes[keyframes.length - 1]);
6
+ onComplete && onComplete();
7
+ return () => { };
8
+ };
9
+ return elapsed ? delay(setValue, -elapsed) : setValue();
10
+ }
11
+
12
+ export { createInstantAnimation };
@@ -1,6 +1,6 @@
1
1
  import { invariant } from 'hey-listen';
2
- import { stopAnimation, animateVisualElement } from '../render/utils/animation.mjs';
3
- import { setValues } from '../render/utils/setters.mjs';
2
+ import { stopAnimation, animateVisualElement } from '../../render/utils/animation.mjs';
3
+ import { setValues } from '../../render/utils/setters.mjs';
4
4
 
5
5
  /**
6
6
  * @public
@@ -1,10 +1,10 @@
1
1
  import { useState, useEffect } from 'react';
2
- import { useConstant } from '../utils/use-constant.mjs';
3
- import { getOrigin, checkTargetForNewValues } from '../render/utils/setters.mjs';
4
- import { animateVisualElement } from '../render/utils/animation.mjs';
5
- import { makeUseVisualState } from '../motion/utils/use-visual-state.mjs';
6
- import { createBox } from '../projection/geometry/models.mjs';
7
- import { VisualElement } from '../render/VisualElement.mjs';
2
+ import { useConstant } from '../../utils/use-constant.mjs';
3
+ import { getOrigin, checkTargetForNewValues } from '../../render/utils/setters.mjs';
4
+ import { animateVisualElement } from '../../render/utils/animation.mjs';
5
+ import { makeUseVisualState } from '../../motion/utils/use-visual-state.mjs';
6
+ import { createBox } from '../../projection/geometry/models.mjs';
7
+ import { VisualElement } from '../../render/VisualElement.mjs';
8
8
 
9
9
  const createObject = () => ({});
10
10
  class StateVisualElement extends VisualElement {
@@ -1,6 +1,6 @@
1
1
  import { animationControls } from './animation-controls.mjs';
2
2
  import { useEffect } from 'react';
3
- import { useConstant } from '../utils/use-constant.mjs';
3
+ import { useConstant } from '../../utils/use-constant.mjs';
4
4
 
5
5
  /**
6
6
  * Creates `AnimationControls`, which can be used to manually start, stop
@@ -0,0 +1,124 @@
1
+ import { warning } from 'hey-listen';
2
+ import { secondsToMilliseconds } from '../utils/time-conversion.mjs';
3
+ import { instantAnimationState } from '../utils/use-instant-transition-state.mjs';
4
+ import { createAcceleratedAnimation } from './waapi/create-accelerated-animation.mjs';
5
+ import { createInstantAnimation } from './create-instant-animation.mjs';
6
+ import { animate } from './legacy-popmotion/index.mjs';
7
+ import { inertia } from './legacy-popmotion/inertia.mjs';
8
+ import { getDefaultTransition } from './utils/default-transitions.mjs';
9
+ import { isAnimatable } from './utils/is-animatable.mjs';
10
+ import { getKeyframes } from './utils/keyframes.mjs';
11
+ import { getValueTransition, isTransitionDefined } from './utils/transitions.mjs';
12
+ import { supports } from './waapi/supports.mjs';
13
+
14
+ /**
15
+ * A list of values that can be hardware-accelerated.
16
+ */
17
+ const acceleratedValues = new Set(["opacity"]);
18
+ const createMotionValueAnimation = (valueName, value, target, transition = {}) => {
19
+ return (onComplete) => {
20
+ const valueTransition = getValueTransition(transition, valueName) || {};
21
+ /**
22
+ * Most transition values are currently completely overwritten by value-specific
23
+ * transitions. In the future it'd be nicer to blend these transitions. But for now
24
+ * delay actually does inherit from the root transition if not value-specific.
25
+ */
26
+ const delay = valueTransition.delay || transition.delay || 0;
27
+ /**
28
+ * Elapsed isn't a public transition option but can be passed through from
29
+ * optimized appear effects in milliseconds.
30
+ */
31
+ let { elapsed = 0 } = transition;
32
+ elapsed = elapsed - secondsToMilliseconds(delay);
33
+ const keyframes = getKeyframes(value, valueName, target, valueTransition);
34
+ /**
35
+ * Check if we're able to animate between the start and end keyframes,
36
+ * and throw a warning if we're attempting to animate between one that's
37
+ * animatable and another that isn't.
38
+ */
39
+ const originKeyframe = keyframes[0];
40
+ const targetKeyframe = keyframes[keyframes.length - 1];
41
+ const isOriginAnimatable = isAnimatable(valueName, originKeyframe);
42
+ const isTargetAnimatable = isAnimatable(valueName, targetKeyframe);
43
+ warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${valueName} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`);
44
+ let options = {
45
+ keyframes,
46
+ velocity: value.getVelocity(),
47
+ ...valueTransition,
48
+ elapsed,
49
+ onUpdate: (v) => {
50
+ value.set(v);
51
+ valueTransition.onUpdate && valueTransition.onUpdate(v);
52
+ },
53
+ onComplete: () => {
54
+ onComplete();
55
+ valueTransition.onComplete && valueTransition.onComplete();
56
+ },
57
+ };
58
+ if (!isOriginAnimatable ||
59
+ !isTargetAnimatable ||
60
+ instantAnimationState.current ||
61
+ valueTransition.type === false) {
62
+ /**
63
+ * If we can't animate this value, or the global instant animation flag is set,
64
+ * or this is simply defined as an instant transition, return an instant transition.
65
+ */
66
+ return createInstantAnimation(options);
67
+ }
68
+ else if (valueTransition.type === "inertia") {
69
+ /**
70
+ * If this is an inertia animation, we currently don't support pre-generating
71
+ * keyframes for this as such it must always run on the main thread.
72
+ */
73
+ const animation = inertia(options);
74
+ return () => animation.stop();
75
+ }
76
+ /**
77
+ * If there's no transition defined for this value, we can generate
78
+ * unqiue transition settings for this value.
79
+ */
80
+ if (!isTransitionDefined(valueTransition)) {
81
+ options = {
82
+ ...options,
83
+ ...getDefaultTransition(valueName, options),
84
+ };
85
+ }
86
+ /**
87
+ * Both WAAPI and our internal animation functions use durations
88
+ * as defined by milliseconds, while our external API defines them
89
+ * as seconds.
90
+ */
91
+ if (options.duration) {
92
+ options.duration = secondsToMilliseconds(options.duration);
93
+ }
94
+ if (options.repeatDelay) {
95
+ options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
96
+ }
97
+ const visualElement = value.owner;
98
+ const element = visualElement && visualElement.current;
99
+ const canAccelerateAnimation = supports.waapi() &&
100
+ acceleratedValues.has(valueName) &&
101
+ !options.repeatDelay &&
102
+ options.repeatType !== "mirror" &&
103
+ options.damping !== 0 &&
104
+ typeof options.ease !== "function" &&
105
+ visualElement &&
106
+ element instanceof HTMLElement &&
107
+ !visualElement.getProps().onUpdate;
108
+ if (canAccelerateAnimation) {
109
+ /**
110
+ * If this animation is capable of being run via WAAPI, then do so.
111
+ */
112
+ return createAcceleratedAnimation(value, valueName, options);
113
+ }
114
+ else {
115
+ /**
116
+ * Otherwise, fall back to the main thread.
117
+ */
118
+ const animation = animate(options);
119
+ return () => animation.stop();
120
+ }
121
+ };
122
+ };
123
+
124
+ export { createMotionValueAnimation };
@@ -1,18 +1,25 @@
1
- function decay({ velocity = 0, from = 0, power = 0.8, timeConstant = 350, restDelta = 0.5, modifyTarget, }) {
1
+ function decay({
2
+ /**
3
+ * The decay animation dynamically calculates an end of the animation
4
+ * based on the initial keyframe, so we only need to define a single keyframe
5
+ * as default.
6
+ */
7
+ keyframes = [0], velocity = 0, power = 0.8, timeConstant = 350, restDelta = 0.5, modifyTarget, }) {
8
+ const origin = keyframes[0];
2
9
  /**
3
10
  * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
4
11
  * to reduce GC during animation.
5
12
  */
6
- const state = { done: false, value: from };
13
+ const state = { done: false, value: origin };
7
14
  let amplitude = power * velocity;
8
- const ideal = from + amplitude;
15
+ const ideal = origin + amplitude;
9
16
  const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
10
17
  /**
11
18
  * If the target has changed we need to re-calculate the amplitude, otherwise
12
19
  * the animation will start from the wrong position.
13
20
  */
14
21
  if (target !== ideal)
15
- amplitude = target - from;
22
+ amplitude = target - origin;
16
23
  return {
17
24
  next: (t) => {
18
25
  const delta = -amplitude * Math.exp(-t / timeConstant);
@@ -4,7 +4,12 @@ import { decay } from './decay.mjs';
4
4
  import { sync, cancelSync } from '../../frameloop/index.mjs';
5
5
  import { interpolate } from '../../utils/interpolate.mjs';
6
6
 
7
- const types = { decay, keyframes, spring };
7
+ const types = {
8
+ decay,
9
+ keyframes: keyframes,
10
+ tween: keyframes,
11
+ spring,
12
+ };
8
13
  function loopElapsed(elapsed, duration, delay = 0) {
9
14
  return elapsed - duration - delay;
10
15
  }
@@ -23,26 +28,29 @@ const framesync = (update) => {
23
28
  stop: () => cancelSync.update(passTimestamp),
24
29
  };
25
30
  };
26
- function animate({ from, autoplay = true, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, onPlay, onStop, onComplete, onRepeat, onUpdate, type = "keyframes", ...options }) {
31
+ function animate({ duration, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, keyframes, autoplay = true, onPlay, onStop, onComplete, onRepeat, onUpdate, type = "keyframes", ...options }) {
27
32
  var _a, _b;
28
- let { to } = options;
29
33
  let driverControls;
30
34
  let repeatCount = 0;
31
- let computedDuration = options
32
- .duration;
35
+ let computedDuration = duration;
33
36
  let latest;
34
37
  let isComplete = false;
35
38
  let isForwardPlayback = true;
36
39
  let interpolateFromNumber;
37
- const animator = types[Array.isArray(to) ? "keyframes" : type];
38
- if ((_b = (_a = animator).needsInterpolation) === null || _b === void 0 ? void 0 : _b.call(_a, from, to)) {
39
- interpolateFromNumber = interpolate([0, 100], [from, to], {
40
+ const animator = types[keyframes.length > 2 ? "keyframes" : type];
41
+ const origin = keyframes[0];
42
+ const target = keyframes[keyframes.length - 1];
43
+ if ((_b = (_a = animator).needsInterpolation) === null || _b === void 0 ? void 0 : _b.call(_a, origin, target)) {
44
+ interpolateFromNumber = interpolate([0, 100], [origin, target], {
40
45
  clamp: false,
41
46
  });
42
- from = 0;
43
- to = 100;
47
+ keyframes = [0, 100];
44
48
  }
45
- const animation = animator({ ...options, from, to });
49
+ const animation = animator({
50
+ ...options,
51
+ duration,
52
+ keyframes,
53
+ });
46
54
  function repeat() {
47
55
  repeatCount++;
48
56
  if (repeatType === "reverse") {
@@ -97,6 +105,9 @@ function animate({ from, autoplay = true, driver = framesync, elapsed = 0, repea
97
105
  onStop && onStop();
98
106
  driverControls.stop();
99
107
  },
108
+ sample: (t) => {
109
+ return animation.next(Math.max(0, t)).value;
110
+ },
100
111
  };
101
112
  }
102
113
 
@@ -2,12 +2,13 @@ import { animate } from './index.mjs';
2
2
  import { velocityPerSecond } from '../../utils/velocity-per-second.mjs';
3
3
  import { frameData } from '../../frameloop/data.mjs';
4
4
 
5
- function inertia({ from = 0, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
5
+ function inertia({ keyframes, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
6
+ const origin = keyframes[0];
6
7
  let currentAnimation;
7
8
  function isOutOfBounds(v) {
8
9
  return (min !== undefined && v < min) || (max !== undefined && v > max);
9
10
  }
10
- function boundaryNearest(v) {
11
+ function findNearestBoundary(v) {
11
12
  if (min === undefined)
12
13
  return max;
13
14
  if (max === undefined)
@@ -17,6 +18,8 @@ function inertia({ from = 0, velocity = 0, min, max, power = 0.8, timeConstant =
17
18
  function startAnimation(options) {
18
19
  currentAnimation === null || currentAnimation === void 0 ? void 0 : currentAnimation.stop();
19
20
  currentAnimation = animate({
21
+ keyframes: [0, 1],
22
+ velocity: 0,
20
23
  ...options,
21
24
  driver,
22
25
  onUpdate: (v) => {
@@ -37,9 +40,12 @@ function inertia({ from = 0, velocity = 0, min, max, power = 0.8, timeConstant =
37
40
  ...options,
38
41
  });
39
42
  }
40
- if (isOutOfBounds(from)) {
43
+ if (isOutOfBounds(origin)) {
41
44
  // Start the animation with spring if outside the defined boundaries
42
- startSpring({ from, velocity, to: boundaryNearest(from) });
45
+ startSpring({
46
+ velocity,
47
+ keyframes: [origin, findNearestBoundary(origin)],
48
+ });
43
49
  }
44
50
  else {
45
51
  /**
@@ -50,10 +56,10 @@ function inertia({ from = 0, velocity = 0, min, max, power = 0.8, timeConstant =
50
56
  * If it is, we want to check per frame when to switch to a spring
51
57
  * animation
52
58
  */
53
- let target = power * velocity + from;
59
+ let target = power * velocity + origin;
54
60
  if (typeof modifyTarget !== "undefined")
55
61
  target = modifyTarget(target);
56
- const boundary = boundaryNearest(target);
62
+ const boundary = findNearestBoundary(target);
57
63
  const heading = boundary === min ? -1 : 1;
58
64
  let prev;
59
65
  let current;
@@ -63,12 +69,12 @@ function inertia({ from = 0, velocity = 0, min, max, power = 0.8, timeConstant =
63
69
  velocity = velocityPerSecond(v - prev, frameData.delta);
64
70
  if ((heading === 1 && v > boundary) ||
65
71
  (heading === -1 && v < boundary)) {
66
- startSpring({ from: v, to: boundary, velocity });
72
+ startSpring({ keyframes: [v, boundary], velocity });
67
73
  }
68
74
  };
69
75
  startAnimation({
70
76
  type: "decay",
71
- from,
77
+ keyframes: [origin, 0],
72
78
  velocity,
73
79
  timeConstant,
74
80
  power,
@@ -1,5 +1,6 @@
1
1
  import { easeInOut } from '../../easing/ease.mjs';
2
2
  import { interpolate } from '../../utils/interpolate.mjs';
3
+ import { isEasingArray, easingDefinitionToFunction } from '../utils/easing.mjs';
3
4
 
4
5
  function defaultEasing(values, easing) {
5
6
  return values.map(() => easing || easeInOut).splice(0, values.length - 1);
@@ -11,28 +12,35 @@ function defaultOffset(values) {
11
12
  function convertOffsetToTimes(offset, duration) {
12
13
  return offset.map((o) => o * duration);
13
14
  }
14
- function keyframes({ from = 0, to = 1, ease, offset, duration = 300, }) {
15
+ function keyframes({ keyframes: keyframeValues, ease = easeInOut, times, duration = 300, }) {
16
+ keyframeValues = [...keyframeValues];
17
+ const origin = keyframes[0];
15
18
  /**
16
- * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
17
- * to reduce GC during animation.
19
+ * Easing functions can be externally defined as strings. Here we convert them
20
+ * into actual functions.
18
21
  */
19
- const state = { done: false, value: from };
22
+ const easingFunctions = isEasingArray(ease)
23
+ ? ease.map(easingDefinitionToFunction)
24
+ : easingDefinitionToFunction(ease);
20
25
  /**
21
- * Convert values to an array if they've been given as from/to options
26
+ * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
27
+ * to reduce GC during animation.
22
28
  */
23
- const values = Array.isArray(to) ? to : [from, to];
29
+ const state = { done: false, value: origin };
24
30
  /**
25
31
  * Create a times array based on the provided 0-1 offsets
26
32
  */
27
- const times = convertOffsetToTimes(
33
+ const absoluteTimes = convertOffsetToTimes(
28
34
  // Only use the provided offsets if they're the correct length
29
35
  // TODO Maybe we should warn here if there's a length mismatch
30
- offset && offset.length === values.length
31
- ? offset
32
- : defaultOffset(values), duration);
36
+ times && times.length === keyframes.length
37
+ ? times
38
+ : defaultOffset(keyframeValues), duration);
33
39
  function createInterpolator() {
34
- return interpolate(times, values, {
35
- ease: Array.isArray(ease) ? ease : defaultEasing(values, ease),
40
+ return interpolate(absoluteTimes, keyframeValues, {
41
+ ease: Array.isArray(easingFunctions)
42
+ ? easingFunctions
43
+ : defaultEasing(keyframeValues, easingFunctions),
36
44
  });
37
45
  }
38
46
  let interpolator = createInterpolator();
@@ -43,7 +51,7 @@ function keyframes({ from = 0, to = 1, ease, offset, duration = 300, }) {
43
51
  return state;
44
52
  },
45
53
  flipTarget: () => {
46
- values.reverse();
54
+ keyframeValues.reverse();
47
55
  interpolator = createInterpolator();
48
56
  },
49
57
  };
@@ -33,32 +33,34 @@ const velocitySampleDuration = 5;
33
33
  /**
34
34
  * This is based on the spring implementation of Wobble https://github.com/skevy/wobble
35
35
  */
36
- function spring({ from = 0.0, to = 1.0, restSpeed = 2, restDelta = 0.01, ...options }) {
36
+ function spring({ keyframes, restSpeed = 2, restDelta = 0.01, ...options }) {
37
+ let origin = keyframes[0];
38
+ let target = keyframes[keyframes.length - 1];
37
39
  /**
38
40
  * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
39
41
  * to reduce GC during animation.
40
42
  */
41
- const state = { done: false, value: from };
42
- let { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options);
43
+ const state = { done: false, value: origin };
44
+ const { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options);
43
45
  let resolveSpring = zero;
44
46
  let initialVelocity = velocity ? -(velocity / 1000) : 0.0;
45
47
  const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
46
48
  function createSpring() {
47
- const initialDelta = to - from;
49
+ const initialDelta = target - origin;
48
50
  const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000;
49
51
  /**
50
52
  * If we're working within what looks like a 0-1 range, change the default restDelta
51
53
  * to 0.01
52
54
  */
53
55
  if (restDelta === undefined) {
54
- restDelta = Math.min(Math.abs(to - from) / 100, 0.4);
56
+ restDelta = Math.min(Math.abs(target - origin) / 100, 0.4);
55
57
  }
56
58
  if (dampingRatio < 1) {
57
59
  const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
58
60
  // Underdamped spring
59
61
  resolveSpring = (t) => {
60
62
  const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
61
- return (to -
63
+ return (target -
62
64
  envelope *
63
65
  (((initialVelocity +
64
66
  dampingRatio * undampedAngularFreq * initialDelta) /
@@ -69,7 +71,7 @@ function spring({ from = 0.0, to = 1.0, restSpeed = 2, restDelta = 0.01, ...opti
69
71
  }
70
72
  else if (dampingRatio === 1) {
71
73
  // Critically damped spring
72
- resolveSpring = (t) => to -
74
+ resolveSpring = (t) => target -
73
75
  Math.exp(-undampedAngularFreq * t) *
74
76
  (initialDelta +
75
77
  (initialVelocity + undampedAngularFreq * initialDelta) *
@@ -82,7 +84,7 @@ function spring({ from = 0.0, to = 1.0, restSpeed = 2, restDelta = 0.01, ...opti
82
84
  const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
83
85
  // When performing sinh or cosh values can hit Infinity so we cap them here
84
86
  const freqForT = Math.min(dampedAngularFreq * t, 300);
85
- return (to -
87
+ return (target -
86
88
  (envelope *
87
89
  ((initialVelocity +
88
90
  dampingRatio * undampedAngularFreq * initialDelta) *
@@ -115,19 +117,19 @@ function spring({ from = 0.0, to = 1.0, restSpeed = 2, restDelta = 0.01, ...opti
115
117
  }
116
118
  }
117
119
  const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
118
- const isBelowDisplacementThreshold = Math.abs(to - current) <= restDelta;
120
+ const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
119
121
  state.done =
120
122
  isBelowVelocityThreshold && isBelowDisplacementThreshold;
121
123
  }
122
124
  else {
123
125
  state.done = t >= duration;
124
126
  }
125
- state.value = state.done ? to : current;
127
+ state.value = state.done ? target : current;
126
128
  return state;
127
129
  },
128
130
  flipTarget: () => {
129
131
  initialVelocity = -initialVelocity;
130
- [from, to] = [to, from];
132
+ [origin, target] = [target, origin];
131
133
  createSpring();
132
134
  },
133
135
  };
@@ -1,15 +1,13 @@
1
- import { isKeyframesTarget } from './is-keyframes-target.mjs';
2
-
3
1
  const underDampedSpring = () => ({
4
2
  type: "spring",
5
3
  stiffness: 500,
6
4
  damping: 25,
7
5
  restSpeed: 10,
8
6
  });
9
- const criticallyDampedSpring = (to) => ({
7
+ const criticallyDampedSpring = (target) => ({
10
8
  type: "spring",
11
9
  stiffness: 550,
12
- damping: to === 0 ? 2 * Math.sqrt(550) : 30,
10
+ damping: target === 0 ? 2 * Math.sqrt(550) : 30,
13
11
  restSpeed: 10,
14
12
  });
15
13
  const linearTween = () => ({
@@ -17,11 +15,10 @@ const linearTween = () => ({
17
15
  ease: "linear",
18
16
  duration: 0.3,
19
17
  });
20
- const keyframes = (values) => ({
18
+ const keyframesTransition = {
21
19
  type: "keyframes",
22
20
  duration: 0.8,
23
- values,
24
- });
21
+ };
25
22
  const defaultTransitions = {
26
23
  x: underDampedSpring,
27
24
  y: underDampedSpring,
@@ -38,16 +35,14 @@ const defaultTransitions = {
38
35
  color: linearTween,
39
36
  default: criticallyDampedSpring,
40
37
  };
41
- const getDefaultTransition = (valueKey, to) => {
42
- let transitionFactory;
43
- if (isKeyframesTarget(to)) {
44
- transitionFactory = keyframes;
38
+ const getDefaultTransition = (valueKey, { keyframes }) => {
39
+ if (keyframes.length > 2) {
40
+ return keyframesTransition;
45
41
  }
46
42
  else {
47
- transitionFactory =
48
- defaultTransitions[valueKey] || defaultTransitions.default;
43
+ const factory = defaultTransitions[valueKey] || defaultTransitions.default;
44
+ return factory(keyframes[1]);
49
45
  }
50
- return { to, ...transitionFactory(to) };
51
46
  };
52
47
 
53
48
  export { criticallyDampedSpring, getDefaultTransition, linearTween, underDampedSpring };
@@ -0,0 +1,41 @@
1
+ import { getAnimatableNone } from '../../render/dom/value-types/animatable-none.mjs';
2
+ import { isAnimatable } from './is-animatable.mjs';
3
+ import { isZero, getZeroUnit } from './transitions.mjs';
4
+
5
+ function getKeyframes(value, valueName, target, transition) {
6
+ const isTargetAnimatable = isAnimatable(valueName, target);
7
+ let origin = transition.from !== undefined ? transition.from : value.get();
8
+ if (origin === "none" && isTargetAnimatable && typeof target === "string") {
9
+ /**
10
+ * If we're trying to animate from "none", try and get an animatable version
11
+ * of the target. This could be improved to work both ways.
12
+ */
13
+ origin = getAnimatableNone(valueName, target);
14
+ }
15
+ else if (isZero(origin) && typeof target === "string") {
16
+ origin = getZeroUnit(target);
17
+ }
18
+ else if (!Array.isArray(target) &&
19
+ isZero(target) &&
20
+ typeof origin === "string") {
21
+ target = getZeroUnit(origin);
22
+ }
23
+ /**
24
+ * If the target has been defined as a series of keyframes
25
+ */
26
+ if (Array.isArray(target)) {
27
+ /**
28
+ * Ensure an initial wildcard keyframe is hydrated by the origin.
29
+ * TODO: Support extra wildcard keyframes i.e [1, null, 0]
30
+ */
31
+ if (target[0] === null) {
32
+ target[0] = origin;
33
+ }
34
+ return target;
35
+ }
36
+ else {
37
+ return [origin, target];
38
+ }
39
+ }
40
+
41
+ export { getKeyframes };