framer-motion 11.0.0-alpha.1 → 11.0.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.
@@ -57,6 +57,177 @@
57
57
  const optimizedAppearDataId = "framerAppearId";
58
58
  const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
59
59
 
60
+ const MotionGlobalConfig = {
61
+ skipAnimations: false,
62
+ useManualTiming: false,
63
+ };
64
+
65
+ class Queue {
66
+ constructor() {
67
+ this.order = [];
68
+ this.scheduled = new Set();
69
+ }
70
+ add(process) {
71
+ if (!this.scheduled.has(process)) {
72
+ this.scheduled.add(process);
73
+ this.order.push(process);
74
+ return true;
75
+ }
76
+ }
77
+ remove(process) {
78
+ const index = this.order.indexOf(process);
79
+ if (index !== -1) {
80
+ this.order.splice(index, 1);
81
+ this.scheduled.delete(process);
82
+ }
83
+ }
84
+ clear() {
85
+ this.order.length = 0;
86
+ this.scheduled.clear();
87
+ }
88
+ }
89
+ function createRenderStep(runNextFrame) {
90
+ /**
91
+ * We create and reuse two queues, one to queue jobs for the current frame
92
+ * and one for the next. We reuse to avoid triggering GC after x frames.
93
+ */
94
+ let thisFrame = new Queue();
95
+ let nextFrame = new Queue();
96
+ let numToRun = 0;
97
+ /**
98
+ * Track whether we're currently processing jobs in this step. This way
99
+ * we can decide whether to schedule new jobs for this frame or next.
100
+ */
101
+ let isProcessing = false;
102
+ let flushNextFrame = false;
103
+ /**
104
+ * A set of processes which were marked keepAlive when scheduled.
105
+ */
106
+ const toKeepAlive = new WeakSet();
107
+ const step = {
108
+ /**
109
+ * Schedule a process to run on the next frame.
110
+ */
111
+ schedule: (callback, keepAlive = false, immediate = false) => {
112
+ const addToCurrentFrame = immediate && isProcessing;
113
+ const queue = addToCurrentFrame ? thisFrame : nextFrame;
114
+ if (keepAlive)
115
+ toKeepAlive.add(callback);
116
+ if (queue.add(callback) && addToCurrentFrame && isProcessing) {
117
+ // If we're adding it to the currently running queue, update its measured size
118
+ numToRun = thisFrame.order.length;
119
+ }
120
+ return callback;
121
+ },
122
+ /**
123
+ * Cancel the provided callback from running on the next frame.
124
+ */
125
+ cancel: (callback) => {
126
+ nextFrame.remove(callback);
127
+ toKeepAlive.delete(callback);
128
+ },
129
+ /**
130
+ * Execute all schedule callbacks.
131
+ */
132
+ process: (frameData) => {
133
+ /**
134
+ * If we're already processing we've probably been triggered by a flushSync
135
+ * inside an existing process. Instead of executing, mark flushNextFrame
136
+ * as true and ensure we flush the following frame at the end of this one.
137
+ */
138
+ if (isProcessing) {
139
+ flushNextFrame = true;
140
+ return;
141
+ }
142
+ isProcessing = true;
143
+ [thisFrame, nextFrame] = [nextFrame, thisFrame];
144
+ // Clear the next frame queue
145
+ nextFrame.clear();
146
+ // Execute this frame
147
+ numToRun = thisFrame.order.length;
148
+ if (numToRun) {
149
+ for (let i = 0; i < numToRun; i++) {
150
+ const callback = thisFrame.order[i];
151
+ if (toKeepAlive.has(callback)) {
152
+ step.schedule(callback);
153
+ runNextFrame();
154
+ }
155
+ callback(frameData);
156
+ }
157
+ }
158
+ isProcessing = false;
159
+ if (flushNextFrame) {
160
+ flushNextFrame = false;
161
+ step.process(frameData);
162
+ }
163
+ },
164
+ };
165
+ return step;
166
+ }
167
+
168
+ const stepsOrder = [
169
+ "prepare",
170
+ "read",
171
+ "update",
172
+ "preRender",
173
+ "render",
174
+ "postRender",
175
+ ];
176
+ const maxElapsed$1 = 40;
177
+ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
178
+ let runNextFrame = false;
179
+ let useDefaultElapsed = true;
180
+ const state = {
181
+ delta: 0,
182
+ timestamp: 0,
183
+ isProcessing: false,
184
+ };
185
+ const steps = stepsOrder.reduce((acc, key) => {
186
+ acc[key] = createRenderStep(() => (runNextFrame = true));
187
+ return acc;
188
+ }, {});
189
+ const processStep = (stepId) => {
190
+ steps[stepId].process(state);
191
+ };
192
+ const processBatch = () => {
193
+ const timestamp = MotionGlobalConfig.useManualTiming
194
+ ? state.timestamp
195
+ : performance.now();
196
+ runNextFrame = false;
197
+ state.delta = useDefaultElapsed
198
+ ? 1000 / 60
199
+ : Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1);
200
+ state.timestamp = timestamp;
201
+ state.isProcessing = true;
202
+ stepsOrder.forEach(processStep);
203
+ state.isProcessing = false;
204
+ if (runNextFrame && allowKeepAlive) {
205
+ useDefaultElapsed = false;
206
+ scheduleNextBatch(processBatch);
207
+ }
208
+ };
209
+ const wake = () => {
210
+ runNextFrame = true;
211
+ useDefaultElapsed = true;
212
+ if (!state.isProcessing) {
213
+ scheduleNextBatch(processBatch);
214
+ }
215
+ };
216
+ const schedule = stepsOrder.reduce((acc, key) => {
217
+ const step = steps[key];
218
+ acc[key] = (process, keepAlive = false, immediate = false) => {
219
+ if (!runNextFrame)
220
+ wake();
221
+ return step.schedule(process, keepAlive, immediate);
222
+ };
223
+ return acc;
224
+ }, {});
225
+ const cancel = (process) => stepsOrder.forEach((key) => steps[key].cancel(process));
226
+ return { schedule, cancel, state, steps };
227
+ }
228
+
229
+ const { schedule: microtask, cancel: cancelMicrotask } = createRenderBatcher(queueMicrotask, false);
230
+
60
231
  function useVisualElement(Component, visualState, props, createVisualElement) {
61
232
  const { visualElement: parent } = React.useContext(MotionContext);
62
233
  const lazyContext = React.useContext(LazyContext);
@@ -91,7 +262,7 @@
91
262
  useIsomorphicLayoutEffect(() => {
92
263
  if (!visualElement)
93
264
  return;
94
- visualElement.render();
265
+ microtask.postRender(visualElement.render);
95
266
  /**
96
267
  * Ideally this function would always run in a useEffect.
97
268
  *
@@ -1221,173 +1392,6 @@
1221
1392
 
1222
1393
  const noop = (any) => any;
1223
1394
 
1224
- const MotionGlobalConfig = {
1225
- skipAnimations: false,
1226
- useManualTiming: false,
1227
- };
1228
-
1229
- class Queue {
1230
- constructor() {
1231
- this.order = [];
1232
- this.scheduled = new Set();
1233
- }
1234
- add(process) {
1235
- if (!this.scheduled.has(process)) {
1236
- this.scheduled.add(process);
1237
- this.order.push(process);
1238
- return true;
1239
- }
1240
- }
1241
- remove(process) {
1242
- const index = this.order.indexOf(process);
1243
- if (index !== -1) {
1244
- this.order.splice(index, 1);
1245
- this.scheduled.delete(process);
1246
- }
1247
- }
1248
- clear() {
1249
- this.order.length = 0;
1250
- this.scheduled.clear();
1251
- }
1252
- }
1253
- function createRenderStep(runNextFrame) {
1254
- /**
1255
- * We create and reuse two queues, one to queue jobs for the current frame
1256
- * and one for the next. We reuse to avoid triggering GC after x frames.
1257
- */
1258
- let thisFrame = new Queue();
1259
- let nextFrame = new Queue();
1260
- let numToRun = 0;
1261
- /**
1262
- * Track whether we're currently processing jobs in this step. This way
1263
- * we can decide whether to schedule new jobs for this frame or next.
1264
- */
1265
- let isProcessing = false;
1266
- let flushNextFrame = false;
1267
- /**
1268
- * A set of processes which were marked keepAlive when scheduled.
1269
- */
1270
- const toKeepAlive = new WeakSet();
1271
- const step = {
1272
- /**
1273
- * Schedule a process to run on the next frame.
1274
- */
1275
- schedule: (callback, keepAlive = false, immediate = false) => {
1276
- const addToCurrentFrame = immediate && isProcessing;
1277
- const queue = addToCurrentFrame ? thisFrame : nextFrame;
1278
- if (keepAlive)
1279
- toKeepAlive.add(callback);
1280
- if (queue.add(callback) && addToCurrentFrame && isProcessing) {
1281
- // If we're adding it to the currently running queue, update its measured size
1282
- numToRun = thisFrame.order.length;
1283
- }
1284
- return callback;
1285
- },
1286
- /**
1287
- * Cancel the provided callback from running on the next frame.
1288
- */
1289
- cancel: (callback) => {
1290
- nextFrame.remove(callback);
1291
- toKeepAlive.delete(callback);
1292
- },
1293
- /**
1294
- * Execute all schedule callbacks.
1295
- */
1296
- process: (frameData) => {
1297
- /**
1298
- * If we're already processing we've probably been triggered by a flushSync
1299
- * inside an existing process. Instead of executing, mark flushNextFrame
1300
- * as true and ensure we flush the following frame at the end of this one.
1301
- */
1302
- if (isProcessing) {
1303
- flushNextFrame = true;
1304
- return;
1305
- }
1306
- isProcessing = true;
1307
- [thisFrame, nextFrame] = [nextFrame, thisFrame];
1308
- // Clear the next frame queue
1309
- nextFrame.clear();
1310
- // Execute this frame
1311
- numToRun = thisFrame.order.length;
1312
- if (numToRun) {
1313
- for (let i = 0; i < numToRun; i++) {
1314
- const callback = thisFrame.order[i];
1315
- if (toKeepAlive.has(callback)) {
1316
- step.schedule(callback);
1317
- runNextFrame();
1318
- }
1319
- callback(frameData);
1320
- }
1321
- }
1322
- isProcessing = false;
1323
- if (flushNextFrame) {
1324
- flushNextFrame = false;
1325
- step.process(frameData);
1326
- }
1327
- },
1328
- };
1329
- return step;
1330
- }
1331
-
1332
- const stepsOrder = [
1333
- "prepare",
1334
- "read",
1335
- "update",
1336
- "preRender",
1337
- "render",
1338
- "postRender",
1339
- ];
1340
- const maxElapsed$1 = 40;
1341
- function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
1342
- let runNextFrame = false;
1343
- let useDefaultElapsed = true;
1344
- const state = {
1345
- delta: 0,
1346
- timestamp: 0,
1347
- isProcessing: false,
1348
- };
1349
- const steps = stepsOrder.reduce((acc, key) => {
1350
- acc[key] = createRenderStep(() => (runNextFrame = true));
1351
- return acc;
1352
- }, {});
1353
- const processStep = (stepId) => steps[stepId].process(state);
1354
- const processBatch = () => {
1355
- const timestamp = MotionGlobalConfig.useManualTiming
1356
- ? state.timestamp
1357
- : performance.now();
1358
- runNextFrame = false;
1359
- state.delta = useDefaultElapsed
1360
- ? 1000 / 60
1361
- : Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1);
1362
- state.timestamp = timestamp;
1363
- state.isProcessing = true;
1364
- stepsOrder.forEach(processStep);
1365
- state.isProcessing = false;
1366
- if (runNextFrame && allowKeepAlive) {
1367
- useDefaultElapsed = false;
1368
- scheduleNextBatch(processBatch);
1369
- }
1370
- };
1371
- const wake = () => {
1372
- runNextFrame = true;
1373
- useDefaultElapsed = true;
1374
- if (!state.isProcessing) {
1375
- scheduleNextBatch(processBatch);
1376
- }
1377
- };
1378
- const schedule = stepsOrder.reduce((acc, key) => {
1379
- const step = steps[key];
1380
- acc[key] = (process, keepAlive = false, immediate = false) => {
1381
- if (!runNextFrame)
1382
- wake();
1383
- return step.schedule(process, keepAlive, immediate);
1384
- };
1385
- return acc;
1386
- }, {});
1387
- const cancel = (process) => stepsOrder.forEach((key) => steps[key].cancel(process));
1388
- return { schedule, cancel, state, steps };
1389
- }
1390
-
1391
1395
  const { schedule: frame, cancel: cancelFrame, state: frameData, steps, } = createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
1392
1396
 
1393
1397
  const svgMotionConfig = {
@@ -2909,8 +2913,6 @@
2909
2913
  };
2910
2914
  }
2911
2915
 
2912
- const { schedule: microtask, cancel: cancelMicrotask } = createRenderBatcher(queueMicrotask, false);
2913
-
2914
2916
  let now;
2915
2917
  function clearTime() {
2916
2918
  now = undefined;
@@ -2934,7 +2936,7 @@
2934
2936
  },
2935
2937
  set: (newTime) => {
2936
2938
  now = newTime;
2937
- microtask.postRender(clearTime);
2939
+ queueMicrotask(clearTime);
2938
2940
  },
2939
2941
  };
2940
2942
 
@@ -3895,7 +3897,7 @@
3895
3897
  * This will be replaced by the build step with the latest version number.
3896
3898
  * When MotionValues are provided to motion components, warn if versions are mixed.
3897
3899
  */
3898
- this.version = "11.0.0-alpha.1";
3900
+ this.version = "11.0.0";
3899
3901
  /**
3900
3902
  * Tracks whether this value can output a velocity. Currently this is only true
3901
3903
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -6783,7 +6785,7 @@
6783
6785
  * and warn against mismatches.
6784
6786
  */
6785
6787
  {
6786
- warnOnce(nextValue.version === "11.0.0-alpha.1", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.0.0-alpha.1 may not work as expected.`);
6788
+ warnOnce(nextValue.version === "11.0.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.0.0 may not work as expected.`);
6787
6789
  }
6788
6790
  }
6789
6791
  else if (isMotionValue(prevValue)) {
@@ -8800,7 +8802,7 @@
8800
8802
  didUpdate() {
8801
8803
  if (!this.updateScheduled) {
8802
8804
  this.updateScheduled = true;
8803
- queueMicrotask(() => this.update());
8805
+ microtask.read(() => this.update());
8804
8806
  }
8805
8807
  }
8806
8808
  clearAllSnapshots() {
@@ -9082,9 +9084,7 @@
9082
9084
  * a relativeParent. This will allow a component to perform scale correction
9083
9085
  * even if no animation has started.
9084
9086
  */
9085
- // TODO If this is unsuccessful this currently happens every frame
9086
9087
  if (!this.targetDelta && !this.relativeTarget) {
9087
- // TODO: This is a semi-repetition of further down this function, make DRY
9088
9088
  const relativeParent = this.getClosestProjectingParent();
9089
9089
  if (relativeParent &&
9090
9090
  relativeParent.layout &&
@@ -10148,7 +10148,7 @@
10148
10148
  const { projection } = this.props.visualElement;
10149
10149
  if (projection) {
10150
10150
  projection.root.didUpdate();
10151
- queueMicrotask(() => {
10151
+ microtask.postRender(() => {
10152
10152
  if (!projection.currentAnimation && projection.isLead()) {
10153
10153
  this.safeToRemove();
10154
10154
  }
@@ -10976,6 +10976,13 @@
10976
10976
  */
10977
10977
  if (isStatic)
10978
10978
  return set(v);
10979
+ /**
10980
+ * If the previous animation hasn't had the chance to even render a frame, render it now.
10981
+ */
10982
+ const animation = activeSpringAnimation.current;
10983
+ if (animation && animation.time === 0) {
10984
+ animation.sample(frameData.delta);
10985
+ }
10979
10986
  stopAnimation();
10980
10987
  activeSpringAnimation.current = animateValue({
10981
10988
  keyframes: [value.get(), v],
@@ -10986,16 +10993,6 @@
10986
10993
  ...config,
10987
10994
  onUpdate: set,
10988
10995
  });
10989
- /**
10990
- * If we're between frames, resync the animation to the frameloop.
10991
- */
10992
- if (!frameData.isProcessing) {
10993
- const delta = performance.now() - frameData.timestamp;
10994
- if (delta < 30) {
10995
- activeSpringAnimation.current.time =
10996
- millisecondsToSeconds(delta);
10997
- }
10998
- }
10999
10996
  return value.get();
11000
10997
  }, stopAnimation);
11001
10998
  }, [JSON.stringify(config)]);
@@ -11033,6 +11030,10 @@
11033
11030
  const updateVelocity = () => {
11034
11031
  const latest = value.getVelocity();
11035
11032
  velocity.set(latest);
11033
+ /**
11034
+ * If we still have velocity, schedule an update for the next frame
11035
+ * to keep checking until it is zero.
11036
+ */
11036
11037
  if (latest)
11037
11038
  frame.update(updateVelocity);
11038
11039
  };