Skip to content

Commit

Permalink
Cancel finished view transitions Animations manually in fire-and-forg…
Browse files Browse the repository at this point in the history
…et too (facebook#32545)

Otherwise these can survive into the next View Transition and cause
havoc to that transition.

This was appearing as a flash in Safari in the fixture when going from
A->B. This triggers a View Transition and at the same time the scroll
position updates in an effect. That fires a scroll event which starts a
gesture. This shouldn't really happen and the SwipeRecognizer should
ideally ignore those but it's good to surface edge cases. That gesture
is blocked on the View Transition finishing and then immediately after
it starts a gesture View Transition. That gesture then picked up the
former Animation from the previous transition which caused issues. This
PR fixes that flash.
  • Loading branch information
sebmarkbage authored Mar 10, 2025
1 parent 50ab2dd commit a8c2bbd
Showing 1 changed file with 26 additions and 14 deletions.
40 changes: 26 additions & 14 deletions packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -1550,6 +1550,25 @@ export function hasInstanceAffectedParent(
return oldRect.height !== newRect.height || oldRect.width !== newRect.width;
}

function cancelAllViewTransitionAnimations(scope: Element) {
// In Safari, we need to manually cancel all manually start animations
// or it'll block or interfer with future transitions.
const animations = scope.getAnimations({subtree: true});
for (let i = 0; i < animations.length; i++) {
const anim = animations[i];
const effect: KeyframeEffect = (anim.effect: any);
// $FlowFixMe
const pseudo: ?string = effect.pseudoElement;
if (
pseudo != null &&
pseudo.startsWith('::view-transition') &&
effect.target === scope
) {
anim.cancel();
}
}
}

// How long to wait for new fonts to load before just committing anyway.
// This freezes the screen. It needs to be short enough that it doesn't cause too much of
// an issue when it's a new load and slow, yet long enough that you have a chance to load
Expand Down Expand Up @@ -1640,6 +1659,7 @@ export function startViewTransition(
}
transition.ready.then(spawnedWorkCallback, spawnedWorkCallback);
transition.finished.then(() => {
cancelAllViewTransitionAnimations((ownerDocument.documentElement: any));
// $FlowFixMe[prop-missing]
if (ownerDocument.__reactViewTransition === transition) {
// $FlowFixMe[prop-missing]
Expand Down Expand Up @@ -1817,12 +1837,16 @@ export function startGestureTransition(
}
for (let i = 0; i < animations.length; i++) {
const anim = animations[i];
if (anim.playState !== 'running') {
continue;
}
const effect: KeyframeEffect = (anim.effect: any);
// $FlowFixMe
const pseudoElement: ?string = effect.pseudoElement;
if (
pseudoElement != null &&
pseudoElement.startsWith('::view-transition')
pseudoElement.startsWith('::view-transition') &&
effect.target === documentElement
) {
// Ideally we could mutate the existing animation but unfortunately
// the mutable APIs seem less tested and therefore are lacking or buggy.
Expand Down Expand Up @@ -1913,19 +1937,7 @@ export function startGestureTransition(
: readyCallback;
transition.ready.then(readyForAnimations, readyCallback);
transition.finished.then(() => {
// In Safari, we need to manually cancel all manually start animations
// or it'll block future transitions.
const documentElement: Element = (ownerDocument.documentElement: any);
const animations = documentElement.getAnimations({subtree: true});
for (let i = 0; i < animations.length; i++) {
const anim = animations[i];
const effect: KeyframeEffect = (anim.effect: any);
// $FlowFixMe
const pseudo: ?string = effect.pseudoElement;
if (pseudo != null && pseudo.startsWith('::view-transition')) {
anim.cancel();
}
}
cancelAllViewTransitionAnimations((ownerDocument.documentElement: any));
// $FlowFixMe[prop-missing]
if (ownerDocument.__reactViewTransition === transition) {
// $FlowFixMe[prop-missing]
Expand Down

0 comments on commit a8c2bbd

Please sign in to comment.