UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

158 lines (142 loc) • 4.91 kB
'use strict'; import { useEffect, useRef } from 'react'; import type { WorkletFunction } from 'react-native-worklets'; import { makeShareable } from 'react-native-worklets'; import { initialUpdaterRun } from '../animation'; import { logger } from '../common'; import type { AnimatedPropsAdapterWorklet } from '../commonTypes'; import { startMapper, stopMapper } from '../core'; import type { AnimatedProps } from '../createAnimatedComponent/commonTypes'; import { makeViewDescriptorsSet } from '../ViewDescriptorsSet'; import type { AnimatedStyleHandle, DefaultStyle, DependencyList, } from './commonTypes'; import type { AnimatedUpdaterData } from './useAnimatedStyleCommon'; import { buildWorkletsHash, checkSharedValueUsage, styleUpdater, } from './useAnimatedStyleCommon'; import { useSharedValue } from './useSharedValue'; import { validateAnimatedStyles } from './utils'; /** * Lets you create a styles object, similar to StyleSheet styles, which can be * animated using shared values. * * @param updater - A function returning an object with style properties you * want to animate. * @param dependencies - An optional array of dependencies. * @returns An animated style object which has to be passed to the `style` * property of an Animated component you want to animate. * @see https://docs.swmansion.com/react-native-reanimated/docs/core/useAnimatedStyle */ // @ts-expect-error Public type definition which strips internals. export function useAnimatedStyle<Style extends DefaultStyle>( updater: () => Style, dependencies?: DependencyList | null ): AnimatedStyleHandle<Style>; export function useAnimatedStyle<Style extends DefaultStyle | AnimatedProps>( updater: | WorkletFunction<[], Style> | ((() => Style) & Record<string, unknown>), _dependencies?: DependencyList | null, adapters?: AnimatedPropsAdapterWorklet | AnimatedPropsAdapterWorklet[] | null, isAnimatedProps = false ): AnimatedStyleHandle<Style | AnimatedProps> { if (__DEV__ && _dependencies !== undefined && _dependencies !== null) { logger.warn('dependencies should only be used in web implementation.'); } const animatedUpdaterData = useRef<AnimatedUpdaterData | null>(null); const inputs = Object.values(updater.__closure ?? {}); const adaptersArray = adapters ? Array.isArray(adapters) ? adapters : [adapters] : []; const adaptersHash = adapters ? buildWorkletsHash(adaptersArray) : null; const areAnimationsActive = useSharedValue<boolean>(true); // build dependencies const dependencies = [...inputs, updater.__workletHash]; if (adaptersHash) { dependencies.push(adaptersHash); } if (!animatedUpdaterData.current) { const initialStyle = initialUpdaterRun(updater); if (__DEV__) { validateAnimatedStyles(initialStyle); } animatedUpdaterData.current = { initial: { value: initialStyle, updater, }, remoteState: makeShareable({ last: initialStyle, animations: {}, isAnimationCancelled: false, isAnimationRunning: false, }), viewDescriptors: makeViewDescriptorsSet(), styleUpdaterContainer: { current: undefined }, }; } const { initial, remoteState, viewDescriptors } = animatedUpdaterData.current; const shareableViewDescriptors = viewDescriptors.shareableViewDescriptors; dependencies.push(shareableViewDescriptors); useEffect(() => { let updaterFn = updater; if (adapters) { updaterFn = (() => { 'worklet'; const newValues = updater(); adaptersArray.forEach((adapter) => { adapter(newValues as Record<string, unknown>); }); return newValues; }) as WorkletFunction<[], Style>; } const fun = (forceUpdate?: boolean) => { 'worklet'; styleUpdater( shareableViewDescriptors, updaterFn, remoteState, areAnimationsActive, isAnimatedProps, forceUpdate ); }; if (animatedUpdaterData.current) { animatedUpdaterData.current.styleUpdaterContainer.current = fun; } const mapperId = startMapper(fun, inputs); return () => { stopMapper(mapperId); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, dependencies); useEffect(() => { areAnimationsActive.value = true; return () => { areAnimationsActive.value = false; }; }, [areAnimationsActive]); if (__DEV__) { checkSharedValueUsage(initial.value); } const animatedStyleHandle = useRef<AnimatedStyleHandle< Style | AnimatedProps > | null>(null); if (!animatedStyleHandle.current) { const styleUpdaterContainer = animatedUpdaterData.current.styleUpdaterContainer; animatedStyleHandle.current = { viewDescriptors, initial, styleUpdaterContainer, }; } return animatedStyleHandle.current; }