use-spring.mjs 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import { frame } from 'motion-dom';
  2. import { noop } from 'motion-utils';
  3. import { useContext, useRef, useInsertionEffect } from 'react';
  4. import { animateValue } from '../animation/animators/MainThreadAnimation.mjs';
  5. import { MotionConfigContext } from '../context/MotionConfigContext.mjs';
  6. import { useConstant } from '../utils/use-constant.mjs';
  7. import { useIsomorphicLayoutEffect } from '../utils/use-isomorphic-effect.mjs';
  8. import { useMotionValue } from './use-motion-value.mjs';
  9. import { isMotionValue } from './utils/is-motion-value.mjs';
  10. function useSpring(source, config = {}) {
  11. const { isStatic } = useContext(MotionConfigContext);
  12. const activeSpringAnimation = useRef(null);
  13. const initialValue = useConstant(() => isMotionValue(source) ? source.get() : source);
  14. const unit = useConstant(() => typeof initialValue === "string"
  15. ? initialValue.replace(/[\d.-]/g, "")
  16. : undefined);
  17. const value = useMotionValue(initialValue);
  18. const latestValue = useRef(initialValue);
  19. const latestSetter = useRef(noop);
  20. const startAnimation = () => {
  21. stopAnimation();
  22. activeSpringAnimation.current = animateValue({
  23. keyframes: [asNumber(value.get()), asNumber(latestValue.current)],
  24. velocity: value.getVelocity(),
  25. type: "spring",
  26. restDelta: 0.001,
  27. restSpeed: 0.01,
  28. ...config,
  29. onUpdate: latestSetter.current,
  30. });
  31. };
  32. const stopAnimation = () => {
  33. if (activeSpringAnimation.current) {
  34. activeSpringAnimation.current.stop();
  35. }
  36. };
  37. useInsertionEffect(() => {
  38. return value.attach((v, set) => {
  39. if (isStatic)
  40. return set(v);
  41. latestValue.current = v;
  42. latestSetter.current = (latest) => set(parseValue(latest, unit));
  43. frame.postRender(startAnimation);
  44. return value.get();
  45. }, stopAnimation);
  46. }, [JSON.stringify(config)]);
  47. useIsomorphicLayoutEffect(() => {
  48. if (isMotionValue(source)) {
  49. return source.on("change", (v) => value.set(parseValue(v, unit)));
  50. }
  51. }, [value, unit]);
  52. return value;
  53. }
  54. function parseValue(v, unit) {
  55. return unit ? v + unit : v;
  56. }
  57. function asNumber(v) {
  58. return typeof v === "number" ? v : parseFloat(v);
  59. }
  60. export { useSpring };