animation-controls.mjs 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import { invariant } from 'motion-utils';
  2. import { setTarget } from '../../render/utils/setters.mjs';
  3. import { animateVisualElement } from '../interfaces/visual-element.mjs';
  4. function stopAnimation(visualElement) {
  5. visualElement.values.forEach((value) => value.stop());
  6. }
  7. function setVariants(visualElement, variantLabels) {
  8. const reversedLabels = [...variantLabels].reverse();
  9. reversedLabels.forEach((key) => {
  10. const variant = visualElement.getVariant(key);
  11. variant && setTarget(visualElement, variant);
  12. if (visualElement.variantChildren) {
  13. visualElement.variantChildren.forEach((child) => {
  14. setVariants(child, variantLabels);
  15. });
  16. }
  17. });
  18. }
  19. function setValues(visualElement, definition) {
  20. if (Array.isArray(definition)) {
  21. return setVariants(visualElement, definition);
  22. }
  23. else if (typeof definition === "string") {
  24. return setVariants(visualElement, [definition]);
  25. }
  26. else {
  27. setTarget(visualElement, definition);
  28. }
  29. }
  30. /**
  31. * @public
  32. */
  33. function animationControls() {
  34. /**
  35. * Track whether the host component has mounted.
  36. */
  37. let hasMounted = false;
  38. /**
  39. * A collection of linked component animation controls.
  40. */
  41. const subscribers = new Set();
  42. const controls = {
  43. subscribe(visualElement) {
  44. subscribers.add(visualElement);
  45. return () => void subscribers.delete(visualElement);
  46. },
  47. start(definition, transitionOverride) {
  48. invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook.");
  49. const animations = [];
  50. subscribers.forEach((visualElement) => {
  51. animations.push(animateVisualElement(visualElement, definition, {
  52. transitionOverride,
  53. }));
  54. });
  55. return Promise.all(animations);
  56. },
  57. set(definition) {
  58. invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook.");
  59. return subscribers.forEach((visualElement) => {
  60. setValues(visualElement, definition);
  61. });
  62. },
  63. stop() {
  64. subscribers.forEach((visualElement) => {
  65. stopAnimation(visualElement);
  66. });
  67. },
  68. mount() {
  69. hasMounted = true;
  70. return () => {
  71. hasMounted = false;
  72. controls.stop();
  73. };
  74. },
  75. };
  76. return controls;
  77. }
  78. export { animationControls, setValues };