我正在使用PanGestureHandler
并PinchGestureHandler
允许在屏幕上平移和放大/缩小大图像。但是,一旦我引入了缩放变换,平移的行为就会有所不同。
我对这个实现有三个目标:
- 我试图在首次加载视图时缩小图像以适应特定高度。这样用户就可以看到尽可能多的图像。如果您只对图像应用比例变换,则平移值
0
不会将图像放在左上角(作为居中比例原点的结果)。 - 我正在尝试这样做,以便当有人使用捏合手势进行缩放时,也会调整平移值,以使其看起来好像缩放原点位于用户启动手势的位置(React Native 目前仅支持比例变换的中心原点)。为了实现这一点,我假设我需要在用户缩放时调整平移值(如果比例不是
1
)。 - 在缩放后平移时,我希望平移通过调整与缩放比例相关的平移值来跟踪用户的手指(而不是移动得更快/更慢)。
这是我到目前为止所拥有的:
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))
这些数字不太适用于这个实现,所以我可能做的不对。此外,这只能解释我的第一个目标,我猜我需要做一些事情,比如根据比例值插入平移值来完成另外两个。