3

我正在使用PanGestureHandlerPinchGestureHandler允许在屏幕上平移和放大/缩小大图像。但是,一旦我引入了缩放变换,平移的行为就会有所不同。

我对这个实现有三个目标:

  1. 我试图在首次加载视图时缩小图像以适应特定高度。这样用户就可以看到尽可能多的图像。如果您只对图像应用比例变换,则平移值0不会将图像放在左上角(作为居中比例原点的结果)。
  2. 我正在尝试这样做,以便当有人使用捏合手势进行缩放时,也会调整平移值,以使其看起来好像缩放原点位于用户启动手势的位置(React Native 目前仅支持比例变换的中心原点)。为了实现这一点,我假设我需要在用户缩放时调整平移值(如果比例不是1)。
  3. 在缩放后平移时,我希望平移通过调整与缩放比例相关的平移值来跟踪用户的手指(而不是移动得更快/更慢)。

这是我到目前为止所拥有的:

import React, { useRef, useCallback } from 'react';
import { StyleSheet, Animated, View, LayoutChangeEvent } from 'react-native';
import {
    PanGestureHandler,
    PinchGestureHandler,
    PinchGestureHandlerStateChangeEvent,
    State,
    PanGestureHandlerStateChangeEvent,
} from 'react-native-gesture-handler';

const IMAGE_DIMENSIONS = {
    width: 2350,
    height: 1767,
} as const;

export default function App() {
    const scale = useRef(new Animated.Value(1)).current;
    const translateX = useRef(new Animated.Value(0)).current;
    const translateY = useRef(new Animated.Value(0)).current;
    const setInitialPanZoom = useCallback((event: LayoutChangeEvent) => {
        const { height: usableHeight } = event.nativeEvent.layout;
        const scaleRatio = usableHeight / IMAGE_DIMENSIONS.height;
        scale.setValue(scaleRatio);
        // TODO: should these translation values be set based on the scale?
        translateY.setValue(0);
        translateX.setValue(0);
    }, []);
    // Zoom
    const onZoomEvent = Animated.event(
        [
            {
                nativeEvent: { scale },
            },
        ],
        {
            useNativeDriver: true,
        }
    );
    const onZoomStateChange = (event: PinchGestureHandlerStateChangeEvent) => {
        if (event.nativeEvent.oldState === State.ACTIVE) {
            // Do something
        }
    };
    // Pan
    const handlePanGesture = Animated.event([{ nativeEvent: { translationX: translateX, translationY: translateY } }], {
        useNativeDriver: true,
    });
    const onPanStateChange = (_event: PanGestureHandlerStateChangeEvent) => {
        // Extract offset so that panning resumes from previous location, rather than resetting
        translateX.extractOffset();
        translateY.extractOffset();
    };
    return (
        <View style={styles.container}>
            <PanGestureHandler
                minPointers={1}
                maxPointers={1}
                onGestureEvent={handlePanGesture}
                onHandlerStateChange={onPanStateChange}
            >
                <Animated.View style={styles.imageContainer} onLayout={setInitialPanZoom}>
                    <PinchGestureHandler onGestureEvent={onZoomEvent} onHandlerStateChange={onZoomStateChange}>
                        <Animated.View style={{ transform: [{ scale }, { translateY }, { translateX }] }}>
                            <Animated.Image
                                source={require('./assets/my-image.png')}
                                style={{
                                    width: IMAGE_DIMENSIONS.width,
                                    height: IMAGE_DIMENSIONS.height,
                                }}
                                resizeMode="contain"
                            />
                        </Animated.View>
                    </PinchGestureHandler>
                </Animated.View>
            </PanGestureHandler>
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
    },
    imageContainer: {
        flex: 1,
        backgroundColor: 'orange',
        width: '100%',
    },
});

我已经尝试过从翻译值中减去尺寸差异的方法:

translateX.setValue(0 - (IMAGE_DIMENSIONS.width / 2) - (IMAGE_DIMENSIONS.width * scaleRatio / 2))

这些数字不太适用于这个实现,所以我可能做的不对。此外,这只能解释我的第一个目标,我猜我需要做一些事情,比如根据比例值插入平移值来完成另外两个。

4

0 回答 0