UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

185 lines (165 loc) • 5.22 kB
'use strict'; import type { StyleProps } from '../commonTypes'; import { adaptViewConfig } from '../ConfigHelper'; import { isSharedValue } from '../isSharedValue'; import { startMapper, stopMapper } from '../mappers'; import { updateProps } from '../updateProps'; import type { ViewDescriptorsSet } from '../ViewDescriptorsSet'; import { makeViewDescriptorsSet } from '../ViewDescriptorsSet'; import type { AnimatedComponentProps, IAnimatedComponentInternal, IInlinePropManager, ViewInfo, } from './commonTypes'; import { flattenArray } from './utils'; function isInlineStyleTransform(transform: unknown): boolean { if (!Array.isArray(transform)) { return false; } return transform.some((t: Record<string, unknown>) => hasInlineStyles(t)); } function inlinePropsHasChanged( styles1: StyleProps, styles2: StyleProps ): boolean { if (Object.keys(styles1).length !== Object.keys(styles2).length) { return true; } for (const key of Object.keys(styles1)) { if (styles1[key] !== styles2[key]) { return true; } } return false; } function getInlinePropsUpdate(inlineProps: Record<string, unknown>) { 'worklet'; const update: Record<string, unknown> = {}; for (const [key, styleValue] of Object.entries(inlineProps)) { if (isSharedValue(styleValue)) { update[key] = styleValue.value; } else if (Array.isArray(styleValue)) { update[key] = styleValue.map((item) => { return getInlinePropsUpdate(item); }); } else if (typeof styleValue === 'object') { update[key] = getInlinePropsUpdate(styleValue as Record<string, unknown>); } else { update[key] = styleValue; } } return update; } function extractSharedValuesMapFromProps( props: AnimatedComponentProps< Record<string, unknown> /* Initial component props */ > ): Record<string, unknown> { const inlineProps: Record<string, unknown> = {}; for (const key in props) { const value = props[key]; if (key === 'style') { const styles = flattenArray<StyleProps>(props.style ?? []); styles.forEach((style) => { if (!style) { return; } for (const [styleKey, styleValue] of Object.entries(style)) { if (isSharedValue(styleValue)) { inlineProps[styleKey] = styleValue; } else if ( styleKey === 'transform' && isInlineStyleTransform(styleValue) ) { inlineProps[styleKey] = styleValue; } } }); } else if (isSharedValue(value)) { inlineProps[key] = value; } } return inlineProps; } export function hasInlineStyles(style: StyleProps): boolean { if (!style) { return false; } return Object.keys(style).some((key) => { const styleValue = style[key]; return ( isSharedValue(styleValue) || (key === 'transform' && isInlineStyleTransform(styleValue)) ); }); } export function getInlineStyle( style: Record<string, unknown>, isFirstRender: boolean ) { if (isFirstRender) { return getInlinePropsUpdate(style); } const newStyle: StyleProps = {}; for (const [key, styleValue] of Object.entries(style)) { if ( !isSharedValue(styleValue) && !(key === 'transform' && isInlineStyleTransform(styleValue)) ) { newStyle[key] = styleValue; } } return newStyle; } export class InlinePropManager implements IInlinePropManager { _inlinePropsViewDescriptors: ViewDescriptorsSet | null = null; _inlinePropsMapperId: number | null = null; _inlineProps: StyleProps = {}; public attachInlineProps( animatedComponent: React.Component<unknown, unknown> & IAnimatedComponentInternal, viewInfo: ViewInfo ) { const newInlineProps: Record<string, unknown> = extractSharedValuesMapFromProps(animatedComponent.props); const hasChanged = inlinePropsHasChanged(newInlineProps, this._inlineProps); if (hasChanged) { if (!this._inlinePropsViewDescriptors) { this._inlinePropsViewDescriptors = makeViewDescriptorsSet(); const { viewTag, viewName, shadowNodeWrapper, viewConfig } = viewInfo; if (Object.keys(newInlineProps).length && viewConfig) { adaptViewConfig(viewConfig); } this._inlinePropsViewDescriptors.add({ tag: viewTag as number, name: viewName!, shadowNodeWrapper: shadowNodeWrapper!, }); } const shareableViewDescriptors = this._inlinePropsViewDescriptors.shareableViewDescriptors; const updaterFunction = () => { 'worklet'; const update = getInlinePropsUpdate(newInlineProps); updateProps(shareableViewDescriptors, update); }; this._inlineProps = newInlineProps; if (this._inlinePropsMapperId) { stopMapper(this._inlinePropsMapperId); } this._inlinePropsMapperId = null; if (Object.keys(newInlineProps).length) { this._inlinePropsMapperId = startMapper( updaterFunction, Object.values(newInlineProps) ); } } } public detachInlineProps() { if (this._inlinePropsMapperId) { stopMapper(this._inlinePropsMapperId); } } }