framer-motion 7.9.1 → 7.10.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.
package/dist/cjs/index.js CHANGED
@@ -2124,7 +2124,7 @@ class MotionValue {
2124
2124
  * This will be replaced by the build step with the latest version number.
2125
2125
  * When MotionValues are provided to motion components, warn if versions are mixed.
2126
2126
  */
2127
- this.version = "7.9.1";
2127
+ this.version = "7.10.0";
2128
2128
  /**
2129
2129
  * Duration, in milliseconds, since last updating frame.
2130
2130
  *
@@ -2137,24 +2137,6 @@ class MotionValue {
2137
2137
  * @internal
2138
2138
  */
2139
2139
  this.lastUpdated = 0;
2140
- /**
2141
- * Functions to notify when the `MotionValue` updates.
2142
- *
2143
- * @internal
2144
- */
2145
- this.updateSubscribers = new SubscriptionManager();
2146
- /**
2147
- * Functions to notify when the velocity updates.
2148
- *
2149
- * @internal
2150
- */
2151
- this.velocityUpdateSubscribers = new SubscriptionManager();
2152
- /**
2153
- * Functions to notify when the `MotionValue` updates and `render` is set to `true`.
2154
- *
2155
- * @internal
2156
- */
2157
- this.renderSubscribers = new SubscriptionManager();
2158
2140
  /**
2159
2141
  * Tracks whether this value can output a velocity. Currently this is only true
2160
2142
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -2163,6 +2145,10 @@ class MotionValue {
2163
2145
  * @internal
2164
2146
  */
2165
2147
  this.canTrackVelocity = false;
2148
+ /**
2149
+ * An object containing a SubscriptionManager for each active event.
2150
+ */
2151
+ this.events = {};
2166
2152
  this.updateAndNotify = (v, render = true) => {
2167
2153
  this.prev = this.current;
2168
2154
  this.current = v;
@@ -2174,16 +2160,16 @@ class MotionValue {
2174
2160
  sync.postRender(this.scheduleVelocityCheck);
2175
2161
  }
2176
2162
  // Update update subscribers
2177
- if (this.prev !== this.current) {
2178
- this.updateSubscribers.notify(this.current);
2163
+ if (this.prev !== this.current && this.events.change) {
2164
+ this.events.change.notify(this.current);
2179
2165
  }
2180
2166
  // Update velocity subscribers
2181
- if (this.velocityUpdateSubscribers.getSize()) {
2182
- this.velocityUpdateSubscribers.notify(this.getVelocity());
2167
+ if (this.events.velocityChange) {
2168
+ this.events.velocityChange.notify(this.getVelocity());
2183
2169
  }
2184
2170
  // Update render subscribers
2185
- if (render) {
2186
- this.renderSubscribers.notify(this.current);
2171
+ if (render && this.events.renderRequest) {
2172
+ this.events.renderRequest.notify(this.current);
2187
2173
  }
2188
2174
  };
2189
2175
  /**
@@ -2207,7 +2193,9 @@ class MotionValue {
2207
2193
  this.velocityCheck = ({ timestamp }) => {
2208
2194
  if (timestamp !== this.lastUpdated) {
2209
2195
  this.prev = this.current;
2210
- this.velocityUpdateSubscribers.notify(this.getVelocity());
2196
+ if (this.events.velocityChange) {
2197
+ this.events.velocityChange.notify(this.getVelocity());
2198
+ }
2211
2199
  }
2212
2200
  };
2213
2201
  this.hasAnimated = false;
@@ -2237,8 +2225,8 @@ class MotionValue {
2237
2225
  * opacity.set(newOpacity)
2238
2226
  * }
2239
2227
  *
2240
- * const unsubscribeX = x.onChange(updateOpacity)
2241
- * const unsubscribeY = y.onChange(updateOpacity)
2228
+ * const unsubscribeX = x.on("change", updateOpacity)
2229
+ * const unsubscribeY = y.on("change", updateOpacity)
2242
2230
  *
2243
2231
  * return () => {
2244
2232
  * unsubscribeX()
@@ -2261,26 +2249,21 @@ class MotionValue {
2261
2249
  * @param subscriber - A function that receives the latest value.
2262
2250
  * @returns A function that, when called, will cancel this subscription.
2263
2251
  *
2264
- * @public
2252
+ * @deprecated
2265
2253
  */
2266
2254
  onChange(subscription) {
2267
- return this.updateSubscribers.add(subscription);
2255
+ return this.on("change", subscription);
2268
2256
  }
2269
- clearListeners() {
2270
- this.updateSubscribers.clear();
2257
+ on(eventName, callback) {
2258
+ if (!this.events[eventName]) {
2259
+ this.events[eventName] = new SubscriptionManager();
2260
+ }
2261
+ return this.events[eventName].add(callback);
2271
2262
  }
2272
- /**
2273
- * Adds a function that will be notified when the `MotionValue` requests a render.
2274
- *
2275
- * @param subscriber - A function that's provided the latest value.
2276
- * @returns A function that, when called, will cancel this subscription.
2277
- *
2278
- * @internal
2279
- */
2280
- onRenderRequest(subscription) {
2281
- // Render immediately
2282
- subscription(this.get());
2283
- return this.renderSubscribers.add(subscription);
2263
+ clearListeners() {
2264
+ for (const eventManagers in this.events) {
2265
+ this.events[eventManagers].clear();
2266
+ }
2284
2267
  }
2285
2268
  /**
2286
2269
  * Attaches a passive effect to the `MotionValue`.
@@ -2366,7 +2349,15 @@ class MotionValue {
2366
2349
  return new Promise((resolve) => {
2367
2350
  this.hasAnimated = true;
2368
2351
  this.stopAnimation = animation(resolve);
2369
- }).then(() => this.clearAnimation());
2352
+ if (this.events.animationStart) {
2353
+ this.events.animationStart.notify();
2354
+ }
2355
+ }).then(() => {
2356
+ if (this.events.animationComplete) {
2357
+ this.events.animationComplete.notify();
2358
+ }
2359
+ this.clearAnimation();
2360
+ });
2370
2361
  }
2371
2362
  /**
2372
2363
  * Stop the currently active animation.
@@ -2374,8 +2365,12 @@ class MotionValue {
2374
2365
  * @public
2375
2366
  */
2376
2367
  stop() {
2377
- if (this.stopAnimation)
2368
+ if (this.stopAnimation) {
2378
2369
  this.stopAnimation();
2370
+ if (this.events.animationCancel) {
2371
+ this.events.animationCancel.notify();
2372
+ }
2373
+ }
2379
2374
  this.clearAnimation();
2380
2375
  }
2381
2376
  /**
@@ -2399,8 +2394,7 @@ class MotionValue {
2399
2394
  * @public
2400
2395
  */
2401
2396
  destroy() {
2402
- this.updateSubscribers.clear();
2403
- this.renderSubscribers.clear();
2397
+ this.clearListeners();
2404
2398
  this.stop();
2405
2399
  }
2406
2400
  }
@@ -5944,7 +5938,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5944
5938
  * and warn against mismatches.
5945
5939
  */
5946
5940
  if (process.env.NODE_ENV === "development") {
5947
- warnOnce(nextValue.version === "7.9.1", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.9.1 may not work as expected.`);
5941
+ warnOnce(nextValue.version === "7.10.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.10.0 may not work as expected.`);
5948
5942
  }
5949
5943
  }
5950
5944
  else if (isMotionValue(prevValue)) {
@@ -6147,7 +6141,7 @@ class VisualElement {
6147
6141
  }
6148
6142
  bindToMotionValue(key, value) {
6149
6143
  const valueIsTransform = transformProps.has(key);
6150
- const removeOnChange = value.onChange((latestValue) => {
6144
+ const removeOnChange = value.on("change", (latestValue) => {
6151
6145
  this.latestValues[key] = latestValue;
6152
6146
  this.props.onUpdate &&
6153
6147
  sync.update(this.notifyUpdate, false, true);
@@ -6155,7 +6149,7 @@ class VisualElement {
6155
6149
  this.projection.isTransformDirty = true;
6156
6150
  }
6157
6151
  });
6158
- const removeOnRenderRequest = value.onRenderRequest(this.scheduleRender);
6152
+ const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender);
6159
6153
  this.valueSubscriptions.set(key, () => {
6160
6154
  removeOnChange();
6161
6155
  removeOnRenderRequest();
@@ -9037,7 +9031,7 @@ function useMotionValue(initial) {
9037
9031
  const { isStatic } = React.useContext(MotionConfigContext);
9038
9032
  if (isStatic) {
9039
9033
  const [, setLatest] = React.useState(initial);
9040
- React.useEffect(() => value.onChange(setLatest), []);
9034
+ React.useEffect(() => value.on("change", setLatest), []);
9041
9035
  }
9042
9036
  return value;
9043
9037
  }
@@ -9064,13 +9058,13 @@ function useOnChange(value, callback) {
9064
9058
  useIsomorphicLayoutEffect(() => {
9065
9059
  if (isMotionValue(value)) {
9066
9060
  callback(value.get());
9067
- return value.onChange(callback);
9061
+ return value.on("change", callback);
9068
9062
  }
9069
9063
  }, [value, callback]);
9070
9064
  }
9071
9065
  function useMultiOnChange(values, handler, cleanup) {
9072
9066
  useIsomorphicLayoutEffect(() => {
9073
- const subscriptions = values.map((value) => value.onChange(handler));
9067
+ const subscriptions = values.map((value) => value.on("change", handler));
9074
9068
  return () => {
9075
9069
  subscriptions.forEach((unsubscribe) => unsubscribe());
9076
9070
  cleanup();
@@ -9277,7 +9271,7 @@ function useSpring(source, config = {}) {
9277
9271
  function useVelocity(value) {
9278
9272
  const velocity = useMotionValue(value.getVelocity());
9279
9273
  React.useEffect(() => {
9280
- return value.velocityUpdateSubscribers.add((newVelocity) => {
9274
+ return value.on("velocityChange", (newVelocity) => {
9281
9275
  velocity.set(newVelocity);
9282
9276
  });
9283
9277
  }, [value]);
@@ -9390,6 +9384,10 @@ function useWillChange() {
9390
9384
  return useConstant(() => new WillChangeMotionValue("auto"));
9391
9385
  }
9392
9386
 
9387
+ function useMotionValueEvent(value, event, callback) {
9388
+ useIsomorphicLayoutEffect(() => value.on(event, callback), [value, event, callback]);
9389
+ }
9390
+
9393
9391
  /**
9394
9392
  * A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting.
9395
9393
  *
@@ -9946,6 +9944,7 @@ exports.useIsPresent = useIsPresent;
9946
9944
  exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
9947
9945
  exports.useMotionTemplate = useMotionTemplate;
9948
9946
  exports.useMotionValue = useMotionValue;
9947
+ exports.useMotionValueEvent = useMotionValueEvent;
9949
9948
  exports.usePresence = usePresence;
9950
9949
  exports.useReducedMotion = useReducedMotion;
9951
9950
  exports.useReducedMotionConfig = useReducedMotionConfig;
package/dist/es/index.mjs CHANGED
@@ -20,6 +20,7 @@ export { useElementScroll } from './value/scroll/use-element-scroll.mjs';
20
20
  export { useViewportScroll } from './value/scroll/use-viewport-scroll.mjs';
21
21
  export { useTime } from './value/use-time.mjs';
22
22
  export { useWillChange } from './value/use-will-change/index.mjs';
23
+ export { useMotionValueEvent } from './utils/use-motion-value-event.mjs';
23
24
  export { useReducedMotion } from './utils/reduced-motion/use-reduced-motion.mjs';
24
25
  export { useReducedMotionConfig } from './utils/reduced-motion/use-reduced-motion-config.mjs';
25
26
  export { animationControls } from './animation/hooks/animation-controls.mjs';
@@ -183,7 +183,7 @@ class VisualElement {
183
183
  }
184
184
  bindToMotionValue(key, value) {
185
185
  const valueIsTransform = transformProps.has(key);
186
- const removeOnChange = value.onChange((latestValue) => {
186
+ const removeOnChange = value.on("change", (latestValue) => {
187
187
  this.latestValues[key] = latestValue;
188
188
  this.props.onUpdate &&
189
189
  sync.update(this.notifyUpdate, false, true);
@@ -191,7 +191,7 @@ class VisualElement {
191
191
  this.projection.isTransformDirty = true;
192
192
  }
193
193
  });
194
- const removeOnRenderRequest = value.onRenderRequest(this.scheduleRender);
194
+ const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender);
195
195
  this.valueSubscriptions.set(key, () => {
196
196
  removeOnChange();
197
197
  removeOnRenderRequest();
@@ -22,7 +22,7 @@ function updateMotionValuesFromProps(element, next, prev) {
22
22
  * and warn against mismatches.
23
23
  */
24
24
  if (process.env.NODE_ENV === "development") {
25
- warnOnce(nextValue.version === "7.9.1", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.9.1 may not work as expected.`);
25
+ warnOnce(nextValue.version === "7.10.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.10.0 may not work as expected.`);
26
26
  }
27
27
  }
28
28
  else if (isMotionValue(prevValue)) {
@@ -0,0 +1,7 @@
1
+ import { useIsomorphicLayoutEffect } from './use-isomorphic-effect.mjs';
2
+
3
+ function useMotionValueEvent(value, event, callback) {
4
+ useIsomorphicLayoutEffect(() => value.on(event, callback), [value, event, callback]);
5
+ }
6
+
7
+ export { useMotionValueEvent };
@@ -25,7 +25,7 @@ class MotionValue {
25
25
  * This will be replaced by the build step with the latest version number.
26
26
  * When MotionValues are provided to motion components, warn if versions are mixed.
27
27
  */
28
- this.version = "7.9.1";
28
+ this.version = "7.10.0";
29
29
  /**
30
30
  * Duration, in milliseconds, since last updating frame.
31
31
  *
@@ -38,24 +38,6 @@ class MotionValue {
38
38
  * @internal
39
39
  */
40
40
  this.lastUpdated = 0;
41
- /**
42
- * Functions to notify when the `MotionValue` updates.
43
- *
44
- * @internal
45
- */
46
- this.updateSubscribers = new SubscriptionManager();
47
- /**
48
- * Functions to notify when the velocity updates.
49
- *
50
- * @internal
51
- */
52
- this.velocityUpdateSubscribers = new SubscriptionManager();
53
- /**
54
- * Functions to notify when the `MotionValue` updates and `render` is set to `true`.
55
- *
56
- * @internal
57
- */
58
- this.renderSubscribers = new SubscriptionManager();
59
41
  /**
60
42
  * Tracks whether this value can output a velocity. Currently this is only true
61
43
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -64,6 +46,10 @@ class MotionValue {
64
46
  * @internal
65
47
  */
66
48
  this.canTrackVelocity = false;
49
+ /**
50
+ * An object containing a SubscriptionManager for each active event.
51
+ */
52
+ this.events = {};
67
53
  this.updateAndNotify = (v, render = true) => {
68
54
  this.prev = this.current;
69
55
  this.current = v;
@@ -75,16 +61,16 @@ class MotionValue {
75
61
  sync.postRender(this.scheduleVelocityCheck);
76
62
  }
77
63
  // Update update subscribers
78
- if (this.prev !== this.current) {
79
- this.updateSubscribers.notify(this.current);
64
+ if (this.prev !== this.current && this.events.change) {
65
+ this.events.change.notify(this.current);
80
66
  }
81
67
  // Update velocity subscribers
82
- if (this.velocityUpdateSubscribers.getSize()) {
83
- this.velocityUpdateSubscribers.notify(this.getVelocity());
68
+ if (this.events.velocityChange) {
69
+ this.events.velocityChange.notify(this.getVelocity());
84
70
  }
85
71
  // Update render subscribers
86
- if (render) {
87
- this.renderSubscribers.notify(this.current);
72
+ if (render && this.events.renderRequest) {
73
+ this.events.renderRequest.notify(this.current);
88
74
  }
89
75
  };
90
76
  /**
@@ -108,7 +94,9 @@ class MotionValue {
108
94
  this.velocityCheck = ({ timestamp }) => {
109
95
  if (timestamp !== this.lastUpdated) {
110
96
  this.prev = this.current;
111
- this.velocityUpdateSubscribers.notify(this.getVelocity());
97
+ if (this.events.velocityChange) {
98
+ this.events.velocityChange.notify(this.getVelocity());
99
+ }
112
100
  }
113
101
  };
114
102
  this.hasAnimated = false;
@@ -138,8 +126,8 @@ class MotionValue {
138
126
  * opacity.set(newOpacity)
139
127
  * }
140
128
  *
141
- * const unsubscribeX = x.onChange(updateOpacity)
142
- * const unsubscribeY = y.onChange(updateOpacity)
129
+ * const unsubscribeX = x.on("change", updateOpacity)
130
+ * const unsubscribeY = y.on("change", updateOpacity)
143
131
  *
144
132
  * return () => {
145
133
  * unsubscribeX()
@@ -162,26 +150,21 @@ class MotionValue {
162
150
  * @param subscriber - A function that receives the latest value.
163
151
  * @returns A function that, when called, will cancel this subscription.
164
152
  *
165
- * @public
153
+ * @deprecated
166
154
  */
167
155
  onChange(subscription) {
168
- return this.updateSubscribers.add(subscription);
156
+ return this.on("change", subscription);
169
157
  }
170
- clearListeners() {
171
- this.updateSubscribers.clear();
158
+ on(eventName, callback) {
159
+ if (!this.events[eventName]) {
160
+ this.events[eventName] = new SubscriptionManager();
161
+ }
162
+ return this.events[eventName].add(callback);
172
163
  }
173
- /**
174
- * Adds a function that will be notified when the `MotionValue` requests a render.
175
- *
176
- * @param subscriber - A function that's provided the latest value.
177
- * @returns A function that, when called, will cancel this subscription.
178
- *
179
- * @internal
180
- */
181
- onRenderRequest(subscription) {
182
- // Render immediately
183
- subscription(this.get());
184
- return this.renderSubscribers.add(subscription);
164
+ clearListeners() {
165
+ for (const eventManagers in this.events) {
166
+ this.events[eventManagers].clear();
167
+ }
185
168
  }
186
169
  /**
187
170
  * Attaches a passive effect to the `MotionValue`.
@@ -267,7 +250,15 @@ class MotionValue {
267
250
  return new Promise((resolve) => {
268
251
  this.hasAnimated = true;
269
252
  this.stopAnimation = animation(resolve);
270
- }).then(() => this.clearAnimation());
253
+ if (this.events.animationStart) {
254
+ this.events.animationStart.notify();
255
+ }
256
+ }).then(() => {
257
+ if (this.events.animationComplete) {
258
+ this.events.animationComplete.notify();
259
+ }
260
+ this.clearAnimation();
261
+ });
271
262
  }
272
263
  /**
273
264
  * Stop the currently active animation.
@@ -275,8 +266,12 @@ class MotionValue {
275
266
  * @public
276
267
  */
277
268
  stop() {
278
- if (this.stopAnimation)
269
+ if (this.stopAnimation) {
279
270
  this.stopAnimation();
271
+ if (this.events.animationCancel) {
272
+ this.events.animationCancel.notify();
273
+ }
274
+ }
280
275
  this.clearAnimation();
281
276
  }
282
277
  /**
@@ -300,8 +295,7 @@ class MotionValue {
300
295
  * @public
301
296
  */
302
297
  destroy() {
303
- this.updateSubscribers.clear();
304
- this.renderSubscribers.clear();
298
+ this.clearListeners();
305
299
  this.stop();
306
300
  }
307
301
  }
@@ -30,7 +30,7 @@ function useMotionValue(initial) {
30
30
  const { isStatic } = useContext(MotionConfigContext);
31
31
  if (isStatic) {
32
32
  const [, setLatest] = useState(initial);
33
- useEffect(() => value.onChange(setLatest), []);
33
+ useEffect(() => value.on("change", setLatest), []);
34
34
  }
35
35
  return value;
36
36
  }
@@ -5,13 +5,13 @@ function useOnChange(value, callback) {
5
5
  useIsomorphicLayoutEffect(() => {
6
6
  if (isMotionValue(value)) {
7
7
  callback(value.get());
8
- return value.onChange(callback);
8
+ return value.on("change", callback);
9
9
  }
10
10
  }, [value, callback]);
11
11
  }
12
12
  function useMultiOnChange(values, handler, cleanup) {
13
13
  useIsomorphicLayoutEffect(() => {
14
- const subscriptions = values.map((value) => value.onChange(handler));
14
+ const subscriptions = values.map((value) => value.on("change", handler));
15
15
  return () => {
16
16
  subscriptions.forEach((unsubscribe) => unsubscribe());
17
17
  cleanup();
@@ -15,7 +15,7 @@ import { useMotionValue } from './use-motion-value.mjs';
15
15
  function useVelocity(value) {
16
16
  const velocity = useMotionValue(value.getVelocity());
17
17
  useEffect(() => {
18
- return value.velocityUpdateSubscribers.add((newVelocity) => {
18
+ return value.on("velocityChange", (newVelocity) => {
19
19
  velocity.set(newVelocity);
20
20
  });
21
21
  }, [value]);