support native interpolation easing (#57237)#57237
Open
zeyap wants to merge 3 commits into
Open
Conversation
|
@zeyap has exported this pull request. If you are a Meta employee, you can view the originating Diff in D108760799. |
zeyap
added a commit
to zeyap/react-native
that referenced
this pull request
Jun 17, 2026
Summary:
## Changelog:
[General] [Added] - support native driven AnimatedValue interpolation easing
Interpolation with easing on AnimatedValue was not supported with native driver, example like below simply doesn't work, because easing function is called on JS thread. This is not solved with shared backend.
```
const progress = useAnimatedValue(0);
const easedX = progress.interpolate({
inputRange: [0, 1],
outputRange: [0, DISTANCE],
easing: Easing.inOut(Easing.cubic),
});
Animated.timing(progress, {
toValue: 1,
duration: 1500,
useNativeDriver: true,
}).start();
// JS error: Interpolation property 'easing' is not supported by native animated module
```
## How it works
The JS `easing` function is sampled and baked into the native interpolation config as a compact set of non-uniform `[position, value]` stops — the same representation as CSS `linear()`:
- JS (`AnimatedInterpolation`): densely samples the easing curve, then simplifies it with Ramer–Douglas–Peucker into non-uniform stops. The simplification tolerance is derived from the interpolation's output span so the on-screen error stays ~sub-pixel — flat curves collapse to a few stops, curvy ones keep more (bounded by the dense-sample budget). `easingStops` is emitted only when an `easing` is set (and is not the linear identity).
- Native (`InterpolationAnimatedNode`): applies the stops to each segment's normalized ratio via binary search + linear interpolation. `easing` is now an accepted interpolation param, so the "not supported" error is gone.
Overshoot is preserved: easings that leave `[0, 1]` (e.g. `Easing.back`, `Easing.elastic`) keep their excursion even under `extrapolate: 'clamp'`, matching the JS driver — `clamp`/`identity` only apply to out-of-range input, not to the easing's own excursion. Works for all output types since easing acts on the normalized ratio, not the output values.
## Known limitation
Color/platform_color interpolation under an overshoot easing can push channel values outside `[0, 255]`, which currently wrap on the native `uint8_t` cast (JS instead emits out-of-gamut). Not addressed here.
Differential Revision: D108760799
…ForceNativeDriver" (react#57250) Summary: ## Changelog: [General] [Added] - remove `useNativeDriver` under featureflag animatedForceNativeDriver When `animatedForceNativeDriver` is enabled, it forces `useNativeDriver` to `true` for all Animated animations and events, overriding the config (explicit `false` set by user will be no-op). Has no effect unless the shared animated backend is enabled, which is required to support native driver for all props. When calling `NativeAnimatedHelper.isNativeDriverForced`, do null check first for backward compatibility in rn-macos Also using this flag to gate the js animation logic that could be cleaned up when this path is fully working. Reviewed By: javache, bmsdave Differential Revision: D108880837
Summary: The RNTester integration build only proves that pods work *together* inside one workspace — it does not build every pod from source, and pods outside RNTester's dependency closure are never validated on their own. As a result, a podspec can ship with a defect that only shows up when the pod is built in isolation: a malformed compiler flag, or a header/dependency that the pod uses but never declares (which surfaces as a `'yoga/Yoga.h' file not found` style build error for open-source consumers). This adds a pull-request job that runs `pod lib lint` on the `*.podspec` files a change actually touches. Each pod is linted in isolation, so a dependency that is used-but-not-declared fails to resolve and the job fails. Because React Native's pods are not published to a spec repo, the script exposes every local podspec via `--include-podspecs`, letting the linter resolve inter-pod dependencies from the checkout. The job is scoped to changed podspecs via `dorny/paths-filter`, so PRs that touch no podspec do no extra work. Changelog: [Internal] - Add CI linting for changed iOS podspecs Differential Revision: D108889958
Summary:
## Changelog:
[General] [Added] - support native driven AnimatedValue interpolation easing
Interpolation with easing on AnimatedValue was not supported with native driver, example like below simply doesn't work, because easing function is called on JS thread. This is not solved with shared backend.
```
const progress = useAnimatedValue(0);
const easedX = progress.interpolate({
inputRange: [0, 1],
outputRange: [0, DISTANCE],
easing: Easing.inOut(Easing.cubic),
});
// case 1: driven by Animated.timing
Animated.timing(progress, {
toValue: 1,
duration: 1500,
useNativeDriver: true,
}).start();
// case 2: driven by native event
<Animated.ScrollView
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: scrollY}}}],
{useNativeDriver: true},
)}>
</Animated.ScrollView>
// JS error: Interpolation property 'easing' is not supported by native animated module
```
## How it works
The JS `easing` function is sampled and baked into the native interpolation config as a compact set of non-uniform `[position, value]` stops — the same representation as CSS `linear()`:
- JS (`AnimatedInterpolation`): densely samples the easing curve, then simplifies it with Ramer–Douglas–Peucker into non-uniform stops. The simplification tolerance is derived from the interpolation's output span so the on-screen error stays ~sub-pixel — flat curves collapse to a few stops, curvy ones keep more (bounded by the dense-sample budget). `easingStops` is emitted only when an `easing` is set (and is not the linear identity).
- Native (`InterpolationAnimatedNode`): applies the stops to each segment's normalized ratio via binary search + linear interpolation. `easing` is now an accepted interpolation param, so the "not supported" error is gone.
Overshoot is preserved: easings that leave `[0, 1]` (e.g. `Easing.back`, `Easing.elastic`) keep their excursion even under `extrapolate: 'clamp'`, matching the JS driver — `clamp`/`identity` only apply to out-of-range input, not to the easing's own excursion. Works for all output types since easing acts on the normalized ratio, not the output values.
## Known limitation
Color/platform_color interpolation under an overshoot easing can push channel values outside `[0, 255]`, which currently wrap on the native `uint8_t` cast (JS instead emits out-of-gamut). Not addressed here.
This is sometimes exchangeable with the native driven easing on Animated.timing. But one unique use case is when you need to interpolate native event driven animated value (e.g. scroll offset from scroll event) differently to derive multiple animation values. I find it impossible to replace with another existing Animated API for this use case.
Differential Revision: D108760799
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
Changelog:
[General] [Added] - support native driven AnimatedValue interpolation easing
Interpolation with easing on AnimatedValue was not supported with native driver, example like below simply doesn't work, because easing function is called on JS thread. This is not solved with shared backend.
How it works
The JS
easingfunction is sampled and baked into the native interpolation config as a compact set of non-uniform[position, value]stops — the same representation as CSSlinear():AnimatedInterpolation): densely samples the easing curve, then simplifies it with Ramer–Douglas–Peucker into non-uniform stops. The simplification tolerance is derived from the interpolation's output span so the on-screen error stays ~sub-pixel — flat curves collapse to a few stops, curvy ones keep more (bounded by the dense-sample budget).easingStopsis emitted only when aneasingis set (and is not the linear identity).InterpolationAnimatedNode): applies the stops to each segment's normalized ratio via binary search + linear interpolation.easingis now an accepted interpolation param, so the "not supported" error is gone.Overshoot is preserved: easings that leave
[0, 1](e.g.Easing.back,Easing.elastic) keep their excursion even underextrapolate: 'clamp', matching the JS driver —clamp/identityonly apply to out-of-range input, not to the easing's own excursion. Works for all output types since easing acts on the normalized ratio, not the output values.Known limitation
Color/platform_color interpolation under an overshoot easing can push channel values outside
[0, 255], which currently wrap on the nativeuint8_tcast (JS instead emits out-of-gamut). Not addressed here.This is sometimes exchangeable with the native driven easing on Animated.timing. But one unique use case is when you need to interpolate native event driven animated value (e.g. scroll offset from scroll event) differently to derive multiple animation values. I find it impossible to replace with another existing Animated API for this use case.
Differential Revision: D108760799