motion-value.mjs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { getValueTransition, frame, GroupAnimationWithThen } from 'motion-dom';
  2. import { secondsToMilliseconds, MotionGlobalConfig } from 'motion-utils';
  3. import { instantAnimationState } from '../../utils/use-instant-transition-state.mjs';
  4. import { AcceleratedAnimation } from '../animators/AcceleratedAnimation.mjs';
  5. import { MainThreadAnimation } from '../animators/MainThreadAnimation.mjs';
  6. import { getFinalKeyframe } from '../animators/waapi/utils/get-final-keyframe.mjs';
  7. import { getDefaultTransition } from '../utils/default-transitions.mjs';
  8. import { isTransitionDefined } from '../utils/is-transition-defined.mjs';
  9. const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
  10. const valueTransition = getValueTransition(transition, name) || {};
  11. /**
  12. * Most transition values are currently completely overwritten by value-specific
  13. * transitions. In the future it'd be nicer to blend these transitions. But for now
  14. * delay actually does inherit from the root transition if not value-specific.
  15. */
  16. const delay = valueTransition.delay || transition.delay || 0;
  17. /**
  18. * Elapsed isn't a public transition option but can be passed through from
  19. * optimized appear effects in milliseconds.
  20. */
  21. let { elapsed = 0 } = transition;
  22. elapsed = elapsed - secondsToMilliseconds(delay);
  23. let options = {
  24. keyframes: Array.isArray(target) ? target : [null, target],
  25. ease: "easeOut",
  26. velocity: value.getVelocity(),
  27. ...valueTransition,
  28. delay: -elapsed,
  29. onUpdate: (v) => {
  30. value.set(v);
  31. valueTransition.onUpdate && valueTransition.onUpdate(v);
  32. },
  33. onComplete: () => {
  34. onComplete();
  35. valueTransition.onComplete && valueTransition.onComplete();
  36. },
  37. name,
  38. motionValue: value,
  39. element: isHandoff ? undefined : element,
  40. };
  41. /**
  42. * If there's no transition defined for this value, we can generate
  43. * unique transition settings for this value.
  44. */
  45. if (!isTransitionDefined(valueTransition)) {
  46. options = {
  47. ...options,
  48. ...getDefaultTransition(name, options),
  49. };
  50. }
  51. /**
  52. * Both WAAPI and our internal animation functions use durations
  53. * as defined by milliseconds, while our external API defines them
  54. * as seconds.
  55. */
  56. if (options.duration) {
  57. options.duration = secondsToMilliseconds(options.duration);
  58. }
  59. if (options.repeatDelay) {
  60. options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
  61. }
  62. if (options.from !== undefined) {
  63. options.keyframes[0] = options.from;
  64. }
  65. let shouldSkip = false;
  66. if (options.type === false ||
  67. (options.duration === 0 && !options.repeatDelay)) {
  68. options.duration = 0;
  69. if (options.delay === 0) {
  70. shouldSkip = true;
  71. }
  72. }
  73. if (instantAnimationState.current ||
  74. MotionGlobalConfig.skipAnimations) {
  75. shouldSkip = true;
  76. options.duration = 0;
  77. options.delay = 0;
  78. }
  79. /**
  80. * If the transition type or easing has been explicitly set by the user
  81. * then we don't want to allow flattening the animation.
  82. */
  83. options.allowFlatten = !valueTransition.type && !valueTransition.ease;
  84. /**
  85. * If we can or must skip creating the animation, and apply only
  86. * the final keyframe, do so. We also check once keyframes are resolved but
  87. * this early check prevents the need to create an animation at all.
  88. */
  89. if (shouldSkip && !isHandoff && value.get() !== undefined) {
  90. const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition);
  91. if (finalKeyframe !== undefined) {
  92. frame.update(() => {
  93. options.onUpdate(finalKeyframe);
  94. options.onComplete();
  95. });
  96. // We still want to return some animation controls here rather
  97. // than returning undefined
  98. return new GroupAnimationWithThen([]);
  99. }
  100. }
  101. /**
  102. * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
  103. * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
  104. * optimised animation.
  105. */
  106. if (!isHandoff && AcceleratedAnimation.supports(options)) {
  107. return new AcceleratedAnimation(options);
  108. }
  109. else {
  110. return new MainThreadAnimation(options);
  111. }
  112. };
  113. export { animateMotionValue };