react-native-reanimated
Version:
More powerful alternative to Animated library for React Native.
290 lines (260 loc) • 8.36 kB
text/typescript
/* eslint-disable @typescript-eslint/no-empty-function */
;
import type {
IWorkletsModule,
SerializableRef,
WorkletFunction,
} from 'react-native-worklets';
import { executeOnUIRuntimeSync, WorkletsModule } from 'react-native-worklets';
import {
ReanimatedError,
registerReanimatedError,
SHOULD_BE_USE_WEB,
} from '../common';
import type {
LayoutAnimationBatchItem,
ShadowNodeWrapper,
StyleProps,
Value3D,
ValueRotation,
WrapperRef,
} from '../commonTypes';
import type {
CSSAnimationUpdates,
NormalizedCSSAnimationKeyframesConfig,
NormalizedCSSTransitionConfig,
} from '../css/native';
import { getShadowNodeWrapperFromRef } from '../fabricUtils';
import { checkCppVersion } from '../platform-specific/checkCppVersion';
import { jsVersion } from '../platform-specific/jsVersion';
import { assertWorkletsVersion } from '../platform-specific/workletsVersion';
import { ReanimatedTurboModule } from '../specs';
import type {
IReanimatedModule,
ReanimatedModuleProxy,
} from './reanimatedModuleProxy';
export function createNativeReanimatedModule(): IReanimatedModule {
return new NativeReanimatedModule();
}
function assertSingleReanimatedInstance() {
if (
global._REANIMATED_VERSION_JS !== undefined &&
global._REANIMATED_VERSION_JS !== jsVersion
) {
throw new ReanimatedError(
`Another instance of Reanimated was detected.
See \`https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#another-instance-of-reanimated-was-detected\` for more details. Previous: ${global._REANIMATED_VERSION_JS}, current: ${jsVersion}.`
);
}
}
class NativeReanimatedModule implements IReanimatedModule {
/**
* We keep the instance of `WorkletsModule` here to keep correct coupling of
* the modules and initialization order.
*/
// eslint-disable-next-line no-unused-private-class-members
#workletsModule: IWorkletsModule;
#reanimatedModuleProxy: ReanimatedModuleProxy;
constructor() {
this.#workletsModule = WorkletsModule;
// These checks have to split since version checking depend on the execution order
if (__DEV__) {
assertSingleReanimatedInstance();
assertWorkletsVersion();
}
global._REANIMATED_VERSION_JS = jsVersion;
if (global.__reanimatedModuleProxy === undefined && ReanimatedTurboModule) {
if (!ReanimatedTurboModule.installTurboModule()) {
// This path means that React Native has failed on reload.
// We don't want to throw any errors to not mislead the users
// that the problem is related to Reanimated.
// We install a DummyReanimatedModuleProxy instead.
this.#reanimatedModuleProxy = new DummyReanimatedModuleProxy();
return;
}
}
if (global.__reanimatedModuleProxy === undefined) {
throw new ReanimatedError(
`Native part of Reanimated doesn't seem to be initialized.
See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#native-part-of-reanimated-doesnt-seem-to-be-initialized for more details.`
);
}
if (!globalThis.RN$Bridgeless && !SHOULD_BE_USE_WEB) {
throw new ReanimatedError(
'Reanimated 4 supports only the React Native New Architecture and web.'
);
}
if (__DEV__) {
checkCppVersion();
}
this.#reanimatedModuleProxy = global.__reanimatedModuleProxy;
executeOnUIRuntimeSync(function initializeUI() {
'worklet';
registerReanimatedError();
})();
}
registerSensor(
sensorType: number,
interval: number,
iosReferenceFrame: number,
handler: SerializableRef<(data: Value3D | ValueRotation) => void>
) {
return this.#reanimatedModuleProxy.registerSensor(
sensorType,
interval,
iosReferenceFrame,
handler
);
}
unregisterSensor(sensorId: number) {
return this.#reanimatedModuleProxy.unregisterSensor(sensorId);
}
registerEventHandler<T>(
eventHandler: SerializableRef<T>,
eventName: string,
emitterReactTag: number
) {
return this.#reanimatedModuleProxy.registerEventHandler(
eventHandler,
eventName,
emitterReactTag
);
}
unregisterEventHandler(id: number) {
return this.#reanimatedModuleProxy.unregisterEventHandler(id);
}
getViewProp<T>(
viewTag: number,
propName: string,
component: WrapperRef, // required on Fabric
callback?: (result: T) => void
) {
const shadowNodeWrapper = getShadowNodeWrapperFromRef(component);
return this.#reanimatedModuleProxy.getViewProp(
shadowNodeWrapper,
propName,
callback
);
}
configureLayoutAnimationBatch(
layoutAnimationsBatch: LayoutAnimationBatchItem[]
) {
this.#reanimatedModuleProxy.configureLayoutAnimationBatch(
layoutAnimationsBatch
);
}
setShouldAnimateExitingForTag(viewTag: number, shouldAnimate: boolean) {
this.#reanimatedModuleProxy.setShouldAnimateExitingForTag(
viewTag,
shouldAnimate
);
}
getStaticFeatureFlag(name: string): boolean {
return this.#reanimatedModuleProxy.getStaticFeatureFlag(name);
}
setDynamicFeatureFlag(name: string, value: boolean) {
this.#reanimatedModuleProxy.setDynamicFeatureFlag(name, value);
}
subscribeForKeyboardEvents(
handler: SerializableRef<WorkletFunction>,
isStatusBarTranslucent: boolean,
isNavigationBarTranslucent: boolean
) {
return this.#reanimatedModuleProxy.subscribeForKeyboardEvents(
handler,
isStatusBarTranslucent,
isNavigationBarTranslucent
);
}
unsubscribeFromKeyboardEvents(listenerId: number) {
this.#reanimatedModuleProxy.unsubscribeFromKeyboardEvents(listenerId);
}
setViewStyle(viewTag: number, style: StyleProps) {
this.#reanimatedModuleProxy.setViewStyle(viewTag, style);
}
markNodeAsRemovable(shadowNodeWrapper: ShadowNodeWrapper) {
this.#reanimatedModuleProxy.markNodeAsRemovable(shadowNodeWrapper);
}
unmarkNodeAsRemovable(viewTag: number) {
this.#reanimatedModuleProxy.unmarkNodeAsRemovable(viewTag);
}
registerCSSKeyframes(
animationName: string,
viewName: string,
keyframesConfig: NormalizedCSSAnimationKeyframesConfig
) {
this.#reanimatedModuleProxy.registerCSSKeyframes(
animationName,
viewName,
keyframesConfig
);
}
unregisterCSSKeyframes(animationName: string, viewName: string) {
this.#reanimatedModuleProxy.unregisterCSSKeyframes(animationName, viewName);
}
applyCSSAnimations(
shadowNodeWrapper: ShadowNodeWrapper,
animationUpdates: CSSAnimationUpdates
) {
this.#reanimatedModuleProxy.applyCSSAnimations(
shadowNodeWrapper,
animationUpdates
);
}
unregisterCSSAnimations(viewTag: number) {
this.#reanimatedModuleProxy.unregisterCSSAnimations(viewTag);
}
registerCSSTransition(
shadowNodeWrapper: ShadowNodeWrapper,
transitionConfig: NormalizedCSSTransitionConfig
) {
this.#reanimatedModuleProxy.registerCSSTransition(
shadowNodeWrapper,
transitionConfig
);
}
updateCSSTransition(
viewTag: number,
configUpdates: Partial<NormalizedCSSTransitionConfig>
) {
this.#reanimatedModuleProxy.updateCSSTransition(viewTag, configUpdates);
}
unregisterCSSTransition(viewTag: number) {
this.#reanimatedModuleProxy.unregisterCSSTransition(viewTag);
}
}
class DummyReanimatedModuleProxy implements ReanimatedModuleProxy {
configureLayoutAnimationBatch(): void {}
setShouldAnimateExitingForTag(): void {}
getStaticFeatureFlag(): boolean {
return false;
}
setDynamicFeatureFlag(): void {}
subscribeForKeyboardEvents(): number {
return -1;
}
unsubscribeFromKeyboardEvents(): void {}
setViewStyle(): void {}
markNodeAsRemovable(): void {}
unmarkNodeAsRemovable(): void {}
registerCSSKeyframes(): void {}
unregisterCSSKeyframes(): void {}
applyCSSAnimations(): void {}
registerCSSAnimations(): void {}
updateCSSAnimations(): void {}
unregisterCSSAnimations(): void {}
registerCSSTransition(): void {}
updateCSSTransition(): void {}
unregisterCSSTransition(): void {}
registerSensor(): number {
return -1;
}
unregisterSensor(): void {}
registerEventHandler(): number {
return -1;
}
unregisterEventHandler(): void {}
getViewProp() {
return null!;
}
}