Skip to content

Commit a8c2bbd

Browse files
authored
Cancel finished view transitions Animations manually in fire-and-forget too (#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.
1 parent 50ab2dd commit a8c2bbd

File tree

1 file changed

+26
-14
lines changed

1 file changed

+26
-14
lines changed

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,25 @@ export function hasInstanceAffectedParent(
15501550
return oldRect.height !== newRect.height || oldRect.width !== newRect.width;
15511551
}
15521552

1553+
function cancelAllViewTransitionAnimations(scope: Element) {
1554+
// In Safari, we need to manually cancel all manually start animations
1555+
// or it'll block or interfer with future transitions.
1556+
const animations = scope.getAnimations({subtree: true});
1557+
for (let i = 0; i < animations.length; i++) {
1558+
const anim = animations[i];
1559+
const effect: KeyframeEffect = (anim.effect: any);
1560+
// $FlowFixMe
1561+
const pseudo: ?string = effect.pseudoElement;
1562+
if (
1563+
pseudo != null &&
1564+
pseudo.startsWith('::view-transition') &&
1565+
effect.target === scope
1566+
) {
1567+
anim.cancel();
1568+
}
1569+
}
1570+
}
1571+
15531572
// How long to wait for new fonts to load before just committing anyway.
15541573
// This freezes the screen. It needs to be short enough that it doesn't cause too much of
15551574
// an issue when it's a new load and slow, yet long enough that you have a chance to load
@@ -1640,6 +1659,7 @@ export function startViewTransition(
16401659
}
16411660
transition.ready.then(spawnedWorkCallback, spawnedWorkCallback);
16421661
transition.finished.then(() => {
1662+
cancelAllViewTransitionAnimations((ownerDocument.documentElement: any));
16431663
// $FlowFixMe[prop-missing]
16441664
if (ownerDocument.__reactViewTransition === transition) {
16451665
// $FlowFixMe[prop-missing]
@@ -1817,12 +1837,16 @@ export function startGestureTransition(
18171837
}
18181838
for (let i = 0; i < animations.length; i++) {
18191839
const anim = animations[i];
1840+
if (anim.playState !== 'running') {
1841+
continue;
1842+
}
18201843
const effect: KeyframeEffect = (anim.effect: any);
18211844
// $FlowFixMe
18221845
const pseudoElement: ?string = effect.pseudoElement;
18231846
if (
18241847
pseudoElement != null &&
1825-
pseudoElement.startsWith('::view-transition')
1848+
pseudoElement.startsWith('::view-transition') &&
1849+
effect.target === documentElement
18261850
) {
18271851
// Ideally we could mutate the existing animation but unfortunately
18281852
// the mutable APIs seem less tested and therefore are lacking or buggy.
@@ -1913,19 +1937,7 @@ export function startGestureTransition(
19131937
: readyCallback;
19141938
transition.ready.then(readyForAnimations, readyCallback);
19151939
transition.finished.then(() => {
1916-
// In Safari, we need to manually cancel all manually start animations
1917-
// or it'll block future transitions.
1918-
const documentElement: Element = (ownerDocument.documentElement: any);
1919-
const animations = documentElement.getAnimations({subtree: true});
1920-
for (let i = 0; i < animations.length; i++) {
1921-
const anim = animations[i];
1922-
const effect: KeyframeEffect = (anim.effect: any);
1923-
// $FlowFixMe
1924-
const pseudo: ?string = effect.pseudoElement;
1925-
if (pseudo != null && pseudo.startsWith('::view-transition')) {
1926-
anim.cancel();
1927-
}
1928-
}
1940+
cancelAllViewTransitionAnimations((ownerDocument.documentElement: any));
19291941
// $FlowFixMe[prop-missing]
19301942
if (ownerDocument.__reactViewTransition === transition) {
19311943
// $FlowFixMe[prop-missing]

0 commit comments

Comments
 (0)