framer-motion 10.2.4 → 10.3.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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var wrap = require('./wrap-62da7859.js');
5
+ var wrap = require('./wrap-462b1507.js');
6
6
 
7
7
 
8
8
 
package/dist/cjs/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var React = require('react');
6
- var wrap = require('./wrap-62da7859.js');
6
+ var wrap = require('./wrap-462b1507.js');
7
7
 
8
8
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
9
9
 
@@ -4227,10 +4227,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
4227
4227
  this.target = undefined;
4228
4228
  this.isLayoutDirty = false;
4229
4229
  }
4230
- /**
4231
- * Frame calculations
4232
- */
4233
- resolveTargetDelta() {
4230
+ resolveTargetDelta(forceRecalculation = false) {
4234
4231
  var _a;
4235
4232
  /**
4236
4233
  * Once the dirty status of nodes has been spread through the tree, we also
@@ -4246,7 +4243,8 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
4246
4243
  * We don't use transform for this step of processing so we don't
4247
4244
  * need to check whether any nodes have changed transform.
4248
4245
  */
4249
- const canSkip = !((isShared && this.isSharedProjectionDirty) ||
4246
+ const canSkip = !(forceRecalculation ||
4247
+ (isShared && this.isSharedProjectionDirty) ||
4250
4248
  this.isProjectionDirty ||
4251
4249
  ((_a = this.parent) === null || _a === void 0 ? void 0 : _a.isProjectionDirty) ||
4252
4250
  this.attemptToResolveRelativeTarget);
@@ -4258,6 +4256,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
4258
4256
  */
4259
4257
  if (!this.layout || !(layout || layoutId))
4260
4258
  return;
4259
+ this.resolvedRelativeTargetAt = wrap.frameData.timestamp;
4261
4260
  /**
4262
4261
  * If we don't have a targetDelta but do have a layout, we can attempt to resolve
4263
4262
  * a relativeParent. This will allow a component to perform scale correction
@@ -4298,6 +4297,16 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
4298
4297
  this.relativeTargetOrigin &&
4299
4298
  this.relativeParent &&
4300
4299
  this.relativeParent.target) {
4300
+ /**
4301
+ * If the parent target isn't up-to-date, force it to update.
4302
+ * This is an unfortunate de-optimisation as it means any updating relative
4303
+ * projection will cause all the relative parents to recalculate back
4304
+ * up the tree.
4305
+ */
4306
+ if (this.relativeParent.resolvedRelativeTargetAt !==
4307
+ wrap.frameData.timestamp) {
4308
+ this.relativeParent.resolveTargetDelta(true);
4309
+ }
4301
4310
  calcRelativeBox(this.target, this.relativeTarget, this.relativeParent.target);
4302
4311
  /**
4303
4312
  * If we've only got a targetDelta, resolve it into a target
@@ -4384,6 +4393,13 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
4384
4393
  (this.isSharedProjectionDirty || this.isTransformDirty)) {
4385
4394
  canSkip = false;
4386
4395
  }
4396
+ /**
4397
+ * If we have resolved the target this frame we must recalculate the
4398
+ * projection to ensure it visually represents the internal calculations.
4399
+ */
4400
+ if (this.resolvedRelativeTargetAt === wrap.frameData.timestamp) {
4401
+ canSkip = false;
4402
+ }
4387
4403
  if (canSkip)
4388
4404
  return;
4389
4405
  const { layout, layoutId } = this.options;
@@ -5516,7 +5532,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5516
5532
  * and warn against mismatches.
5517
5533
  */
5518
5534
  if (process.env.NODE_ENV === "development") {
5519
- wrap.warnOnce(nextValue.version === "10.2.4", `Attempting to mix Framer Motion versions ${nextValue.version} with 10.2.4 may not work as expected.`);
5535
+ wrap.warnOnce(nextValue.version === "10.3.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 10.3.0 may not work as expected.`);
5520
5536
  }
5521
5537
  }
5522
5538
  else if (wrap.isMotionValue(prevValue)) {
@@ -7679,7 +7695,7 @@ sync) {
7679
7695
  */
7680
7696
  sync.update(() => {
7681
7697
  if (value.animation) {
7682
- value.animation.currentTime =
7698
+ value.animation.time =
7683
7699
  performance.now() - wrap.millisecondsToSeconds(sampledTime);
7684
7700
  }
7685
7701
  });
@@ -404,7 +404,7 @@ class MotionValue {
404
404
  * This will be replaced by the build step with the latest version number.
405
405
  * When MotionValues are provided to motion components, warn if versions are mixed.
406
406
  */
407
- this.version = "10.2.4";
407
+ this.version = "10.3.0";
408
408
  /**
409
409
  * Duration, in milliseconds, since last updating frame.
410
410
  *
@@ -1764,6 +1764,20 @@ function calculateDuration(generator) {
1764
1764
  return duration;
1765
1765
  }
1766
1766
  function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, keyframes: keyframes$1, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", onPlay, onStop, onComplete, onUpdate, ...options }) {
1767
+ let resolveFinishedPromise;
1768
+ let currentFinishedPromise;
1769
+ /**
1770
+ * Create a new finished Promise every time we enter the
1771
+ * finished state and resolve the old Promise. This is
1772
+ * WAAPI-compatible behaviour.
1773
+ */
1774
+ const updateFinishedPromise = () => {
1775
+ currentFinishedPromise = new Promise((resolve) => {
1776
+ resolveFinishedPromise = resolve;
1777
+ });
1778
+ };
1779
+ // Create the first finished promise
1780
+ updateFinishedPromise();
1767
1781
  let animationDriver;
1768
1782
  const generatorFactory = types[type] || keyframes;
1769
1783
  /**
@@ -1809,26 +1823,26 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
1809
1823
  resolvedDuration = calculatedDuration + repeatDelay;
1810
1824
  totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
1811
1825
  }
1812
- let currentTime = 0;
1826
+ let time = 0;
1813
1827
  const tick = (timestamp) => {
1814
1828
  if (startTime === null)
1815
1829
  return;
1816
1830
  if (holdTime !== null) {
1817
- currentTime = holdTime;
1831
+ time = holdTime;
1818
1832
  }
1819
1833
  else {
1820
- currentTime = timestamp - startTime;
1834
+ time = timestamp - startTime;
1821
1835
  }
1822
1836
  // Rebase on delay
1823
- currentTime = Math.max(currentTime - delay, 0);
1837
+ time = Math.max(time - delay, 0);
1824
1838
  /**
1825
1839
  * If this animation has finished, set the current time
1826
1840
  * to the total duration.
1827
1841
  */
1828
1842
  if (playState === "finished" && holdTime === null) {
1829
- currentTime = totalDuration;
1843
+ time = totalDuration;
1830
1844
  }
1831
- let elapsed = currentTime;
1845
+ let elapsed = time;
1832
1846
  let frameGenerator = generator;
1833
1847
  if (repeat) {
1834
1848
  /**
@@ -1836,7 +1850,7 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
1836
1850
  * than duration we'll get values like 2.5 (midway through the
1837
1851
  * third iteration)
1838
1852
  */
1839
- const progress = currentTime / resolvedDuration;
1853
+ const progress = time / resolvedDuration;
1840
1854
  /**
1841
1855
  * Get the current iteration (0 indexed). For instance the floor of
1842
1856
  * 2.5 is 2.
@@ -1870,7 +1884,7 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
1870
1884
  frameGenerator = mirroredGenerator;
1871
1885
  }
1872
1886
  }
1873
- const p = currentTime >= totalDuration
1887
+ const p = time >= totalDuration
1874
1888
  ? repeatType === "reverse" && iterationIsOdd
1875
1889
  ? 0
1876
1890
  : 1
@@ -1883,19 +1897,25 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
1883
1897
  onUpdate(mapNumbersToKeyframes ? mapNumbersToKeyframes(value) : value);
1884
1898
  }
1885
1899
  if (calculatedDuration !== null) {
1886
- done = currentTime >= totalDuration;
1900
+ done = time >= totalDuration;
1887
1901
  }
1888
1902
  const isAnimationFinished = holdTime === null &&
1889
1903
  (playState === "finished" || (playState === "running" && done));
1890
1904
  if (isAnimationFinished) {
1891
- playState = "finished";
1892
- onComplete && onComplete();
1893
- animationDriver && animationDriver.stop();
1905
+ finish();
1894
1906
  }
1895
1907
  return state;
1896
1908
  };
1909
+ const finish = () => {
1910
+ animationDriver && animationDriver.stop();
1911
+ playState = "finished";
1912
+ onComplete && onComplete();
1913
+ resolveFinishedPromise();
1914
+ updateFinishedPromise();
1915
+ };
1897
1916
  const play = () => {
1898
- animationDriver = driver(tick);
1917
+ if (!animationDriver)
1918
+ animationDriver = driver(tick);
1899
1919
  const now = animationDriver.now();
1900
1920
  onPlay && onPlay();
1901
1921
  playState = "running";
@@ -1914,21 +1934,30 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
1914
1934
  play();
1915
1935
  }
1916
1936
  const controls = {
1917
- get currentTime() {
1918
- return millisecondsToSeconds(currentTime);
1937
+ then(resolve, reject) {
1938
+ return currentFinishedPromise.then(resolve, reject);
1919
1939
  },
1920
- set currentTime(newTime) {
1940
+ get time() {
1941
+ return millisecondsToSeconds(time);
1942
+ },
1943
+ set time(newTime) {
1944
+ const timeInMs = secondsToMilliseconds(newTime);
1921
1945
  if (holdTime !== null || !animationDriver) {
1922
- holdTime = 0;
1946
+ holdTime = timeInMs;
1923
1947
  }
1924
1948
  else {
1925
- startTime =
1926
- animationDriver.now() - secondsToMilliseconds(newTime);
1949
+ startTime = animationDriver.now() - timeInMs;
1927
1950
  }
1928
1951
  },
1952
+ play,
1953
+ pause: () => {
1954
+ playState = "paused";
1955
+ holdTime = time;
1956
+ },
1929
1957
  stop: () => {
1930
1958
  onStop && onStop();
1931
1959
  animationDriver && animationDriver.stop();
1960
+ animationDriver = undefined;
1932
1961
  },
1933
1962
  sample: (elapsed) => {
1934
1963
  startTime = 0;
@@ -2027,12 +2056,17 @@ function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ..
2027
2056
  * Animation interrupt callback.
2028
2057
  */
2029
2058
  return {
2030
- get currentTime() {
2059
+ then(resolve, reject) {
2060
+ return animation.finished.then(resolve, reject);
2061
+ },
2062
+ get time() {
2031
2063
  return millisecondsToSeconds(animation.currentTime || 0);
2032
2064
  },
2033
- set currentTime(newTime) {
2065
+ set time(newTime) {
2034
2066
  animation.currentTime = secondsToMilliseconds(newTime);
2035
2067
  },
2068
+ play: () => animation.play(),
2069
+ pause: () => animation.pause(),
2036
2070
  stop: () => {
2037
2071
  /**
2038
2072
  * WAAPI doesn't natively have any interruption capabilities.
@@ -2060,8 +2094,11 @@ function createInstantAnimation({ keyframes, delay: delayBy, onUpdate, onComplet
2060
2094
  onUpdate && onUpdate(keyframes[keyframes.length - 1]);
2061
2095
  onComplete && onComplete();
2062
2096
  return {
2063
- stop: () => { },
2064
- currentTime: 0,
2097
+ time: 0,
2098
+ play: (noop),
2099
+ pause: (noop),
2100
+ stop: (noop),
2101
+ then: Promise.resolve,
2065
2102
  };
2066
2103
  };
2067
2104
  return delayBy
@@ -2296,23 +2333,35 @@ class GroupPlaybackControls {
2296
2333
  constructor(animations) {
2297
2334
  this.animations = animations.filter(Boolean);
2298
2335
  }
2336
+ then(onResolve, onReject) {
2337
+ return Promise.all(this.animations).then(onResolve).catch(onReject);
2338
+ }
2299
2339
  /**
2300
2340
  * TODO: Filter out cancelled or stopped animations before returning
2301
2341
  */
2302
- get currentTime() {
2303
- return this.animations[0].currentTime;
2342
+ get time() {
2343
+ return this.animations[0].time;
2304
2344
  }
2305
2345
  /**
2306
- * currentTime assignment could reasonably run every frame, so
2346
+ * time assignment could reasonably run every frame, so
2307
2347
  * we iterate using a normal loop to avoid function creation.
2308
2348
  */
2309
- set currentTime(time) {
2349
+ set time(time) {
2310
2350
  for (let i = 0; i < this.animations.length; i++) {
2311
- this.animations[i].currentTime = time;
2351
+ this.animations[i].time = time;
2312
2352
  }
2313
2353
  }
2354
+ runAll(methodName) {
2355
+ this.animations.forEach((controls) => controls[methodName]());
2356
+ }
2357
+ play() {
2358
+ this.runAll("play");
2359
+ }
2360
+ pause() {
2361
+ this.runAll("pause");
2362
+ }
2314
2363
  stop() {
2315
- this.animations.forEach((controls) => controls.stop());
2364
+ this.runAll("stop");
2316
2365
  }
2317
2366
  }
2318
2367
 
@@ -2365,24 +2414,6 @@ function delay(callback, timeout) {
2365
2414
  return () => cancelSync.read(checkElapsed);
2366
2415
  }
2367
2416
 
2368
- const isCustomValueType = (v) => {
2369
- return typeof v === "object" && v.mix;
2370
- };
2371
- const getMixer = (v) => (isCustomValueType(v) ? v.mix : undefined);
2372
- function transform(...args) {
2373
- const useImmediate = !Array.isArray(args[0]);
2374
- const argOffset = useImmediate ? 0 : -1;
2375
- const inputValue = args[0 + argOffset];
2376
- const inputRange = args[1 + argOffset];
2377
- const outputRange = args[2 + argOffset];
2378
- const options = args[3 + argOffset];
2379
- const interpolator = interpolate(inputRange, outputRange, {
2380
- mixer: getMixer(outputRange[0]),
2381
- ...options,
2382
- });
2383
- return useImmediate ? interpolator(inputValue) : interpolator;
2384
- }
2385
-
2386
2417
  function resolveElements(elements, selectorCache) {
2387
2418
  var _a;
2388
2419
  if (typeof elements === "string") {
@@ -2858,6 +2889,24 @@ function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount =
2858
2889
  return () => observer.disconnect();
2859
2890
  }
2860
2891
 
2892
+ const isCustomValueType = (v) => {
2893
+ return typeof v === "object" && v.mix;
2894
+ };
2895
+ const getMixer = (v) => (isCustomValueType(v) ? v.mix : undefined);
2896
+ function transform(...args) {
2897
+ const useImmediate = !Array.isArray(args[0]);
2898
+ const argOffset = useImmediate ? 0 : -1;
2899
+ const inputValue = args[0 + argOffset];
2900
+ const inputRange = args[1 + argOffset];
2901
+ const outputRange = args[2 + argOffset];
2902
+ const options = args[3 + argOffset];
2903
+ const interpolator = interpolate(inputRange, outputRange, {
2904
+ mixer: getMixer(outputRange[0]),
2905
+ ...options,
2906
+ });
2907
+ return useImmediate ? interpolator(inputValue) : interpolator;
2908
+ }
2909
+
2861
2910
  const wrap = (min, max, v) => {
2862
2911
  const rangeSize = max - min;
2863
2912
  return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
@@ -803,8 +803,11 @@ declare type Transition = (Orchestration & Repeat & TransitionDefinition) | (Orc
803
803
  * @public
804
804
  */
805
805
  interface AnimationPlaybackControls {
806
- currentTime: number;
806
+ time: number;
807
807
  stop: () => void;
808
+ play: () => void;
809
+ pause: () => void;
810
+ then: (onResolve: VoidFunction, onReject?: VoidFunction) => Promise<void>;
808
811
  }
809
812
 
810
813
  /**
@@ -2,23 +2,35 @@ class GroupPlaybackControls {
2
2
  constructor(animations) {
3
3
  this.animations = animations.filter(Boolean);
4
4
  }
5
+ then(onResolve, onReject) {
6
+ return Promise.all(this.animations).then(onResolve).catch(onReject);
7
+ }
5
8
  /**
6
9
  * TODO: Filter out cancelled or stopped animations before returning
7
10
  */
8
- get currentTime() {
9
- return this.animations[0].currentTime;
11
+ get time() {
12
+ return this.animations[0].time;
10
13
  }
11
14
  /**
12
- * currentTime assignment could reasonably run every frame, so
15
+ * time assignment could reasonably run every frame, so
13
16
  * we iterate using a normal loop to avoid function creation.
14
17
  */
15
- set currentTime(time) {
18
+ set time(time) {
16
19
  for (let i = 0; i < this.animations.length; i++) {
17
- this.animations[i].currentTime = time;
20
+ this.animations[i].time = time;
18
21
  }
19
22
  }
23
+ runAll(methodName) {
24
+ this.animations.forEach((controls) => controls[methodName]());
25
+ }
26
+ play() {
27
+ this.runAll("play");
28
+ }
29
+ pause() {
30
+ this.runAll("pause");
31
+ }
20
32
  stop() {
21
- this.animations.forEach((controls) => controls.stop());
33
+ this.runAll("stop");
22
34
  }
23
35
  }
24
36
 
@@ -1,12 +1,16 @@
1
1
  import { animateValue } from './js/index.mjs';
2
+ import { noop } from '../utils/noop.mjs';
2
3
 
3
4
  function createInstantAnimation({ keyframes, delay: delayBy, onUpdate, onComplete, }) {
4
5
  const setValue = () => {
5
6
  onUpdate && onUpdate(keyframes[keyframes.length - 1]);
6
7
  onComplete && onComplete();
7
8
  return {
8
- stop: () => { },
9
- currentTime: 0,
9
+ time: 0,
10
+ play: (noop),
11
+ pause: (noop),
12
+ stop: (noop),
13
+ then: Promise.resolve,
10
14
  };
11
15
  };
12
16
  return delayBy
@@ -29,6 +29,20 @@ function calculateDuration(generator) {
29
29
  return duration;
30
30
  }
31
31
  function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, keyframes: keyframes$1, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", onPlay, onStop, onComplete, onUpdate, ...options }) {
32
+ let resolveFinishedPromise;
33
+ let currentFinishedPromise;
34
+ /**
35
+ * Create a new finished Promise every time we enter the
36
+ * finished state and resolve the old Promise. This is
37
+ * WAAPI-compatible behaviour.
38
+ */
39
+ const updateFinishedPromise = () => {
40
+ currentFinishedPromise = new Promise((resolve) => {
41
+ resolveFinishedPromise = resolve;
42
+ });
43
+ };
44
+ // Create the first finished promise
45
+ updateFinishedPromise();
32
46
  let animationDriver;
33
47
  const generatorFactory = types[type] || keyframes;
34
48
  /**
@@ -74,26 +88,26 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
74
88
  resolvedDuration = calculatedDuration + repeatDelay;
75
89
  totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
76
90
  }
77
- let currentTime = 0;
91
+ let time = 0;
78
92
  const tick = (timestamp) => {
79
93
  if (startTime === null)
80
94
  return;
81
95
  if (holdTime !== null) {
82
- currentTime = holdTime;
96
+ time = holdTime;
83
97
  }
84
98
  else {
85
- currentTime = timestamp - startTime;
99
+ time = timestamp - startTime;
86
100
  }
87
101
  // Rebase on delay
88
- currentTime = Math.max(currentTime - delay, 0);
102
+ time = Math.max(time - delay, 0);
89
103
  /**
90
104
  * If this animation has finished, set the current time
91
105
  * to the total duration.
92
106
  */
93
107
  if (playState === "finished" && holdTime === null) {
94
- currentTime = totalDuration;
108
+ time = totalDuration;
95
109
  }
96
- let elapsed = currentTime;
110
+ let elapsed = time;
97
111
  let frameGenerator = generator;
98
112
  if (repeat) {
99
113
  /**
@@ -101,7 +115,7 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
101
115
  * than duration we'll get values like 2.5 (midway through the
102
116
  * third iteration)
103
117
  */
104
- const progress = currentTime / resolvedDuration;
118
+ const progress = time / resolvedDuration;
105
119
  /**
106
120
  * Get the current iteration (0 indexed). For instance the floor of
107
121
  * 2.5 is 2.
@@ -135,7 +149,7 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
135
149
  frameGenerator = mirroredGenerator;
136
150
  }
137
151
  }
138
- const p = currentTime >= totalDuration
152
+ const p = time >= totalDuration
139
153
  ? repeatType === "reverse" && iterationIsOdd
140
154
  ? 0
141
155
  : 1
@@ -148,19 +162,25 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
148
162
  onUpdate(mapNumbersToKeyframes ? mapNumbersToKeyframes(value) : value);
149
163
  }
150
164
  if (calculatedDuration !== null) {
151
- done = currentTime >= totalDuration;
165
+ done = time >= totalDuration;
152
166
  }
153
167
  const isAnimationFinished = holdTime === null &&
154
168
  (playState === "finished" || (playState === "running" && done));
155
169
  if (isAnimationFinished) {
156
- playState = "finished";
157
- onComplete && onComplete();
158
- animationDriver && animationDriver.stop();
170
+ finish();
159
171
  }
160
172
  return state;
161
173
  };
174
+ const finish = () => {
175
+ animationDriver && animationDriver.stop();
176
+ playState = "finished";
177
+ onComplete && onComplete();
178
+ resolveFinishedPromise();
179
+ updateFinishedPromise();
180
+ };
162
181
  const play = () => {
163
- animationDriver = driver(tick);
182
+ if (!animationDriver)
183
+ animationDriver = driver(tick);
164
184
  const now = animationDriver.now();
165
185
  onPlay && onPlay();
166
186
  playState = "running";
@@ -179,21 +199,30 @@ function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, ke
179
199
  play();
180
200
  }
181
201
  const controls = {
182
- get currentTime() {
183
- return millisecondsToSeconds(currentTime);
202
+ then(resolve, reject) {
203
+ return currentFinishedPromise.then(resolve, reject);
184
204
  },
185
- set currentTime(newTime) {
205
+ get time() {
206
+ return millisecondsToSeconds(time);
207
+ },
208
+ set time(newTime) {
209
+ const timeInMs = secondsToMilliseconds(newTime);
186
210
  if (holdTime !== null || !animationDriver) {
187
- holdTime = 0;
211
+ holdTime = timeInMs;
188
212
  }
189
213
  else {
190
- startTime =
191
- animationDriver.now() - secondsToMilliseconds(newTime);
214
+ startTime = animationDriver.now() - timeInMs;
192
215
  }
193
216
  },
217
+ play,
218
+ pause: () => {
219
+ playState = "paused";
220
+ holdTime = time;
221
+ },
194
222
  stop: () => {
195
223
  onStop && onStop();
196
224
  animationDriver && animationDriver.stop();
225
+ animationDriver = undefined;
197
226
  },
198
227
  sample: (elapsed) => {
199
228
  startTime = 0;
@@ -42,7 +42,7 @@ sync) {
42
42
  */
43
43
  sync.update(() => {
44
44
  if (value.animation) {
45
- value.animation.currentTime =
45
+ value.animation.time =
46
46
  performance.now() - millisecondsToSeconds(sampledTime);
47
47
  }
48
48
  });
@@ -95,12 +95,17 @@ function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ..
95
95
  * Animation interrupt callback.
96
96
  */
97
97
  return {
98
- get currentTime() {
98
+ then(resolve, reject) {
99
+ return animation.finished.then(resolve, reject);
100
+ },
101
+ get time() {
99
102
  return millisecondsToSeconds(animation.currentTime || 0);
100
103
  },
101
- set currentTime(newTime) {
104
+ set time(newTime) {
102
105
  animation.currentTime = secondsToMilliseconds(newTime);
103
106
  },
107
+ play: () => animation.play(),
108
+ pause: () => animation.pause(),
104
109
  stop: () => {
105
110
  /**
106
111
  * WAAPI doesn't natively have any interruption capabilities.