0

我的 React Native 0.64 应用程序使用 react-native-gesture-handler 1.16/react-native-reanimated 2.1 左右滑动图像。这个想法是当用户单击图片库中的图像时,该图像会占据全屏显示,并且可以通过向左或向右滑动来一张一张地查看图库中的其余图像。这是滑动工作的完整代码,但有一个问题:点击的图像不显示,但画廊中的第一张图像总是首先显示。如何登陆用户点击的图片?

import React, { useRef } from "react";
import { Dimensions, StyleSheet, View, Platform } from "react-native";
import FastImage from 'react-native-fast-image';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  useAnimatedRef,
  useAnimatedGestureHandler,
  useAnimatedReaction,
  withTiming,
  withSpring,
  useDerivedValue,
} from "react-native-reanimated";
import {
  snapPoint,
} from "react-native-redash";
import {
  PanGestureHandler,
  PinchGestureHandler,
  State,
  TouchableOpacity,
} from "react-native-gesture-handler";

const { width, height } = Dimensions.get("window");

export default Swipe = ({route, navigation}) => {
    const {images, initIndex} = route.params;  //initIndex is the index of image clicked
    console.log("route params swipe ", route.params);
    const index = useSharedValue(initIndex);
    const snapPoints = images.map((_, i) => i * -width);
    const panRef = useAnimatedRef();
    const pinchRef = useAnimatedRef();
    const offsetX = initIndex ? useSharedValue(initIndex * -width) : useSharedValue(0);  //The init offsetX is set according to initIndex a user clicked.
    const translationX = useSharedValue(0);
    const translateX = useSharedValue(0); //if translateX was assgined with offsetX, then swipe stops working
    //for snap in swipe
    const lowVal = useDerivedValue(() => {return (index.value + 1) * -width}); //shared value, with .value
    const highVal = useDerivedValue(() => {return (index.value - 1) * -width});  //shared value, with .value
    const snapPt = (e) => {
        "worklet"; 
        return snapPoint(translateX.value, e.velocityX, snapPoints);
    };
    
    const panHandler = useAnimatedGestureHandler(
        {
         onStart: () => {
            translateX.value = offsetX.value;  //As soon as a user swipe, this line allow the 
 //image is moved to initIndex-th image. But how to land on the image a user has clicked?
         },
         onActive: (event) => {
            translateX.value = offsetX.value + event.translationX;
            translationX.value = event.translationX;
         },
         onEnd: (event) => {
            let val = snapPt(event);
            let sp = Math.min(Math.max(lowVal.value, val), highVal.value);  //clamp in RN redash
            if (event.translationX !== 0) {
                translateX.value = withTiming(sp);
                offsetX.value = translateX.value;
            };
            //update index
            index.value = Math.floor(translateX.value/-width);          
         }, 
        }, []
    )

    const swipeStyle = useAnimatedStyle(() => {
        return {
            transform: [{ translateX: translateX.value}]
        }
    });

    return (
        <PanGestureHandler
            onGestureEvent={panHandler}
            ref={panRef}
            minDist={10}
            avgTouches
        >
        <Animated.View style={styles.container}>
                <Animated.View
                style={[{width: width * images.length, height, flexDirection: "row"}, swipeStyle]}
                >
                  
                {images.map((img_source, i) => {
                    const isActive = useDerivedValue(() => {return index.value === i});
                    
                    return (
                    <View key={i} style={styles.picture}>
                        <View style={[
                            styles.image,
                        ]}>
                          <TouchableOpacity onPress={() => navigation.goBack()} style={styles.button}>
                            <View style={styles.button}>
                              <FastImage 
                                          source={{uri:img_source.path}} 
                                          resizeMode={FastImage.resizeMode.contain} 
                                          style={styles.image}
                              />
                            </View>
                          </TouchableOpacity>
                        </View>
                    </View>
                    );
                })}
                
                </Animated.View>
            </Animated.View>
            </PanGestureHandler>    
    );
}

const styles = StyleSheet.create({
container: {
  ...StyleSheet.absoluteFillObject,
  backgroundColor: "black",
},

picture: {
  width,
  height,
  overflow: "hidden",
},
image: {
  //...StyleSheet.absoluteFillObject,
  flex:1,
  width: undefined,
  height: undefined,
  //resizeMode: "cover",
},
button: {
  width: width,
  height:height,
},

});

代码的另一个问题是,在滑动图像之前触摸图像时,图像会向左或向右抖动 1/3-1/2 屏幕宽度。这使得滑动不流畅。我想知道如何消除图像抖动并使滑动从头到尾平滑。

4

1 回答 1

0

为了登陆用户点击的图像,添加了一个 useEffect 钩子:

useEffect(() => {
        translateX.value = offsetX.value;
    }, [initIndex]);

onStart需要完全删除。

图像抖动的原因是offsetX价值的歪曲。offsetX应位于屏幕的边界(屏幕宽度的倍数)。但是,当图像宽度小于屏幕宽度时,offsetX图像的边界可能会稍微少一点或多一点(取决于向左滑动还是向右滑动),这会导致图像在最初滑动时向左或向右抖动. 在这种情况下,offsetX需要设置为屏幕的边框(屏幕宽度的倍数)。

于 2021-05-10T15:04:34.603 回答