fix fabric reanimated perf by introducing derived value
This commit is contained in:
+2
-2
@@ -376,8 +376,8 @@
|
||||
"reanimated": {
|
||||
"staticFeatureFlags": {
|
||||
"DISABLE_COMMIT_PAUSING_MECHANISM": true,
|
||||
"ANDROID_SYNCHRONOUSLY_UPDATE_UI_PROPS": true,
|
||||
"IOS_SYNCHRONOUSLY_UPDATE_UI_PROPS": true,
|
||||
"ANDROID_SYNCHRONOUSLY_UPDATE_UI_PROPS": false,
|
||||
"IOS_SYNCHRONOUSLY_UPDATE_UI_PROPS": false,
|
||||
"USE_SYNCHRONIZABLE_FOR_MUTABLES": true,
|
||||
"USE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS": true,
|
||||
"FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS": true
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||
import Animated, {
|
||||
Extrapolation,
|
||||
interpolate,
|
||||
type SharedValue,
|
||||
useAnimatedStyle,
|
||||
@@ -24,30 +23,28 @@ export function GrowableAvatar({
|
||||
return <View style={style}>{children}</View>
|
||||
}
|
||||
|
||||
const {scrollY} = pagerContext
|
||||
const {clampedScrollY} = pagerContext
|
||||
|
||||
return (
|
||||
<GrowableAvatarInner scrollY={scrollY} style={style}>
|
||||
<GrowableAvatarInner clampedScrollY={clampedScrollY} style={style}>
|
||||
{children}
|
||||
</GrowableAvatarInner>
|
||||
)
|
||||
}
|
||||
|
||||
function GrowableAvatarInner({
|
||||
scrollY,
|
||||
clampedScrollY,
|
||||
children,
|
||||
style,
|
||||
}: {
|
||||
scrollY: SharedValue<number>
|
||||
clampedScrollY: SharedValue<number>
|
||||
children: React.ReactNode
|
||||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const animatedStyle = useAnimatedStyle(() => ({
|
||||
transform: [
|
||||
{
|
||||
scale: interpolate(scrollY.get(), [-150, 0], [1.2, 1], {
|
||||
extrapolateRight: Extrapolation.CLAMP,
|
||||
}),
|
||||
scale: interpolate(clampedScrollY.get(), [-150, 0], [1.2, 1]),
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
@@ -44,34 +44,37 @@ export function GrowableBanner({
|
||||
)
|
||||
}
|
||||
|
||||
const {scrollY} = pagerContext
|
||||
const {clampedScrollY} = pagerContext
|
||||
|
||||
return (
|
||||
<GrowableBannerInner scrollY={scrollY} backButton={backButton}>
|
||||
<GrowableBannerInner
|
||||
clampedScrollY={clampedScrollY}
|
||||
backButton={backButton}>
|
||||
{children}
|
||||
</GrowableBannerInner>
|
||||
)
|
||||
}
|
||||
|
||||
function GrowableBannerInner({
|
||||
scrollY,
|
||||
clampedScrollY,
|
||||
backButton,
|
||||
children,
|
||||
}: {
|
||||
scrollY: SharedValue<number>
|
||||
clampedScrollY: SharedValue<number>
|
||||
backButton?: React.ReactNode
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const {top: topInset} = useSafeAreaInsets()
|
||||
const isFetching = useIsProfileFetching()
|
||||
const animateSpinner = useShouldAnimateSpinner({isFetching, scrollY})
|
||||
const animateSpinner = useShouldAnimateSpinner({
|
||||
isFetching,
|
||||
clampedScrollY,
|
||||
})
|
||||
|
||||
const animatedStyle = useAnimatedStyle(() => ({
|
||||
transform: [
|
||||
{
|
||||
scale: interpolate(scrollY.get(), [-150, 0], [2, 1], {
|
||||
extrapolateRight: Extrapolation.CLAMP,
|
||||
}),
|
||||
scale: interpolate(clampedScrollY.get(), [-150, 0], [2, 1]),
|
||||
},
|
||||
],
|
||||
}))
|
||||
@@ -79,7 +82,7 @@ function GrowableBannerInner({
|
||||
const animatedBlurViewProps = useAnimatedProps(() => {
|
||||
return {
|
||||
intensity: interpolate(
|
||||
scrollY.get(),
|
||||
clampedScrollY.get(),
|
||||
[-300, -65, -15],
|
||||
[50, 40, 0],
|
||||
Extrapolation.CLAMP,
|
||||
@@ -88,7 +91,7 @@ function GrowableBannerInner({
|
||||
})
|
||||
|
||||
const animatedSpinnerStyle = useAnimatedStyle(() => {
|
||||
const scrollYValue = scrollY.get()
|
||||
const scrollYValue = clampedScrollY.get()
|
||||
return {
|
||||
display: scrollYValue < 0 ? 'flex' : 'none',
|
||||
opacity: interpolate(
|
||||
@@ -98,11 +101,7 @@ function GrowableBannerInner({
|
||||
Extrapolation.CLAMP,
|
||||
),
|
||||
transform: [
|
||||
{
|
||||
translateY: interpolate(scrollYValue, [-150, 0], [-75, 0], {
|
||||
extrapolateRight: Extrapolation.CLAMP,
|
||||
}),
|
||||
},
|
||||
{translateY: interpolate(scrollYValue, [-150, 0], [-75, 0])},
|
||||
{rotate: '90deg'},
|
||||
],
|
||||
}
|
||||
@@ -111,9 +110,7 @@ function GrowableBannerInner({
|
||||
const animatedBackButtonStyle = useAnimatedStyle(() => ({
|
||||
transform: [
|
||||
{
|
||||
translateY: interpolate(scrollY.get(), [-150, 10], [-150, 10], {
|
||||
extrapolateRight: Extrapolation.CLAMP,
|
||||
}),
|
||||
translateY: Math.max(-150, Math.min(10, clampedScrollY.get())),
|
||||
},
|
||||
],
|
||||
}))
|
||||
@@ -172,10 +169,10 @@ function useIsProfileFetching() {
|
||||
|
||||
function useShouldAnimateSpinner({
|
||||
isFetching,
|
||||
scrollY,
|
||||
clampedScrollY,
|
||||
}: {
|
||||
isFetching: boolean
|
||||
scrollY: SharedValue<number>
|
||||
clampedScrollY: SharedValue<number>
|
||||
}) {
|
||||
const [isOverscrolled, setIsOverscrolled] = useState(false)
|
||||
// HACK: it reports a scroll pos of 0 for a tick when fetching finishes
|
||||
@@ -183,13 +180,13 @@ function useShouldAnimateSpinner({
|
||||
const stickyIsOverscrolled = useStickyToggle(isOverscrolled, 10)
|
||||
|
||||
useAnimatedReaction(
|
||||
() => scrollY.get() < -5,
|
||||
() => clampedScrollY.get() < -5,
|
||||
(value, prevValue) => {
|
||||
if (value !== prevValue) {
|
||||
runOnJS(setIsOverscrolled)(value)
|
||||
}
|
||||
},
|
||||
[scrollY],
|
||||
[clampedScrollY],
|
||||
)
|
||||
|
||||
const [isAnimating, setIsAnimating] = useState(isFetching)
|
||||
|
||||
@@ -14,8 +14,8 @@ export function StatusBarShadow() {
|
||||
const pagerContext = usePagerHeaderContext()
|
||||
|
||||
if (isIOS && pagerContext) {
|
||||
const {scrollY} = pagerContext
|
||||
return <StatusBarShadowInnner scrollY={scrollY} />
|
||||
const {clampedScrollY} = pagerContext
|
||||
return <StatusBarShadowInnner clampedScrollY={clampedScrollY} />
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -30,14 +30,18 @@ export function StatusBarShadow() {
|
||||
)
|
||||
}
|
||||
|
||||
function StatusBarShadowInnner({scrollY}: {scrollY: SharedValue<number>}) {
|
||||
function StatusBarShadowInnner({
|
||||
clampedScrollY,
|
||||
}: {
|
||||
clampedScrollY: SharedValue<number>
|
||||
}) {
|
||||
const {top: topInset} = useSafeAreaInsets()
|
||||
|
||||
const animatedStyle = useAnimatedStyle(() => {
|
||||
return {
|
||||
transform: [
|
||||
{
|
||||
translateY: Math.min(0, scrollY.get()),
|
||||
translateY: Math.min(0, clampedScrollY.get()),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React, {useContext} from 'react'
|
||||
import {type SharedValue} from 'react-native-reanimated'
|
||||
import {createContext, useContext, useMemo} from 'react'
|
||||
import {type SharedValue, useDerivedValue} from 'react-native-reanimated'
|
||||
|
||||
import {isNative} from '#/platform/detection'
|
||||
|
||||
export const PagerHeaderContext = React.createContext<{
|
||||
export const PagerHeaderContext = createContext<{
|
||||
scrollY: SharedValue<number>
|
||||
clampedScrollY: SharedValue<number>
|
||||
headerHeight: number
|
||||
} | null>(null)
|
||||
PagerHeaderContext.displayName = 'PagerHeaderContext'
|
||||
@@ -24,9 +25,16 @@ export function PagerHeaderProvider({
|
||||
headerHeight: number
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const value = React.useMemo(
|
||||
() => ({scrollY, headerHeight}),
|
||||
[scrollY, headerHeight],
|
||||
// bit of a hackfix - most of the usage of scrollY here is for the pull-to-refresh
|
||||
// behaviour in the header, and after scrolling it's not needed anymore.
|
||||
// to improve performance, we create a derived value that clamps up-front,
|
||||
// therefore removing the need for the downstream animated styles to run on every frame
|
||||
// when they won't actually be used -sfn
|
||||
const clampedScrollY = useDerivedValue(() => Math.min(100, scrollY.get()))
|
||||
|
||||
const value = useMemo(
|
||||
() => ({scrollY, clampedScrollY, headerHeight}),
|
||||
[scrollY, headerHeight, clampedScrollY],
|
||||
)
|
||||
return (
|
||||
<PagerHeaderContext.Provider value={value}>
|
||||
|
||||
Reference in New Issue
Block a user