react-native-media-viewing
Version:
React Native modal component for viewing images and video as a sliding gallery
84 lines (83 loc) • 4.23 kB
JavaScript
import React, { useCallback, useRef, useState } from "react";
import VideoPlayer from "react-native-video-controls";
import { Animated, ScrollView, Dimensions, StyleSheet, } from "react-native";
import useImageDimensions from "../../hooks/useImageDimensions";
import usePanResponder from "../../hooks/usePanResponder";
import { getImageStyles, getImageTransform } from "../../utils";
import { MediaLoading } from "./MediaLoading";
const SWIPE_CLOSE_OFFSET = 75;
const SWIPE_CLOSE_VELOCITY = 1.75;
const SCREEN = Dimensions.get("window");
const SCREEN_WIDTH = SCREEN.width;
const SCREEN_HEIGHT = SCREEN.height;
const MediaItem = ({ mediaSrc, onZoom, onRequestClose, onLongPress, delayLongPress, swipeToCloseEnabled = true, doubleTapToZoomEnabled = true, }) => {
const imageContainer = useRef(null);
const imageDimensions = useImageDimensions(mediaSrc);
const [translate, scale] = getImageTransform(imageDimensions, SCREEN);
const scrollValueY = new Animated.Value(0);
const [isLoaded, setLoadEnd] = useState(false);
const onLoaded = useCallback(() => setLoadEnd(true), []);
const onZoomPerformed = useCallback((isZoomed) => {
var _a;
onZoom(isZoomed);
if ((_a = imageContainer) === null || _a === void 0 ? void 0 : _a.current) {
imageContainer.current.setNativeProps({
scrollEnabled: !isZoomed,
});
}
}, [imageContainer]);
const onLongPressHandler = useCallback(() => {
onLongPress(mediaSrc);
}, [mediaSrc, onLongPress]);
const [panHandlers, scaleValue, translateValue] = usePanResponder({
initialScale: scale || 1,
initialTranslate: translate || { x: 0, y: 0 },
onZoom: onZoomPerformed,
doubleTapToZoomEnabled,
onLongPress: onLongPressHandler,
delayLongPress,
});
const imagesStyles = getImageStyles(imageDimensions, translateValue, scaleValue);
const imageOpacity = scrollValueY.interpolate({
inputRange: [-SWIPE_CLOSE_OFFSET, 0, SWIPE_CLOSE_OFFSET],
outputRange: [0.7, 1, 0.7],
});
const imageStylesWithOpacity = { ...imagesStyles, opacity: imageOpacity };
const onScrollEndDrag = ({ nativeEvent, }) => {
var _a, _b, _c, _d, _e, _f;
const velocityY = (_c = (_b = (_a = nativeEvent) === null || _a === void 0 ? void 0 : _a.velocity) === null || _b === void 0 ? void 0 : _b.y, (_c !== null && _c !== void 0 ? _c : 0));
const offsetY = (_f = (_e = (_d = nativeEvent) === null || _d === void 0 ? void 0 : _d.contentOffset) === null || _e === void 0 ? void 0 : _e.y, (_f !== null && _f !== void 0 ? _f : 0));
if ((Math.abs(velocityY) > SWIPE_CLOSE_VELOCITY &&
offsetY > SWIPE_CLOSE_OFFSET) ||
offsetY > SCREEN_HEIGHT / 2) {
onRequestClose();
}
};
const onScroll = ({ nativeEvent, }) => {
var _a, _b, _c;
const offsetY = (_c = (_b = (_a = nativeEvent) === null || _a === void 0 ? void 0 : _a.contentOffset) === null || _b === void 0 ? void 0 : _b.y, (_c !== null && _c !== void 0 ? _c : 0));
scrollValueY.setValue(offsetY);
};
return (<ScrollView ref={imageContainer} style={styles.listItem} pagingEnabled nestedScrollEnabled showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} contentContainerStyle={styles.imageScrollContainer} scrollEnabled={swipeToCloseEnabled} {...(swipeToCloseEnabled && {
onScroll,
onScrollEndDrag,
})}>
{mediaSrc.mediaType === "image" && (<Animated.Image {...panHandlers} source={mediaSrc} style={imageStylesWithOpacity} onLoad={onLoaded}/>)}
{mediaSrc.mediaType === "video" && (<VideoPlayer source={mediaSrc} navigator={null} disableBack disableVolume disableFullscreen style={styles.containerMedia} onLoad={onLoaded} paused/>)}
{(!isLoaded || !imageDimensions) && <MediaLoading />}
</ScrollView>);
};
const styles = StyleSheet.create({
listItem: {
width: SCREEN_WIDTH,
height: SCREEN_HEIGHT,
},
imageScrollContainer: {
height: SCREEN_HEIGHT * 2,
},
containerMedia: {
width: SCREEN_WIDTH,
height: "100%",
},
});
export default React.memo(MediaItem);