Skip to content

Commit 79d5399

Browse files
committed
Disable ScrollTimeline in Safari
1 parent 6afd61c commit 79d5399

File tree

1 file changed

+77
-11
lines changed

1 file changed

+77
-11
lines changed

fixtures/view-transition/src/components/SwipeRecognizer.js

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ import React, {
66
} from 'react';
77

88
import ScrollTimelinePolyfill from 'animation-timelines/scroll-timeline';
9+
import TouchPanTimeline from 'animation-timelines/touch-pan-timeline';
10+
11+
const ua = typeof navigator === 'undefined' ? '' : navigator.userAgent;
12+
const isSafariMobile =
13+
ua.indexOf('Safari') !== -1 &&
14+
(ua.indexOf('iPhone') !== -1 ||
15+
ua.indexOf('iPad') !== -1 ||
16+
ua.indexOf('iPod') !== -1);
917

1018
// Example of a Component that can recognize swipe gestures using a ScrollTimeline
1119
// without scrolling its own content. Allowing it to be used as an inert gesture
@@ -23,13 +31,61 @@ export default function SwipeRecognizer({
2331

2432
const scrollRef = useRef(null);
2533
const activeGesture = useRef(null);
34+
const touchTimeline = useRef(null);
35+
36+
function onTouchStart(event) {
37+
if (!isSafariMobile && typeof ScrollTimeline === 'function') {
38+
// If not Safari and native ScrollTimeline is supported, then we use that.
39+
return;
40+
}
41+
if (touchTimeline.current) {
42+
// We can catch the gesture before it settles.
43+
return;
44+
}
45+
const scrollElement = scrollRef.current;
46+
const bounds =
47+
axis === 'x' ? scrollElement.clientWidth : scrollElement.clientHeight;
48+
const range =
49+
direction === 'left' || direction === 'up' ? [bounds, 0] : [0, -bounds];
50+
const timeline = new TouchPanTimeline({
51+
touch: event,
52+
source: scrollElement,
53+
axis: axis,
54+
range: range,
55+
snap: range,
56+
});
57+
touchTimeline.current = timeline;
58+
timeline.settled.then(() => {
59+
if (touchTimeline.current !== timeline) {
60+
return;
61+
}
62+
touchTimeline.current = null;
63+
const changed =
64+
direction === 'left' || direction === 'up'
65+
? timeline.currentTime < 50
66+
: timeline.currentTime > 50;
67+
onGestureEnd(changed);
68+
});
69+
}
70+
71+
function onTouchEnd() {
72+
if (activeGesture.current === null) {
73+
// If we didn't start a gesture before we release, we can release our
74+
// timeline.
75+
touchTimeline.current = null;
76+
}
77+
}
78+
2679
function onScroll() {
2780
if (activeGesture.current !== null) {
2881
return;
2982
}
3083

3184
let scrollTimeline;
32-
if (typeof ScrollTimeline === 'function') {
85+
if (touchTimeline.current) {
86+
// We're in a polyfilled touch gesture. Let's use that timeline instead.
87+
scrollTimeline = touchTimeline.current;
88+
} else if (typeof ScrollTimeline === 'function') {
3389
// eslint-disable-next-line no-undef
3490
scrollTimeline = new ScrollTimeline({
3591
source: scrollRef.current,
@@ -57,7 +113,23 @@ export default function SwipeRecognizer({
57113
}
58114
);
59115
}
116+
function onGestureEnd(changed) {
117+
// Reset scroll
118+
if (changed) {
119+
// Trigger side-effects
120+
startTransition(action);
121+
}
122+
if (activeGesture.current !== null) {
123+
const cancelGesture = activeGesture.current;
124+
activeGesture.current = null;
125+
cancelGesture();
126+
}
127+
}
60128
function onScrollEnd() {
129+
if (touchTimeline.current) {
130+
// We have a touch gesture controlling the swipe.
131+
return;
132+
}
61133
let changed;
62134
const scrollElement = scrollRef.current;
63135
if (axis === 'x') {
@@ -75,16 +147,7 @@ export default function SwipeRecognizer({
75147
? scrollElement.scrollTop < halfway
76148
: scrollElement.scrollTop > halfway;
77149
}
78-
// Reset scroll
79-
if (changed) {
80-
// Trigger side-effects
81-
startTransition(action);
82-
}
83-
if (activeGesture.current !== null) {
84-
const cancelGesture = activeGesture.current;
85-
activeGesture.current = null;
86-
cancelGesture();
87-
}
150+
onGestureEnd(changed);
88151
}
89152

90153
useEffect(() => {
@@ -176,6 +239,9 @@ export default function SwipeRecognizer({
176239
return (
177240
<div
178241
style={scrollStyle}
242+
onTouchStart={onTouchStart}
243+
onTouchEnd={onTouchEnd}
244+
onTouchCancel={onTouchEnd}
179245
onScroll={onScroll}
180246
onScrollEnd={onScrollEnd}
181247
ref={scrollRef}>

0 commit comments

Comments
 (0)