Blame view

天文台pc/tianwentai-ui/node_modules/motion-dom/dist/es/value/follow-value.mjs 4.28 KB
bc518174   王天杨   提交两个项目文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
  import { motionValue } from './index.mjs';
  import { JSAnimation } from '../animation/JSAnimation.mjs';
  import { isMotionValue } from './utils/is-motion-value.mjs';
  import { frame } from '../frameloop/frame.mjs';
  
  /**
   * Create a `MotionValue` that animates to its latest value using any transition type.
   * Can either be a value or track another `MotionValue`.
   *
   * ```jsx
   * const x = motionValue(0)
   * const y = followValue(x, { type: "spring", stiffness: 300 })
   * // or with tween
   * const z = followValue(x, { type: "tween", duration: 0.5, ease: "easeOut" })
   * ```
   *
   * @param source - Initial value or MotionValue to track
   * @param options - Animation transition options
   * @returns `MotionValue`
   *
   * @public
   */
  function followValue(source, options) {
      const initialValue = isMotionValue(source) ? source.get() : source;
      const value = motionValue(initialValue);
      attachFollow(value, source, options);
      return value;
  }
  /**
   * Attach an animation to a MotionValue that will animate whenever the value changes.
   * Similar to attachSpring but supports any transition type (spring, tween, inertia, etc.)
   *
   * @param value - The MotionValue to animate
   * @param source - Initial value or MotionValue to track
   * @param options - Animation transition options
   * @returns Cleanup function
   *
   * @public
   */
  function attachFollow(value, source, options = {}) {
      const initialValue = value.get();
      let activeAnimation = null;
      let latestValue = initialValue;
      let latestSetter;
      const unit = typeof initialValue === "string"
          ? initialValue.replace(/[\d.-]/g, "")
          : undefined;
      const stopAnimation = () => {
          if (activeAnimation) {
              activeAnimation.stop();
              activeAnimation = null;
          }
          value.animation = undefined;
      };
      const startAnimation = () => {
          const currentValue = asNumber(value.get());
          const targetValue = asNumber(latestValue);
          // Don't animate if we're already at the target
          if (currentValue === targetValue) {
              stopAnimation();
              return;
          }
          // Use the running animation's analytical velocity for accuracy,
          // falling back to the MotionValue's velocity for the initial animation.
          // This prevents systematic velocity loss at high frame rates (240hz+).
          const velocity = activeAnimation
              ? activeAnimation.getGeneratorVelocity()
              : value.getVelocity();
          stopAnimation();
          activeAnimation = new JSAnimation({
              keyframes: [currentValue, targetValue],
              velocity,
              // Default to spring if no type specified (matches useSpring behavior)
              type: "spring",
              restDelta: 0.001,
              restSpeed: 0.01,
              ...options,
              onUpdate: latestSetter,
          });
      };
      // Use a stable function reference so the frame loop Set deduplicates
      // multiple calls within the same frame (e.g. rapid mouse events)
      const scheduleAnimation = () => {
          startAnimation();
          value.animation = activeAnimation ?? undefined;
          value["events"].animationStart?.notify();
          activeAnimation?.then(() => {
              value.animation = undefined;
              value["events"].animationComplete?.notify();
          });
      };
      value.attach((v, set) => {
          latestValue = v;
          latestSetter = (latest) => set(parseValue(latest, unit));
          frame.postRender(scheduleAnimation);
      }, stopAnimation);
      if (isMotionValue(source)) {
          let skipNextAnimation = options.skipInitialAnimation === true;
          const removeSourceOnChange = source.on("change", (v) => {
              if (skipNextAnimation) {
                  skipNextAnimation = false;
                  value.jump(parseValue(v, unit), false);
              }
              else {
                  value.set(parseValue(v, unit));
              }
          });
          const removeValueOnDestroy = value.on("destroy", removeSourceOnChange);
          return () => {
              removeSourceOnChange();
              removeValueOnDestroy();
          };
      }
      return stopAnimation;
  }
  function parseValue(v, unit) {
      return unit ? v + unit : v;
  }
  function asNumber(v) {
      return typeof v === "number" ? v : parseFloat(v);
  }
  
  export { attachFollow, followValue };
  //# sourceMappingURL=follow-value.mjs.map