This commit is contained in:
Dan Abramov
2023-09-29 21:02:22 +01:00
parent 6998349231
commit 23a8cb9744
2 changed files with 37 additions and 16 deletions
@@ -12,6 +12,7 @@ import Animated, {
useAnimatedStyle, useAnimatedStyle,
useAnimatedReaction, useAnimatedReaction,
useSharedValue, useSharedValue,
withDecay,
withSpring, withSpring,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import { import {
@@ -115,18 +116,6 @@ function applyRounding(t) {
t[4] = Math.round(t[0] * 200) / 200; t[4] = Math.round(t[0] * 200) / 200;
} }
function toStyle(t) {
"worklet";
const [translateX, translateY, scale] = readTransform(t)
return {
transform: [
{ translateX },
{ translateY },
{ scale }
],
};
}
function getScaledDimensions(imageDimensions, scale) { function getScaledDimensions(imageDimensions, scale) {
'worklet' 'worklet'
const imageAspect = imageDimensions.width / imageDimensions.height const imageAspect = imageDimensions.width / imageDimensions.height
@@ -176,14 +165,15 @@ const ImageItem = ({
const pinchScale = useSharedValue(1) const pinchScale = useSharedValue(1)
const pinchTranslation = useSharedValue({ x: 0, y: 0 }) const pinchTranslation = useSharedValue({ x: 0, y: 0 })
const panTranslation = useSharedValue({ x: 0, y: 0 }) const panTranslation = useSharedValue({ x: 0, y: 0 })
const dismissSwipeTranslateY = useSharedValue(0)
useAnimatedReaction( useAnimatedReaction(
() => { () => {
if (pinchScale.value !== 1) { if (pinchScale.value !== 1) {
return true; return true;
} }
const [,,scale] = readTransform(committedTransform.value) const [,,committedScale] = readTransform(committedTransform.value)
if (scale !== 1) { if (committedScale !== 1) {
return true; return true;
} }
return false; return false;
@@ -205,7 +195,17 @@ const ImageItem = ({
prependPan(t, panTranslation.value); prependPan(t, panTranslation.value);
prependPinch(t, pinchScale.value, pinchOrigin.value, pinchTranslation.value); prependPinch(t, pinchScale.value, pinchOrigin.value, pinchTranslation.value);
prependTransform(t, committedTransform.value); prependTransform(t, committedTransform.value);
return toStyle(t) const [translateX, translateY, scale] = readTransform(t)
const dismissDistance = dismissSwipeTranslateY.value
const dismissProgress = Math.min(Math.abs(dismissDistance) / (SCREEN.height/2), 1);
return {
opacity: 1 - dismissProgress,
transform: [
{ translateX },
{ translateY: translateY + dismissDistance },
{ scale }
],
};
}) })
function getPanAdjustment(candidateTransform) { function getPanAdjustment(candidateTransform) {
@@ -318,11 +318,31 @@ const ImageItem = ({
committedTransform.value = withClampedSpring(finalTransform) committedTransform.value = withClampedSpring(finalTransform)
}); });
const dismissSwipePan = Gesture.Pan()
.enabled(!isScaled)
.activeOffsetY([-10, 10])
.failOffsetX([-2, 2])
.maxPointers(1)
.onUpdate(e => {
dismissSwipeTranslateY.value = e.translationY
})
.onEnd((e) => {
if (Math.abs(e.velocityY) > 1000) {
dismissSwipeTranslateY.value = withDecay({ velocity: e.velocityY })
runOnJS(onRequestClose)();
} else {
dismissSwipeTranslateY.value = withSpring(0, {
stiffness: 700,
damping: 50
})
}
});
const isLoading = !isLoaded || !imageDimensions; const isLoading = !isLoaded || !imageDimensions;
return ( return (
<View <View
style={styles.container}> style={styles.container}>
<GestureDetector gesture={Gesture.Exclusive(Gesture.Simultaneous(pinch, pan), doubleTap)}> <GestureDetector gesture={Gesture.Exclusive(dismissSwipePan, Gesture.Simultaneous(pinch, pan), doubleTap)}>
<AnimatedImage <AnimatedImage
source={imageSrc} source={imageSrc}
contentFit="contain" contentFit="contain"
@@ -132,6 +132,7 @@ function ImageViewing({
data={images} data={images}
horizontal horizontal
pagingEnabled pagingEnabled
nestedScrollEnabled
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
getItem={(_, index) => images[index]} getItem={(_, index) => images[index]}