我正在 React Native 中构建一个裁剪组件。我已经组装了一些代码(https://snack.expo.dev/@leurs247/cropper)。
说明: 它的目标是将给定图像裁剪成正方形,但用户可以选择要裁剪图像的哪一部分(也可以缩放图像)。开始时,比例为 1,因此图像具有原始大小。正方形的背景是粉红色的,在移动图像时,图像应限制在正方形内,这样就看不到粉红色的背景。这有效。但是当缩放> 1(最大缩放= 3)时,边界计算不正确。移动图像时,可以移动到边界之外,以便粉红色背景变得可见。
这是裁剪器组件的完整代码:
import React, {useMemo} from 'react';
import {View, useWindowDimensions} from 'react-native';
import {GestureDetector, Gesture} from 'react-native-gesture-handler';
import Animated, {useSharedValue, clamp} from 'react-native-reanimated';
import Slider from '@react-native-community/slider';
const Cropper = props => {
const {width} = useWindowDimensions();
const {ratio, scaledWidth, scaledHeight} = useMemo(() => {
let ratio;
let scaledWidth;
let scaledHeight;
if (props.width < props.height) {
ratio = width / props.width;
scaledWidth = width;
scaledHeight = props.height * ratio;
} else if (props.height < props.width) {
ratio = width / props.height;
scaledWidth = props.width * ratio;
scaledHeight = width;
} else {
ratio = width / props.width;
scaledWidth = width;
scaledHeight = width;
}
return {ratio, scaledWidth, scaledHeight};
}, [props.width, props.height, width]);
const posX = useSharedValue(0);
const posY = useSharedValue(0);
const transX = useSharedValue(0);
const transY = useSharedValue(0);
const scaleT = useSharedValue(1);
const maxLeft = useSharedValue(0);
const maxRight = useSharedValue(0);
const maxTop = useSharedValue(0);
const maxBottom = useSharedValue(0);
const x = useSharedValue(0);
const y = useSharedValue(0);
const pan = Gesture.Pan()
.onStart(e => {})
.onUpdate(e => {
const totalWidth = scaledWidth * scaleT.value;
const totalHeight = Math.round(scaledHeight * scaleT.value);
maxLeft.value = -(totalWidth - width) * scaleT.value;
maxRight.value = (totalWidth - width) * (scaleT.value - 1);
maxTop.value = -((totalHeight - width) * scaleT.value);
maxBottom.value = (totalHeight - width) * (scaleT.value - 1);
posX.value = clamp(
transX.value + e.translationX,
maxLeft.value,
maxRight.value,
);
posY.value = clamp(
transY.value + e.translationY,
maxTop.value,
maxBottom.value,
);
})
.onEnd(e => {
transX.value = posX.value;
transY.value = posY.value;
});
const composed = Gesture.Race(pan);
return (
<View style={{flex: 1}}>
<View style={{width, height: width, overflow: 'hidden'}}>
<GestureDetector gesture={composed}>
<Animated.Image
source={{uri: props.uri}}
style={{
transform: [
{
translateX: posX,
},
{
translateY: posY,
},
{
scale: scaleT,
},
],
width: scaledWidth,
height: scaledHeight,
}}
/>
</GestureDetector>
</View>
<Slider
minimumValue={1}
maximumValue={3}
value={1}
onValueChange={value => {
scaleT.value = value;
}}
/>
</View>
);
};
export default Cropper;
我认为正确工作的主要部分是使用 maxLeft、maxRight、maxTop 和 maxBottom 动画值。
任何人都可以引导我走向正确的方向吗?
根据我的快速分析,您对 maxLeft、maxRight、maxTop 和 maxBottom 的计算在缩放时未正确限制图像的位置。
因此,在应用缩放时,您需要调整计算以正确反映裁剪区域内图像的边界。
试试这个:
const pan = Gesture.Pan()
.onUpdate(e => {
const displayedWidth = scaledWidth * scaleT.value;
const displayedHeight = scaledHeight * scaleT.value;
maxLeft.value = width - displayedWidth;
maxRight.value = 0;
maxTop.value = width - displayedHeight;
maxBottom.value = 0;
posX.value = clamp(
transX.value + e.translationX,
maxLeft.value,
maxRight.value,
);
posY.value = clamp(
transY.value + e.translationY,
maxTop.value,
maxBottom.value,
);
})
.onEnd(e => {
transX.value = posX.value;
transY.value = posY.value;
});
在上面的代码中,我进行了调整,以便当图像放大时,displayWidth 变得大于宽度。
然后 maxLeft.value 变为负数,表示图像右边缘不再覆盖裁剪区域之前的最大向左移动量。
将 posX.value 限制在 maxLeft.value(最负值)和 0(不向右移动超出裁剪区域的左边缘)之间,确保图像保持在边界内。
相同的逻辑适用于垂直移动,使用 displayedHeight 和 maxTop.value。这有点棘手,但你可以测试一下自己,然后让我知道这是否正常工作。
我无法测试的一件事是 transX.value 和 transY.value 设置是否正确。所以这取决于你xD