import React, { useCallback, useEffect, useState } from 'react'; import { ComposedGesture, Gesture, GestureDetector, GestureType, } from 'react-native-gesture-handler'; import { cancelAnimation, useAnimatedStyle, useSharedValue, withSpring, } from 'react-native-reanimated'; import ViewGestureHandlerRootHOC from './ViewGestureHandlerRootHOC'; import type { ImageElementType, SimpleImageViewProps } from './types'; import { ActivityIndicator, Dimensions, FlatList, NativeScrollEvent, NativeSyntheticEvent, Text, View, } from 'react-native'; import ThumbnailComponent from './ThumbnailComponent'; import { styles } from './styles'; const SimpleImageView = ({ imageUri, bgColor, images, viewMode, selectedIndex, perPage, itemMargin, showTitle, complementaryBgColor, showPage, token, tokenHeader, requestMethod }: SimpleImageViewProps) => { const DeviceWidth = Dimensions.get('window').width; const imageSize = (perPage! < 4 ? Math.round(DeviceWidth / 4) : Math.round(DeviceWidth / perPage!)) - itemMargin!; const imageBorderRadius = imageSize * 0.05; const tmpImgObj = { uri: '', title: '' }; const [nowImage, setNowImage] = useState(tmpImgObj); const [totPage, setToTPage] = useState(undefined); const [imgIndex, setImgIndex] = useState(0); const [imgArray, setImgArray] = useState([]); const [nowPage , setNowPage] = useState(1); const startX = useSharedValue(0); const transX = useSharedValue(0); const startY = useSharedValue(0); const transY = useSharedValue(0); const scaleXY = useSharedValue(1); const savedScale = useSharedValue(1); const rotation = useSharedValue(0); const savedRotation = useSharedValue(0); useEffect(() => { if (viewMode === 'multi' && images && images?.length > 0) { setToTPage(Math.ceil(images?.length / perPage!)); setNowImage(images[selectedIndex!] ?? imageUri); } else { setToTPage(1); setNowImage(imageUri); } }, [images, imageUri]); useEffect(() => { if (totPage && totPage > 0 && images && images?.length > 0) { const newArray: ImageElementType[][] = []; Array.from({ length: totPage }).forEach((_, index) => { newArray.push( images?.slice(perPage! * index, perPage! * index + perPage!) ); }); setImgArray(newArray); } }, [totPage]); useEffect(() => { if ( (viewMode !== 'multi') || !imgArray) return; setNowImage(tmpImgObj); setNowImage(images![imgIndex]); }, [imgIndex]); const transXYStyle = useAnimatedStyle(() => { return { transform: [ { translateX: transX.value }, { translateY: transY.value }, { scale: scaleXY.value }, { rotateZ: `${(rotation.value / Math.PI) * 180}deg` }, ], }; }, []); const scaleAnimation = () => { 'worklet'; cancelAnimation(scaleXY); cancelAnimation(startX); cancelAnimation(startY); savedScale.value = 1; startX.value = 0; startY.value = 0; scaleXY.value = withSpring(1); transX.value = withSpring(0); transY.value = withSpring(0); rotation.value = withSpring(0); savedRotation.value = 0; }; const panHandler: ComposedGesture | GestureType | undefined = Gesture.Pan() .maxPointers(1) .onStart(() => { transX.value = startX.value; transY.value = startY.value; }) .onUpdate((event) => { transX.value = event.translationX + startX.value; transY.value = event.translationY + startY.value; }) .onEnd(() => { startX.value = transX.value; startY.value = transY.value; }); const pinchHandler = Gesture.Pinch() .onUpdate((e) => { scaleXY.value = savedScale.value * (e.scale > 0 ? e.scale : 1); }) .onEnd(() => { savedScale.value = scaleXY.value > 0 ? scaleXY.value : 1; }); const tapHandler: ComposedGesture | GestureType | undefined = Gesture.Tap() .numberOfTaps(2) .minPointers(1) .maxDelay(250) .maxDistance(70) .onEnd(scaleAnimation); const rotateHandler: ComposedGesture | GestureType | undefined = Gesture.Rotation() .onUpdate((e) => { rotation.value = savedRotation.value + e.rotation; }) .onEnd(() => { savedRotation.value = rotation.value; }); const simultaneousHandler = Gesture.Simultaneous( panHandler, pinchHandler, tapHandler, rotateHandler ); const updateNowImageIndex = (pageIndex: number, imageIndex: number) => { setImgIndex(pageIndex * perPage! + imageIndex); }; const updateNowPage = useCallback((e: NativeSyntheticEvent) => { setNowPage((Math.floor(e?.nativeEvent?.contentOffset?.x / e?.nativeEvent?.layoutMeasurement?.width)??0)+1); },[]); const keyExtractor = (_: ImageElementType[], index: number) => index.toString(); const _renderItem = ({ item, index, }: { item: ImageElementType[]; index: number; }) => { return ( {item?.map((el, imageIndex) => { return ( ); })} ); }; return images && (imageUri?.uri || images?.length > 0) ? ( { showTitle && nowImage?.title && nowImage?.title?.length > 0 ? ( {`${nowImage?.title}`} ) : null} {nowImage && nowImage?.uri ? ( ) : ( )} {viewMode === 'multi' && (totPage && totPage > 0) && ( imgArray && imgArray?.length > 0) ? ( } style={{ flex: 1 }} windowSize={1} initialNumToRender={ totPage ? (totPage) : undefined } keyExtractor={keyExtractor} data={imgArray} renderItem={_renderItem} horizontal={true} pagingEnabled={true} removeClippedSubviews={true} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} onMomentumScrollEnd={updateNowPage} onEndReachedThreshold={0.5} onMomentumScrollBegin={undefined} onEndReached={undefined} /> ) : null} { (viewMode === 'multi' && showPage) ? {`${nowPage} / ${totPage}`} : } ) : null; }; export default SimpleImageView;