创建在方形视图内移动图像的边界

问题描述 投票:0回答:1

我正在 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 动画值。

任何人都可以引导我走向正确的方向吗?

javascript react-native react-native-reanimated react-native-gesture-handler
1个回答
0
投票

根据我的快速分析,您对 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

© www.soinside.com 2019 - 2024. All rights reserved.