Вопрос или проблема
Я пытаюсь создать слайдер для сравнения изображений на React Native, как здесь https://www.npmjs.com/package/react-compare-slider. Я почти построил компонент в портретном режиме. Но, когда я пытался сделать то же самое в альбомном режиме, перетаскивание слайдера работает не так, как ожидалось.
Вот код
import React, {useState, useEffect} from 'react';
import {
View,
Image,
PanResponder,
Animated,
StyleSheet,
Dimensions,
ActivityIndicator,
} from 'react-native';
import Orientation from 'react-native-orientation-locker';
// import {CompareSlider} from 'react-native-compare-slider';
const ImageComparisonSlider = ({
leftImage,
rightImage,
initialPosition = 0.5,
}) => {
const [sliderPosition, setSliderPosition] = useState(
new Animated.Value(initialPosition * Dimensions.get('window').width),
);
const [leftImageLoaded, setLeftImageLoaded] = useState(false);
const [rightImageLoaded, setRightImageLoaded] = useState(false);
const [isDeviceRotated, setisDeviceRotated] = useState(false);
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (_, gestureState) => {
const newPosition = Math.max(
0,
Math.min(gestureState.moveX, Dimensions.get('window').width),
);
sliderPosition.setValue(newPosition);
},
});
const renderImage = (imageUri, onLoadEnd, style) => (
<Image
source={{uri: imageUri}}
style={style}
resizeMode="cover"
onLoadEnd={onLoadEnd}
/>
);
const bothImagesLoaded = leftImageLoaded && rightImageLoaded;
const onOrientationDidChange = orientation => {
if (orientation == 'LANDSCAPE-LEFT' || orientation == 'LANDSCAPE-RIGHT') {
setisDeviceRotated(true);
} else {
setisDeviceRotated(false);
}
};
useEffect(() => {
Orientation.addOrientationListener(onOrientationDidChange);
return () => Orientation.removeOrientationListener(onOrientationDidChange);
}, []);
return (
<View
style={{
width: isDeviceRotated ? '50%' : '100%',
height: !isDeviceRotated ? '50%' : '100%', // Настройте при необходимости
position: 'relative',
}}>
{!bothImagesLoaded && (
<View style={styles.loaderContainer}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
)}
{bothImagesLoaded && (
<>
{renderImage(rightImage, () => {}, styles.image)}
<Animated.View
style={[
styles.leftImageContainer,
{
width: sliderPosition,
},
]}>
{renderImage(leftImage, () => {}, styles.leftImage)}
</Animated.View>
<Animated.View
style={[styles.sliderLine, {left: sliderPosition}]}
{...panResponder.panHandlers}>
<View style={styles.sliderHandle} />
</Animated.View>
</>
)}
<Image
source={{uri: leftImage}}
style={styles.hiddenImage}
onLoad={() => setLeftImageLoaded(true)}
/>
<Image
source={{uri: rightImage}}
style={styles.hiddenImage}
onLoad={() => setRightImageLoaded(true)}
/>
</View>
);
};
const styles = StyleSheet.create({
image: {
width: '100%',
height: '100%',
},
leftImageContainer: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
overflow: 'hidden',
},
leftImage: {
width: Dimensions.get('window').width,
height: '100%',
},
sliderLine: {
position: 'absolute',
top: 0,
bottom: 0,
width: 3,
backgroundColor: 'white',
},
sliderHandle: {
position: 'absolute',
top: '50%',
left: -10,
width: 20,
height: 20,
borderRadius: 10,
backgroundColor: 'white',
transform: [{translateY: -10}],
},
loaderContainer: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
},
hiddenImage: {
width: 0,
height: 0,
},
});
export default ImageComparisonSlider;
Пожалуйста, посмотрите это также: https://www.loom.com/share/4dc8d887fe1449268c8c944aa25f6dbd
Будет очень полезно, если я найду решение, и, пожалуйста, дайте знать, если есть другие пакеты, которые могут решить эту проблему
Заранее спасибо!
Вам, вероятно, нужно сбросить ширину окна, когда экран меняет ориентацию. Похоже, что у вас есть обновление состояния, но ширина окна инициализируется, но не обновляется, если вы переключаете ориентацию.
const [windowWidth, setWindowWidth] = useState(Dimensions.get("window").width);
const onOrientationDidChange = orientation => {
if (orientation == 'LANDSCAPE-LEFT' || orientation == 'LANDSCAPE-RIGHT') {
setisDeviceRotated(true);
} else {
setisDeviceRotated(false);
}
setWindowWidth(Dimensions.get("window").width);
};
Вы также можете попробовать использовать хук useWindowDimensions
, например, const width = useWindowDimensions().width;
Ответ или решение
Для решения проблемы с некорректной работой ползунка сравнения изображений в ландшафтном режиме в React Native, важно учитывать изменение ширины экрана при изменении ориентации устройства. Ваш код уже имеет логику определения ориентации, но ширина окна и положение ползунка не обновляются соответствующим образом.
-
Используйте
useWindowDimensions
: Это позволит вам автоматически отслеживать изменения ширины и высоты окна. С помощью этого хука вам не нужно будет вручную обновлять ширину при изменении ориентации. -
Обновите состояние
sliderPosition
на основе новой ширины: При каждом изменении ориентации, обновляйте значения, чтобы гарантировать, что ползунок остается корректно позиционированным.
Ниже приведен обновленный код с учетом этих изменений:
import React, { useState, useEffect } from 'react';
import {
View,
Image,
PanResponder,
Animated,
StyleSheet,
ActivityIndicator,
useWindowDimensions,
} from 'react-native';
import Orientation from 'react-native-orientation-locker';
const ImageComparisonSlider = ({
leftImage,
rightImage,
initialPosition = 0.5,
}) => {
const { width } = useWindowDimensions(); // Получаем ширину окна через хук
const [sliderPosition, setSliderPosition] = useState(
new Animated.Value(initialPosition * width)
);
const [leftImageLoaded, setLeftImageLoaded] = useState(false);
const [rightImageLoaded, setRightImageLoaded] = useState(false);
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (_, gestureState) => {
const newPosition = Math.max(
0,
Math.min(gestureState.moveX, width)
);
sliderPosition.setValue(newPosition);
},
});
const renderImage = (imageUri, onLoadEnd, style) => (
<Image
source={{ uri: imageUri }}
style={style}
resizeMode="cover"
onLoadEnd={onLoadEnd}
/>
);
const bothImagesLoaded = leftImageLoaded && rightImageLoaded;
useEffect(() => {
const updateSliderPosition = () => {
setSliderPosition(new Animated.Value(initialPosition * width)); // Сброс значения ползунка
};
const subscription = Orientation.addOrientationListener(updateSliderPosition);
return () => {
subscription.remove();
};
}, [width]); // Обновляем при изменении ширины
return (
<View style={{ width: '100%', height: '100%', position: 'relative' }}>
{!bothImagesLoaded && (
<View style={styles.loaderContainer}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
)}
{bothImagesLoaded && (
<>
{renderImage(rightImage, () => {}, styles.image)}
<Animated.View
style={[
styles.leftImageContainer,
{
width: sliderPosition,
},
]}
>
{renderImage(leftImage, () => {}, styles.leftImage)}
</Animated.View>
<Animated.View
style={[styles.sliderLine, { left: sliderPosition }]}
{...panResponder.panHandlers}
>
<View style={styles.sliderHandle} />
</Animated.View>
</>
)}
<Image
source={{ uri: leftImage }}
style={styles.hiddenImage}
onLoad={() => setLeftImageLoaded(true)}
/>
<Image
source={{ uri: rightImage }}
style={styles.hiddenImage}
onLoad={() => setRightImageLoaded(true)}
/>
</View>
);
};
const styles = StyleSheet.create({
image: {
width: '100%',
height: '100%',
},
leftImageContainer: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
overflow: 'hidden',
},
leftImage: {
width: '100%',
height: '100%',
},
sliderLine: {
position: 'absolute',
top: 0,
bottom: 0,
width: 3,
backgroundColor: 'white',
},
sliderHandle: {
position: 'absolute',
top: '50%',
left: -10,
width: 20,
height: 20,
borderRadius: 10,
backgroundColor: 'white',
transform: [{ translateY: -10 }],
},
loaderContainer: {
...StyleSheet.absoluteFillObject,
justifyContent: 'center',
alignItems: 'center',
},
hiddenImage: {
width: 0,
height: 0,
},
});
export default ImageComparisonSlider;
Объяснение изменений:
- Использован хук
useWindowDimensions
, который автоматически обновляет ширину окна. - Удалена лишняя логика, связанная с отслеживанием изменений ориентации, поскольку теперь ширина окна всегда актуальна.
- Убедитесь, что вы обновляете
sliderPosition
при каждом изменении ширины, используяAnimated.Value
.
С помощью этих изменений ваш ползунок для сравнения изображений должен корректно функционировать в обоих режимах.
Если у вас есть дополнительные вопросы или вам нужна помощь с другим пакетом, не стесняйтесь спрашивать!