Open Splicer97 opened 1 year ago
This is definitely something I wanna add, but since it would involves the FlatList, the gestures would be a bit more complex, and I haven't studied how I would do this yet
I think you can give FlatList or ScrollView scrollEnabled={false}, and give the listRef as props to ImageViewer. And then, you can scroll List by scrollTo (from reanimated) with gesture handler. List:
<ScrollView
ref={listRef}
scrollEnabled={false}
pagingEnabled
horizontal
showsHorizontalScrollIndicator={false}>
{images.map((image, index) => (
<ImageViewer
listRef={listRef}
index={index}
{...image}
key={index}
maxIndex={images.length - 1}
onDismiss={handleDismiss}
/>
))}
</ScrollView>
ImageViewer:
onActive: () => {
scrollTo(listRef, index * SCREEN_WIDTH - overOffsetX, 0, animated: false)
},
onEnd: () => {
if (Math.abs(overOffsetX) >= HORIZONTAL_THRESHOLD) {
overOffsetX > 0
? scrollTo(listRef, (index - 1) * SCREEN_WIDTH, 0, true)
: scrollTo(listRef, (index + 1) * SCREEN_WIDTH, 0, true);
} else {
scrollTo(listRef, index * SCREEN_WIDTH, 0, true);
}
}
I think you can give FlatList or ScrollView scrollEnabled={false}, and give the listRef as props to ImageViewer. And then, you can scroll List by scrollTo (from reanimated) with gesture handler. List:
<ScrollView ref={listRef} scrollEnabled={false} pagingEnabled horizontal showsHorizontalScrollIndicator={false}> {images.map((image, index) => ( <ImageViewer listRef={listRef} index={index} {...image} key={index} maxIndex={images.length - 1} onDismiss={handleDismiss} /> ))} </ScrollView>
ImageViewer:
onActive: () => { scrollTo(listRef, index * SCREEN_WIDTH - overOffsetX, 0, animated: false) }, onEnd: () => { if (Math.abs(overOffsetX) >= HORIZONTAL_THRESHOLD) { overOffsetX > 0 ? scrollTo(listRef, (index - 1) * SCREEN_WIDTH, 0, true) : scrollTo(listRef, (index + 1) * SCREEN_WIDTH, 0, true); } else { scrollTo(listRef, index * SCREEN_WIDTH, 0, true); } }
@dodoto could you please explain in more detail the location of the code in ImageViewer?
a demo code with pan and tap scale
import React, { FC, useEffect, useRef } from 'react'; import { Dimensions, Image, StyleSheet, ImageURISource, Text, ScrollView, FlatList } from 'react-native'; import Animated, { useAnimatedStyle, useSharedValue, useAnimatedGestureHandler, withTiming, scrollTo, useAnimatedRef } from 'react-native-reanimated'; import { PanGestureHandler, PanGestureHandlerGestureEvent, TapGestureHandler, TapGestureHandlerGestureEvent } from 'react-native-gesture-handler';
const source = require('../assets/masthead.png');
const { width, height } = Dimensions.get('window'); // if statusbar translucent, use Dimensions.get('screen')
const center = { x: width / 2, y: height / 2 };
const getImageSize = (source: ImageURISource | number): Promise<{ width: number, height: number }> => { return new Promise((resolve,reject) => { if (typeof source === 'number') { const { width, height } = Image.resolveAssetSource(source) resolve({width, height}) } else { Image.getSize(source.uri!, (width, height) => { resolve({width, height}) },(error) => { reject(error) }) } }) }
const styles = StyleSheet.create({ root: { flex: 1 }, container: { width, height, position: "relative", justifyContent: "center", alignItems: 'center', backgroundColor: "black" }, image: { width,
}, title: { position: 'absolute', top: 0, left: 0, right: 0, textAlign: 'center', fontSize: 20, fontWeight: 'bold', color: 'white', }, });
type PanGestureHandlerGestureContext = { right: number; left: number; startTranslateX: number; overOffsetX: number; };
interface ImageViewerProps { index: number; title: string; listRef: React.RefObject<ScrollView | FlatList>; }
export const ImageViewer: FC
const tap = useRef(); const pan = useRef();
const handleTap = useAnimatedGestureHandler
// only handle horizontal const handlePan = useAnimatedGestureHandler<PanGestureHandlerGestureEvent, PanGestureHandlerGestureContext>({ onStart(_event, context) { console.log('title', title); const leftBound = Math.min(center.x - (imageSize.value.width imageScale.value) / 2, 0); // ----> const rightBound = -leftBound; // <------ context.left = leftBound; context.right = rightBound; context.startTranslateX = imageTranslate.value.x; }, onActive(event, context) { const rawTranslate = event.translationX + context.startTranslateX; let translateX = rawTranslate; let overOffsetX = 0; // trigger list slide if (translateX > context.right) { translateX = context.right; overOffsetX = rawTranslate - context.right; console.log('slide to right'); } if (translateX < context.left) { translateX = context.left; overOffsetX = rawTranslate - context.left; console.log('slide to left'); } context.overOffsetX = overOffsetX; if (overOffsetX !== 0) { scrollTo(listRef, -overOffsetX + index width, 0, false); }
const translateY = imageTranslate.value.y;
imageTranslate.value = {
x: translateX,
y: translateY,
};
},
onEnd(_event, context) {
const shreshold = 100;
let scrollX = index * width;
if (context.overOffsetX >= shreshold) {
scrollX = (index - 1) * width;
}
if (context.overOffsetX <= -shreshold) {
scrollX = (index + 1) * width;
}
scrollTo(listRef, scrollX, 0, true);
},
});
const imageStyle = useAnimatedStyle(() => ({ width: imageSize.value.width, height: imageSize.value.height, transform: [ { translateX: imageTranslate.value.x }, { translateY: imageTranslate.value.y }, { scale: imageScale.value }, ], }))
useEffect(() => { getImageSize(source).then((size) => { imageSize.value = { width, height: width * size.height / size.width, }; }) }, []);
return (
Hi! How about array of images (imageUrls)? Something like react-native-image-viewing.