2213
keyframes: any,
2214
targetElement: Element,
2215
pseudoElement: string,
2216
- timeline: AnimationTimeline,
2216
+ timeline: GestureTimeline,
2217
+ customTimelineCleanup: Array<() => void>,
2218
rangeStart: number,
2219
rangeEnd: number,
2220
moveFirstFrameIntoViewport: boolean,
2275
}
2276
// TODO: Reverse the reverse if the original direction is reverse.
2277
const reverse = rangeStart > rangeEnd;
2277
- targetElement.animate(keyframes, {
2278
- pseudoElement: pseudoElement,
2279
- // Set the timeline to the current gesture timeline to drive the updates.
2280
- timeline: timeline,
2281
- // We reset all easing functions to linear so that it feels like you
2282
- // have direct impact on the transition and to avoid double bouncing
2283
- // from scroll bouncing.
2284
- easing: 'linear',
2285
- // We fill in both direction for overscroll.
2286
- fill: 'both', // TODO: Should we preserve the fill instead?
2287
- // We play all gestures in reverse, except if we're in reverse direction
2288
- // in which case we need to play it in reverse of the reverse.
2289
- direction: reverse ? 'normal' : 'reverse',
2290
- // Range start needs to be higher than range end. If it goes in reverse
2291
- // we reverse the whole animation below.
2292
- rangeStart: (reverse ? rangeEnd : rangeStart) + '%',
2293
- rangeEnd: (reverse ? rangeStart : rangeEnd) + '%',
2294
- });
2278
+ if (timeline instanceof AnimationTimeline) {
2279
+ // Native Timeline
2280
+ targetElement.animate(keyframes, {
2281
+ pseudoElement: pseudoElement,
2282
+ // Set the timeline to the current gesture timeline to drive the updates.
2283
+ timeline: timeline,
2284
+ // We reset all easing functions to linear so that it feels like you
2285
+ // have direct impact on the transition and to avoid double bouncing
2286
+ // from scroll bouncing.
2287
+ easing: 'linear',
2288
+ // We fill in both direction for overscroll.
2289
+ fill: 'both', // TODO: Should we preserve the fill instead?
2290
+ // We play all gestures in reverse, except if we're in reverse direction
2291
+ // in which case we need to play it in reverse of the reverse.
2292
+ direction: reverse ? 'normal' : 'reverse',
2293
+ // Range start needs to be higher than range end. If it goes in reverse
2294
+ // we reverse the whole animation below.
2295
+ rangeStart: (reverse ? rangeEnd : rangeStart) + '%',
2296
+ rangeEnd: (reverse ? rangeStart : rangeEnd) + '%',
2297
+ });
2298
+ } else {
2299
+ // Custom Timeline
2300
+ const animation = targetElement.animate(keyframes, {
2301
+ pseudoElement: pseudoElement,
2302
+ // We reset all easing functions to linear so that it feels like you
2303
+ // have direct impact on the transition and to avoid double bouncing
2304
+ // from scroll bouncing.
2305
+ easing: 'linear',
2306
+ // We fill in both direction for overscroll.
2307
+ fill: 'both', // TODO: Should we preserve the fill instead?
2308
+ // We play all gestures in reverse, except if we're in reverse direction
2309
+ // in which case we need to play it in reverse of the reverse.
2310
+ direction: reverse ? 'normal' : 'reverse',
2311
+ // We set the delay and duration to represent the span of the range.
2312
+ delay: reverse ? rangeEnd : rangeStart,
2313
+ duration: reverse ? rangeStart - rangeEnd : rangeEnd - rangeStart,
2314
+ });
2315
+ // Let the custom timeline take control of driving the animation.
2316
+ const cleanup = timeline.animate(animation);
2317
+ if (cleanup) {
2318
+ customTimelineCleanup.push(cleanup);
2319
+ }
2320
+ }
2321
}
2322
2323
export function startGestureTransition(
2346
});
2347
// $FlowFixMe[prop-missing]
2348
ownerDocument.__reactViewTransition = transition;
2349
+ const customTimelineCleanup: Array<() => void> = []; // Cleanup Animations started in a CustomTimeline
2350
const readyCallback = () => {
2351
const documentElement: Element = (ownerDocument.documentElement: any);
2352
// Loop through all View Transition Animations.
2446
effect.target,
2447
pseudoElement,
2448
timeline,
2449
+ customTimelineCleanup,
2450
adjustedRangeStart,
2451
adjustedRangeEnd,
2452
isGeneratedGroupAnim,
2473
effect.target,
2474
pseudoElementName,
2475
timeline,
2476
+ customTimelineCleanup,
2477
rangeStart,
2478
rangeEnd,
2479
false,
2523
transition.ready.then(readyForAnimations, handleError);
2524
transition.finished.finally(() => {
2525
cancelAllViewTransitionAnimations((ownerDocument.documentElement: any));
2526
+ for (let i = 0; i < customTimelineCleanup.length; i++) {
2527
+ const cleanup = customTimelineCleanup[i];
2528
+ cleanup();
2529
+ }
2530
// $FlowFixMe[prop-missing]
2531
if (ownerDocument.__reactViewTransition === transition) {
2532
// $FlowFixMe[prop-missing]
2630
};
2631
}
2632
2600
-export type GestureTimeline = AnimationTimeline; // TODO: More provider types.
2633
+interface CustomTimeline {
2634
+ currentTime: number;
2635
+ animate(animation: Animation): void | (() => void);
2636
+}
2637
+
2638
+export type GestureTimeline = AnimationTimeline | CustomTimeline;
2639
2602
-export function getCurrentGestureOffset(provider: GestureTimeline): number {
2603
- const time = provider.currentTime;
2640
+export function getCurrentGestureOffset(timeline: GestureTimeline): number {
2641
+ const time = timeline.currentTime;
2642
if (time === null) {
2643
throw new Error(
2644
'Cannot start a gesture with a disconnected AnimationTimeline.',