Skip to content

Commit 147048d

Browse files
zeyapfacebook-github-bot
authored andcommitted
remove useNativeDriver under featureflag animatedForceNativeDriver
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. Also using this flag to gate the js animation logic that could be cleaned up when this path is fully working. Differential Revision: D108193641
1 parent dd5c383 commit 147048d

10 files changed

Lines changed: 71 additions & 8 deletions

File tree

packages/react-native/Libraries/Animated/AnimatedImplementation.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type {DecayAnimationConfig} from './animations/DecayAnimation';
2020
import type {SpringAnimationConfig} from './animations/SpringAnimation';
2121
import type {TimingAnimationConfig} from './animations/TimingAnimation';
2222

23+
import NativeAnimatedHelper from '../../src/private/animated/NativeAnimatedHelper';
2324
import {AnimatedEvent, attachNativeEventImpl} from './AnimatedEvent';
2425
import DecayAnimation from './animations/DecayAnimation';
2526
import SpringAnimation from './animations/SpringAnimation';
@@ -200,7 +201,11 @@ const springImpl = function (
200201
},
201202

202203
_isUsingNativeDriver: function (): boolean {
203-
return config.useNativeDriver || false;
204+
return (
205+
NativeAnimatedHelper.isNativeDriverForced() ||
206+
config.useNativeDriver ||
207+
false
208+
);
204209
},
205210
}
206211
);
@@ -254,7 +259,11 @@ const timingImpl = function (
254259
},
255260

256261
_isUsingNativeDriver: function (): boolean {
257-
return config.useNativeDriver || false;
262+
return (
263+
NativeAnimatedHelper.isNativeDriverForced() ||
264+
config.useNativeDriver ||
265+
false
266+
);
258267
},
259268
}
260269
);
@@ -296,7 +305,11 @@ const decayImpl = function (
296305
},
297306

298307
_isUsingNativeDriver: function (): boolean {
299-
return config.useNativeDriver || false;
308+
return (
309+
NativeAnimatedHelper.isNativeDriverForced() ||
310+
config.useNativeDriver ||
311+
false
312+
);
300313
},
301314
}
302315
);

packages/react-native/Libraries/Animated/NativeAnimatedAllowlist.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ const SUPPORTED_STYLES: {[string]: true} = {
7878
top: true,
7979
/* flex */
8080
flex: true,
81+
margin: true,
82+
marginLeft: true,
83+
marginRight: true,
84+
marginTop: true,
85+
marginBottom: true,
8186
}
8287
: {}),
8388
};

packages/react-native/Libraries/Animated/animations/Animation.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export default class Animation {
7070
previousAnimation: ?Animation,
7171
animatedValue: AnimatedValue,
7272
): void {
73+
// TODO: T274006331 - Remove js-only animation once shared backend is fully rolled out
7374
if (!this._useNativeDriver && animatedValue.__isNative === true) {
7475
throw new Error(
7576
'Attempting to run JS driven animation on animated node ' +

packages/react-native/Libraries/Animated/animations/DecayAnimation.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export default class DecayAnimation extends Animation {
8585
this._startTime = Date.now();
8686

8787
const useNativeDriver = this.__startAnimationIfNative(animatedValue);
88+
// TODO: T274006331 - Remove js-only animation once shared backend is fully rolled out
8889
if (!useNativeDriver) {
8990
this._animationFrame = requestAnimationFrame(() => this.onUpdate());
9091
}

packages/react-native/Libraries/Animated/animations/SpringAnimation.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type AnimatedValue from '../nodes/AnimatedValue';
1414
import type AnimatedValueXY from '../nodes/AnimatedValueXY';
1515
import type {AnimationConfig, EndCallback} from './Animation';
1616

17+
import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
1718
import AnimatedColor from '../nodes/AnimatedColor';
1819
import * as SpringConfig from '../SpringConfig';
1920
import Animation from './Animation';
@@ -225,6 +226,7 @@ export default class SpringAnimation extends Animation {
225226

226227
const start = () => {
227228
const useNativeDriver = this.__startAnimationIfNative(animatedValue);
229+
// TODO: T274006331 - Remove js-only animation once shared backend is fully rolled out
228230
if (!useNativeDriver) {
229231
this.onUpdate();
230232
}

packages/react-native/Libraries/Animated/animations/TimingAnimation.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export default class TimingAnimation extends Animation {
129129
this._startTime = Date.now();
130130

131131
const useNativeDriver = this.__startAnimationIfNative(animatedValue);
132+
// TODO: T274006331 - Remove js-only animation once shared backend is fully rolled out
132133
if (!useNativeDriver) {
133134
// Animations that sometimes have 0 duration and sometimes do not
134135
// still need to use the native driver when duration is 0 so as to

packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,17 @@ const definitions: FeatureFlagDefinitions = {
10031003
},
10041004
ossReleaseStage: 'none',
10051005
},
1006+
animatedForceNativeDriver: {
1007+
defaultValue: false,
1008+
metadata: {
1009+
dateAdded: '2026-06-10',
1010+
description:
1011+
'When enabled, forces `useNativeDriver` to `true` for all Animated animations and events, overriding the config (including an explicit `false`). Has no effect unless the shared animated backend is enabled, which is required to support native driver for all props.',
1012+
expectedReleaseValue: true,
1013+
purpose: 'experimentation',
1014+
},
1015+
ossReleaseStage: 'none',
1016+
},
10061017
animatedShouldDebounceQueueFlush: {
10071018
defaultValue: false,
10081019
metadata: {

packages/react-native/src/private/animated/NativeAnimatedHelper.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,17 +406,35 @@ function assertNativeAnimatedModule(): void {
406406

407407
let _warnedMissingNativeAnimated = false;
408408

409+
// Whether the native driver should be forced on for every animation, overriding
410+
// the config (including an explicit `useNativeDriver: false`). This is only safe
411+
// when the shared animated backend is enabled — that backend is what makes every
412+
// prop drivable natively. Forcing native without it would break animations of
413+
// props the legacy native driver doesn't support.
414+
function isNativeDriverForced(): boolean {
415+
return (
416+
ReactNativeFeatureFlags.animatedForceNativeDriver() &&
417+
ReactNativeFeatureFlags.cxxNativeAnimatedEnabled() &&
418+
// eslint-disable-next-line
419+
ReactNativeFeatureFlags.useSharedAnimatedBackend()
420+
);
421+
}
422+
409423
function shouldUseNativeDriver(
410424
config: Readonly<{...AnimationConfig, ...}> | EventConfig<unknown>,
411425
): boolean {
412-
if (config.useNativeDriver == null) {
426+
const forceNativeDriver = isNativeDriverForced();
427+
428+
if (config.useNativeDriver == null && !forceNativeDriver) {
413429
console.warn(
414430
'Animated: `useNativeDriver` was not specified. This is a required ' +
415431
'option and must be explicitly set to `true` or `false`',
416432
);
417433
}
418434

419-
if (config.useNativeDriver === true && !NativeAnimatedModule) {
435+
const useNativeDriver = forceNativeDriver || config.useNativeDriver === true;
436+
437+
if (useNativeDriver === true && !NativeAnimatedModule) {
420438
if (process.env.NODE_ENV !== 'test') {
421439
if (!_warnedMissingNativeAnimated) {
422440
console.warn(
@@ -432,7 +450,7 @@ function shouldUseNativeDriver(
432450
return false;
433451
}
434452

435-
return config.useNativeDriver || false;
453+
return useNativeDriver;
436454
}
437455

438456
function transformDataType(value: number | string): number | string {
@@ -458,6 +476,7 @@ export default {
458476
assertNativeAnimatedModule,
459477
generateNewAnimationId,
460478
generateNewNodeTag,
479+
isNativeDriverForced,
461480
// $FlowExpectedError[unsafe-getters-setters] - unsafe getter lint suppression
462481
// $FlowExpectedError[missing-type-arg] - unsafe getter lint suppression
463482
get nativeEventEmitter(): NativeEventEmitter {

packages/react-native/src/private/animated/NativeAnimatedValidation.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ export function validateInterpolation<
2323
OutputT extends InterpolationConfigSupportedOutputType,
2424
>(config: InterpolationConfigType<OutputT>): void {
2525
for (const key in config) {
26-
if (key !== 'debugID' && !isSupportedInterpolationParam(key)) {
26+
if (
27+
key !== 'debugID' &&
28+
key !== 'easing' &&
29+
!isSupportedInterpolationParam(key)
30+
) {
2731
console.error(
2832
`Interpolation property '${key}' is not supported by native animated module`,
2933
);

packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<609451fd0a38e0f8eaf685e7cf534e27>>
7+
* @generated SignedSource<<78d1c7d5ec3ff924e44432b15b1f378e>>
88
* @flow strict
99
* @noformat
1010
*/
@@ -30,6 +30,7 @@ import {
3030
export type ReactNativeFeatureFlagsJsOnly = Readonly<{
3131
jsOnlyTestFlag: Getter<boolean>,
3232
animatedDeferStartOfTimingAnimations: Getter<boolean>,
33+
animatedForceNativeDriver: Getter<boolean>,
3334
animatedShouldDebounceQueueFlush: Getter<boolean>,
3435
animatedShouldSyncValueBeforeStartCallback: Getter<boolean>,
3536
animatedShouldUseSingleOp: Getter<boolean>,
@@ -149,6 +150,11 @@ export const jsOnlyTestFlag: Getter<boolean> = createJavaScriptFlagGetter('jsOnl
149150
*/
150151
export const animatedDeferStartOfTimingAnimations: Getter<boolean> = createJavaScriptFlagGetter('animatedDeferStartOfTimingAnimations', false);
151152

153+
/**
154+
* When enabled, forces `useNativeDriver` to `true` for all Animated animations and events, overriding the config (including an explicit `false`). Has no effect unless the shared animated backend is enabled, which is required to support native driver for all props.
155+
*/
156+
export const animatedForceNativeDriver: Getter<boolean> = createJavaScriptFlagGetter('animatedForceNativeDriver', false);
157+
152158
/**
153159
* Enables an experimental flush-queue debouncing in Animated.js.
154160
*/

0 commit comments

Comments
 (0)