Fix flicker

Reanimated values and React props were out of sync. Prepare each image's styles separately to avoid that.
This commit is contained in:
Dan Abramov
2024-11-03 04:53:16 +00:00
parent 13b2c8f850
commit f261484d4c
+96 -55
View File
@@ -17,7 +17,7 @@ import {
StyleSheet, StyleSheet,
View, View,
} from 'react-native' } from 'react-native'
import {Gesture} from 'react-native-gesture-handler' import {Gesture, PanGesture} from 'react-native-gesture-handler'
import PagerView from 'react-native-pager-view' import PagerView from 'react-native-pager-view'
import { import {
cancelAnimation, cancelAnimation,
@@ -108,47 +108,6 @@ function ImageViewing({
return {pointerEvents: 'auto'} return {pointerEvents: 'auto'}
}) })
const activeInterpolation = useDerivedValue(() => {
const image = images[imageIndex]
if (image.thumbRect && image.dimensions) {
const interpolatedTransform = interpolateTransform(
openProgress.value,
image.thumbRect,
SCREEN,
image.dimensions,
)
return interpolatedTransform
}
})
const activeImageContainerStyle = useAnimatedStyle(() => {
if (openProgress.value === 1 || dismissSwipeTranslateY.value !== 0) {
return {
transform: [{translateY: dismissSwipeTranslateY.value}],
}
}
const interpolation = activeInterpolation.value
if (interpolation) {
return {
transform: interpolation.transform,
}
}
return {
transform: [],
}
})
const activeImageStyle = useAnimatedStyle(() => {
const interpolation = activeInterpolation.value
if (interpolation) {
return {
height: interpolation.height,
width: interpolation.width,
}
}
return {}
})
const dismissSwipePan = Gesture.Pan() const dismissSwipePan = Gesture.Pan()
.enabled(!isScaled) .enabled(!isScaled)
.activeOffsetY([-10, 10]) .activeOffsetY([-10, 10])
@@ -248,20 +207,21 @@ function ImageViewing({
overdrag={true} overdrag={true}
style={styles.pager}> style={styles.pager}>
{images.map((imageSrc, i) => ( {images.map((imageSrc, i) => (
<Animated.View <LightboxPage
key={imageSrc.uri} key={imageSrc.uri}
style={imageIndex === i ? activeImageContainerStyle : null}> imageSrc={imageSrc}
<ImageItem onRequestClose={onRequestClose}
onTap={onTap} onTap={onTap}
onZoom={onZoom} onZoom={onZoom}
imageSrc={imageSrc} isActive={i === imageIndex}
onRequestClose={onRequestClose} isPagingAndroid={isPagingAndroid}
isPagingAndroid={isPagingAndroid} isFlyingAway={isFlyingAway}
showControls={showControls} isScaled={isScaled}
dismissSwipePan={imageIndex === i ? dismissSwipePan : null} showControls={showControls}
imageStyle={imageIndex === i ? activeImageStyle : null} openProgress={openProgress}
/> dismissSwipePan={dismissSwipePan}
</Animated.View> dismissSwipeTranslateY={dismissSwipeTranslateY}
/>
))} ))}
</PagerView> </PagerView>
<Animated.View style={[styles.footer, animatedFooterStyle]}> <Animated.View style={[styles.footer, animatedFooterStyle]}>
@@ -277,6 +237,87 @@ function ImageViewing({
) )
} }
function LightboxPage({
imageSrc,
onRequestClose,
onTap,
onZoom,
isActive,
isPagingAndroid,
showControls,
openProgress,
dismissSwipeTranslateY,
dismissSwipePan,
}: {
imageSrc: ImageSource
onRequestClose: () => void
onTap: () => void
onZoom: (scaled: boolean) => void
isActive: boolean
isPagingAndroid: boolean
isFlyingAway: SharedValue<boolean>
isScaled: boolean
showControls: boolean
openProgress: SharedValue<number>
dismissSwipeTranslateY: SharedValue<number>
dismissSwipePan: PanGesture
}) {
const {thumbRect, dimensions} = imageSrc
const imageAspect = dimensions
? dimensions.width / dimensions.height
: undefined
const finalWidth = SCREEN.width
const finalHeight = imageAspect ? SCREEN.width / imageAspect : undefined
const interpolation = useDerivedValue(() => {
if (isActive && thumbRect && dimensions && openProgress.value < 1) {
return interpolateTransform(
openProgress.value,
thumbRect,
SCREEN,
dimensions,
)
}
const translateY = isActive ? dismissSwipeTranslateY.value : 0
return {
transform: [{translateY}],
width: finalWidth,
height: finalHeight,
}
})
const containerStyle = useAnimatedStyle(() => {
const {transform} = interpolation.value
return {
flex: 1,
transform,
}
})
const imageStyle = useAnimatedStyle(() => {
const {width, height} = interpolation.value
return {
width,
height,
}
})
return (
<Animated.View style={containerStyle}>
<ImageItem
onTap={onTap}
onZoom={onZoom}
imageSrc={imageSrc}
onRequestClose={onRequestClose}
isPagingAndroid={isPagingAndroid}
showControls={showControls}
dismissSwipePan={isActive ? dismissSwipePan : null}
imageStyle={imageStyle}
/>
</Animated.View>
)
}
function LightboxFooter({ function LightboxFooter({
images, images,
index, index,