Skip to content

Add ScrollTimeline Polyfill for Swipe Recognizer using a new CustomTimeline protocol #33501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 2, 2025

Conversation

sebmarkbage
Copy link
Collaborator

The React API is just that we now accept this protocol as an alternative to a native AnimationTimeline to be passed to startGestureTransition. This is specifically the DOM version.

interface CustomTimeline {
  currentTime: number;
  animate(animation: Animation): void | (() => void);
}

Instead, of passing this to the Animation that we start to control the View Transition keyframes, we instead inverse the control and pass the Animation to this one. It lets any custom implementation drive the updates. It can do so by updating the time every frame or letting it run a time based animation (such as momentum scroll).

In this case I added a basic polyfill for ScrollTimeline in the example but we'll need a better one.

@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Jun 10, 2025
@sebmarkbage sebmarkbage force-pushed the scrolltimelinepolyfill branch from 811e00b to a24876c Compare June 10, 2025 21:31
@react-sizebot
Copy link

react-sizebot commented Jun 10, 2025

Comparing: a947eba...e4bb93c

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB = 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 530.57 kB 530.57 kB = 93.67 kB 93.67 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js +0.12% 651.66 kB 652.47 kB +0.15% 114.78 kB 114.96 kB
facebook-www/ReactDOM-prod.classic.js = 674.81 kB 674.81 kB = 118.78 kB 118.78 kB
facebook-www/ReactDOM-prod.modern.js = 665.30 kB 665.30 kB = 117.19 kB 117.19 kB

Significant size changes

Includes any change greater than 0.2%:

(No significant changes)

Generated by 🚫 dangerJS against e4bb93c

@sebmarkbage
Copy link
Collaborator Author

This is also how you'd implement other gestures that can't be modeled as scroll. Such as rotate and zoom gestures.

@@ -5,6 +5,8 @@ import React, {
unstable_startGestureTransition as startGestureTransition,
} from 'react';

import ScrollTimelinePolyfill from 'animation-timelines/scroll-timeline';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I published this as a third party package instead since it's generally useful and I can put more complex ones there.

https://www.npmjs.com/package/animation-timelines

@sebmarkbage sebmarkbage force-pushed the scrolltimelinepolyfill branch 3 times, most recently from 45ca295 to 761229f Compare June 18, 2025 22:33
This basically lets a custom implementation drive the Animation we start
on pseudo-elements.
@sebmarkbage sebmarkbage merged commit fc41c24 into facebook:main Jul 2, 2025
241 checks passed
sebmarkbage added a commit that referenced this pull request Jul 2, 2025
Stacked on #33501.

This disables the use of ScrollTimeline when detected in Safari in the
recommended SwipeRecognizer approach. I'm instead using a polyfill using
touch events on iOS.

Safari seems set to [release ScrollTimeline
soon](https://webkit.org/blog/16993/news-from-wwdc25-web-technology-coming-this-fall-in-safari-26-beta/).
Unfortunately it's not really what you'd expect.

First of all, [it's not running in sync with the
scroll](https://bugs.webkit.org/show_bug.cgi?id=288402) which is kind of
its main point. Instead, it is running at 60fps and out of sync with the
scroll just like JS. In fact, it is worse than JS because with JS you
can at least spawn CSS animations that run at 120fps. So our polyfill
can respond to touches at 60fps while gesturing and then run at 120fps
upon release. That's better than with ScrollTimeline.

Second, [there's a bug which interrupts scrolling if you start a
ViewTransition](https://bugs.webkit.org/show_bug.cgi?id=288795) when the
element is being removed as part of that. The element can still respond
to touches so in a polyfill this isn't an issue. But it essentially
makes it useless to use ScrollTimeline with swipe-away gestures.

So we're better off in every scenario by not using it.

The UA detection is a bit unfortunate. Not sure if there's something
more specific but we also had to do a UA detection for Chrome for View
Transitions. Those are the only two we have in all of React.


![safarimeme](https://github.com/user-attachments/assets/d4ca9eba-489e-4ade-b462-2ffeee3a470c)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants