react-native-reanimated-viewer
Version:
A high performance image viewer in react-native used by react-native-reanimated
753 lines (751 loc) • 29 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
// src/components/ImageViewer.tsx
import React, {
forwardRef,
useRef,
useState,
useImperativeHandle,
useCallback,
useMemo
} from "react";
import {
StyleSheet,
Modal,
ActivityIndicator,
Dimensions,
Image,
View
} from "react-native";
import Animated, {
runOnJS,
useAnimatedStyle,
useSharedValue,
withTiming,
withDecay,
useWorkletCallback,
runOnUI,
cancelAnimation,
useAnimatedReaction
} from "react-native-reanimated";
import { GestureDetector, Gesture, GestureHandlerRootView } from "react-native-gesture-handler";
import { useStateRef } from "react-hooks-extension";
var GestureEnum = /* @__PURE__ */ ((GestureEnum2) => {
GestureEnum2["TAP"] = "TAP";
GestureEnum2["MODAL"] = "MODAL";
return GestureEnum2;
})(GestureEnum || {});
var IMAGE_SPACE = 20;
var styles = StyleSheet.create({
full: {
flex: 1
},
absolute: {
position: "absolute"
},
animatedContainer: {
backgroundColor: "#000000",
width: "100%",
height: "100%"
},
loading: {
alignItems: "center",
justifyContent: "center"
}
});
var ImageViewer = forwardRef((props, ref) => {
const screenDimensions = Dimensions.get("screen");
const {
data,
renderCustomComponent,
onLongPress,
imageResizeMode = "contain",
onChange,
dragUpToCloseEnabled,
maxScale = 3,
doubleTapScale = 2,
shouldCloseViewer,
originalLayoutOffset
} = props;
const imageItemRef = useRef([]);
const imageMemoSizeRef = useRef({});
const initIndexRef = useRef(0);
const [activeSource, setSourceData] = useState();
const activeLayout = useSharedValue(void 0);
const [animatedOver, setAnimatedOver] = useState(false);
const [loading, setLoading] = useState(false);
const loadedIndexListRef = useRef([]);
const [finishInit, setFinishInit] = useState(false);
const [isScale, setIsScale] = useState(false);
const originalImageSize = useSharedValue({});
const imageSize = useSharedValue({});
const activeIndex = useSharedValue(0);
const [activeIndexState, setActiveIndexState, activeIndexStateRef] = useStateRef(0);
const animatedRate = useSharedValue(0);
const imageX = useSharedValue(0);
const imageY = useSharedValue(0);
const closeRate = useSharedValue(0);
const imageScale = useSharedValue(1);
const savedImageScale = useSharedValue(1);
const savedImageX = useSharedValue(0);
const savedImageY = useSharedValue(0);
useAnimatedReaction(
() => savedImageScale.value !== 1,
(value) => runOnJS(setIsScale)(value)
);
const formatImageStyle = useWorkletCallback(
(imagePosition, imageSizeValue, currentOriginalImageSize, closeRateValue, imageXValue, imageYValue, activeLayoutValue, activeIndexValue, imageScaleValue) => {
var _a, _b, _c;
const relativeActiveIndex = (activeIndexValue % 3 + 3) % 3;
const currentIndex = Math.floor(activeIndexValue / 3) * 3 + (relativeActiveIndex > imagePosition && activeIndexValue > 0 ? 3 : 0) + imagePosition - 1;
if (!((_a = data[currentIndex]) == null ? void 0 : _a.key)) {
return {};
}
const currentImageSize = imageSizeValue[data[currentIndex].key];
const imageWHRate = ((_b = currentImageSize == null ? void 0 : currentImageSize.width) != null ? _b : 1) / ((_c = currentImageSize == null ? void 0 : currentImageSize.height) != null ? _c : 1);
const screenWHRate = screenDimensions.width / screenDimensions.height;
const currentHeight = imageWHRate > screenWHRate ? screenDimensions.width / imageWHRate : screenDimensions.height;
const currentWidth = imageWHRate > screenWHRate ? screenDimensions.width : screenDimensions.height * imageWHRate;
const changeHeight = dragUpToCloseEnabled || imageYValue > 0 ? (((currentOriginalImageSize == null ? void 0 : currentOriginalImageSize.height) || 0) - screenDimensions.height) * (imageScaleValue === 1 ? -Math.abs(imageYValue) : 1) / screenDimensions.height / 3 : 0;
const manualWidth = Math.min(currentWidth, currentWidth + changeHeight * imageWHRate);
const manualHeight = Math.min(currentHeight, currentHeight + changeHeight);
const manualTop = (screenDimensions.height - manualHeight) / 2 + imageYValue;
const manualLeft = imageXValue;
const resultWidth = manualWidth + (((activeLayoutValue == null ? void 0 : activeLayoutValue.width) || 0) - manualWidth) * closeRateValue;
const resultHeight = manualHeight + (((activeLayoutValue == null ? void 0 : activeLayoutValue.height) || 0) - manualHeight) * closeRateValue;
return {
width: resultWidth * imageScaleValue,
height: resultHeight * imageScaleValue,
transform: [
{
translateX: manualLeft - (screenDimensions.width + IMAGE_SPACE) * activeIndexValue + resultWidth * (1 - imageScaleValue) / 2 + (screenDimensions.width - manualWidth) * (1 - closeRateValue) / 2 + (((activeLayoutValue == null ? void 0 : activeLayoutValue.pageX) || 0) - manualLeft) * closeRateValue / imageScaleValue + (imagePosition - 1 + Math.floor(activeIndexValue / 3) * 3 + (relativeActiveIndex > imagePosition && activeIndexValue > 0 ? 3 : 0)) * (screenDimensions.width + IMAGE_SPACE)
},
{
translateY: manualTop + resultHeight * (1 - imageScaleValue) / 2 + (((activeLayoutValue == null ? void 0 : activeLayoutValue.pageY) || 0) - manualTop) * closeRateValue / imageScaleValue
}
],
display: (relativeActiveIndex + 1) % 3 !== imagePosition && (imageYValue || imageScaleValue < 1) ? "none" : "flex"
};
},
[screenDimensions.width, data, dragUpToCloseEnabled]
);
const imageStyle_0 = useAnimatedStyle(
() => formatImageStyle(
0,
imageSize.value,
originalImageSize.value,
closeRate.value,
imageX.value,
imageY.value,
activeLayout.value,
activeIndex.value,
imageScale.value
),
[formatImageStyle]
);
const imageStyle_1 = useAnimatedStyle(
() => formatImageStyle(
1,
imageSize.value,
originalImageSize.value,
closeRate.value,
imageX.value,
imageY.value,
activeLayout.value,
activeIndex.value,
imageScale.value
),
[formatImageStyle]
);
const imageStyle_2 = useAnimatedStyle(
() => formatImageStyle(
2,
imageSize.value,
originalImageSize.value,
closeRate.value,
imageX.value,
imageY.value,
activeLayout.value,
activeIndex.value,
imageScale.value
),
[formatImageStyle]
);
const imageStyleList = [imageStyle_0, imageStyle_1, imageStyle_2];
const originalImageStyle = useAnimatedStyle(() => {
var _a, _b;
const currentLayout = activeLayout.value;
if (!currentLayout) {
return {};
}
const imageWHRate = ((_a = originalImageSize.value.width) != null ? _a : 1) / ((_b = originalImageSize.value.height) != null ? _b : 1);
const screenWHRate = screenDimensions.width / screenDimensions.height;
const currentHeight = currentLayout.height + ((imageWHRate > screenWHRate ? screenDimensions.width / imageWHRate : screenDimensions.height) - currentLayout.height) * animatedRate.value;
const currentWidth = currentLayout.width + ((imageWHRate > screenWHRate ? screenDimensions.width : screenDimensions.height * imageWHRate) - currentLayout.width) * animatedRate.value;
return {
width: currentWidth,
height: currentHeight,
transform: [
{
translateX: currentLayout.pageX + (Math.max(0, (screenDimensions.width - currentWidth) / 2) - currentLayout.pageX) * animatedRate.value
},
{
translateY: currentLayout.pageY + (Math.max(0, (screenDimensions.height - currentHeight) / 2) - currentLayout.pageY) * animatedRate.value
}
]
};
}, [screenDimensions.width]);
const imageContainerStyle = useAnimatedStyle(() => {
let opacity = imageScale.value === 1 && (dragUpToCloseEnabled || imageY.value > 0) || closeRate.value > 0 ? Math.round(
255 * (1 - Math.abs(imageY.value) / screenDimensions.height) * (1 - closeRate.value)
).toString(16) : "ff";
opacity.length === 1 && (opacity = `0${opacity}`);
return {
backgroundColor: `#000000${opacity}`
};
}, [screenDimensions.height, dragUpToCloseEnabled]);
const hideOriginalImage = useCallback(() => {
var _a, _b;
(_b = (_a = imageItemRef.current[activeIndexStateRef.current || 0]) == null ? void 0 : _a.current) == null ? void 0 : _b.setNativeProps({
style: { opacity: 0 }
});
}, [activeIndexStateRef]);
const showOriginalImage = useCallback(() => {
var _a, _b;
(_b = (_a = imageItemRef.current[activeIndexStateRef.current || 0]) == null ? void 0 : _a.current) == null ? void 0 : _b.setNativeProps({
style: { alignSelf: "flex-start", opacity: 1 }
});
}, [activeIndexStateRef]);
const onCloseFinish = useCallback(
(shouldCloseGesture) => {
if (shouldCloseGesture && shouldCloseViewer && activeIndexStateRef.current !== void 0 && !shouldCloseViewer({
gesture: shouldCloseGesture,
index: activeIndexStateRef.current,
imageData: data[activeIndexStateRef.current],
loaded: false
}))
return;
setSourceData(void 0);
setLoading(false);
setFinishInit(false);
setAnimatedOver(false);
setTimeout(() => {
animatedRate.value = 0;
imageY.value = 0;
imageX.value = 0;
closeRate.value = 0;
showOriginalImage();
}, 0);
},
[
shouldCloseViewer,
activeIndexStateRef,
data,
animatedRate,
imageY,
imageX,
closeRate,
showOriginalImage
]
);
const onCloseMeasure = useCallback(
(_imageSize, shouldCloseGesture) => {
var _a, _b, _c, _d, _e;
if (shouldCloseGesture && shouldCloseViewer && activeIndexStateRef.current !== void 0 && !shouldCloseViewer({
gesture: shouldCloseGesture,
index: activeIndexStateRef.current,
imageData: data[activeIndexStateRef.current],
loaded: true
}))
return;
const imageWHRate = ((_a = _imageSize == null ? void 0 : _imageSize.width) != null ? _a : 1) / ((_b = _imageSize == null ? void 0 : _imageSize.height) != null ? _b : 1);
const screenWHRate = screenDimensions.width / screenDimensions.height;
const currentWidth = imageWHRate > screenWHRate ? screenDimensions.width : screenDimensions.height * imageWHRate;
const currentHeight = imageWHRate > screenWHRate ? screenDimensions.width / imageWHRate : screenDimensions.height;
const layoutFinish = (width = currentWidth / 3, height = currentHeight / 3, pageX = (screenDimensions.width - currentWidth / 2) / 2, pageY = (initIndexRef.current > (activeIndexStateRef.current || 0) ? -1 : 1) * screenDimensions.height) => {
activeLayout.value = { width, height, pageX, pageY };
setTimeout(() => {
animatedRate.value = withTiming(1, void 0, (finished) => {
finished && runOnJS(setAnimatedOver)(true);
});
closeRate.value = withTiming(1, void 0, (finished) => {
if (finished) {
runOnJS(onCloseFinish)();
}
});
}, 0);
};
if ((_d = imageItemRef.current[(_c = activeIndexStateRef.current) != null ? _c : -1]) == null ? void 0 : _d.current) {
(_e = imageItemRef.current[activeIndexStateRef.current].current) == null ? void 0 : _e.measure(
(_x, _y, width, height, pageX, pageY) => {
layoutFinish(
width,
height,
pageX + ((originalLayoutOffset == null ? void 0 : originalLayoutOffset.pageX) || 0),
pageY + ((originalLayoutOffset == null ? void 0 : originalLayoutOffset.pageY) || 0)
);
}
);
} else {
layoutFinish();
}
},
[
shouldCloseViewer,
activeIndexStateRef,
data,
screenDimensions.width,
screenDimensions.height,
activeLayout,
animatedRate,
closeRate,
onCloseFinish,
originalLayoutOffset == null ? void 0 : originalLayoutOffset.pageX,
originalLayoutOffset == null ? void 0 : originalLayoutOffset.pageY
]
);
const onClose = useWorkletCallback(
(shouldCloseGesture) => {
imageScale.value = withTiming(1);
runOnJS(onCloseMeasure)(imageSize.value[data[activeIndex.value].key], shouldCloseGesture);
savedImageScale.value = 1;
savedImageX.value = 0;
savedImageY.value = 0;
},
[data, onCloseMeasure]
);
const onRequestClose = useWorkletCallback(() => {
onClose("MODAL" /* MODAL */);
}, [onClose]);
const setImageSize = useWorkletCallback((key, _source) => {
imageSize.value = Object.assign({}, imageSize.value, { [key]: _source });
}, []);
const dragLastTime = useSharedValue(0);
const imageDragGestureY = useMemo(
() => Gesture.Pan().activeOffsetY(dragUpToCloseEnabled ? [-20, 20] : 20).onStart(() => {
runOnJS(hideOriginalImage)();
dragLastTime.value = Date.now().valueOf();
}).onUpdate((event) => {
imageX.value = event.translationX;
imageY.value = event.translationY;
}).onEnd((event) => {
const translationY = dragUpToCloseEnabled ? Math.abs(event.translationY) : event.translationY;
if (translationY < 100 && (Date.now().valueOf() - dragLastTime.value > 500 || event.translationY <= 0 && !dragUpToCloseEnabled)) {
imageX.value = withTiming(0);
imageY.value = withTiming(0);
runOnJS(showOriginalImage)();
} else {
onClose();
}
}),
[
dragUpToCloseEnabled,
hideOriginalImage,
dragLastTime,
imageX,
imageY,
showOriginalImage,
onClose
]
);
const _onChange = useCallback(
(currentIndex) => {
setActiveIndexState(currentIndex);
onChange == null ? void 0 : onChange(currentIndex);
},
[onChange, setActiveIndexState]
);
const imageDragGestureX = useMemo(
() => Gesture.Pan().activeOffsetX([-20, 20]).onStart(() => {
dragLastTime.value = Date.now().valueOf();
}).onUpdate((event) => {
imageX.value = event.translationX * ((event.translationX < 0 ? activeIndex.value < data.length - 1 : activeIndex.value > 0) ? 1 : 0.4);
}).onEnd((event) => {
if ((event.translationX < 0 ? activeIndex.value < data.length - 1 : activeIndex.value > 0) && (Date.now().valueOf() - dragLastTime.value < 500 || Math.abs(event.translationX) > screenDimensions.width / 2)) {
imageX.value = withTiming(
(screenDimensions.width + IMAGE_SPACE) * (event.translationX < 0 ? -1 : 1),
{ duration: 200 },
() => {
activeIndex.value += event.translationX < 0 ? 1 : -1;
imageX.value = 0;
runOnJS(_onChange)(activeIndex.value);
}
);
} else {
imageX.value = withTiming(0);
}
savedImageX.value = 0;
}),
[dragLastTime, imageX, activeIndex, savedImageX, screenDimensions.width, data, _onChange]
);
const imageDragGestureMove = useMemo(
() => Gesture.Pan().minDistance(5).onStart(() => {
cancelAnimation(imageX);
cancelAnimation(imageY);
}).onUpdate((event) => {
imageX.value = savedImageX.value + event.translationX;
imageY.value = savedImageY.value + event.translationY;
}).onEnd((event) => {
var _a, _b;
savedImageX.value = imageX.value;
savedImageY.value = imageY.value;
const currentImageSize = imageSize.value[data[activeIndex.value].key];
const imageWHRate = ((_a = currentImageSize.width) != null ? _a : 1) / ((_b = currentImageSize.height) != null ? _b : 1);
const screenWHRate = screenDimensions.width / screenDimensions.height;
const currentImageHeight = imageWHRate > screenWHRate ? screenDimensions.width / imageWHRate : screenDimensions.height;
const currentImageWidth = imageWHRate > screenWHRate ? screenDimensions.width : screenDimensions.height * imageWHRate;
const currentWidthRange = currentImageWidth * (savedImageScale.value - 1) / 2;
const currentImageX = Math.min(
currentWidthRange,
Math.max(-currentWidthRange, savedImageX.value)
);
if (currentImageX !== savedImageX.value) {
imageX.value = withTiming(currentImageX);
savedImageX.value = currentImageX;
} else if (event == null ? void 0 : event.velocityX) {
const targetImageX = Math.min(
currentWidthRange,
Math.max(
-currentWidthRange,
savedImageX.value + (event.velocityX > 0 ? 100 : -100) * savedImageScale.value
)
);
imageX.value = withDecay(
{
velocity: event.velocityX,
clamp: event.velocityX > 0 ? [savedImageX.value, targetImageX] : [targetImageX, savedImageX.value]
},
() => {
savedImageX.value = imageX.value;
}
);
}
const currentHeightRange = Math.abs(
(currentImageHeight * savedImageScale.value - screenDimensions.height) / 2
);
const currentImageY = Math.min(
currentHeightRange,
Math.max(-currentHeightRange, savedImageY.value)
);
if (currentImageY !== savedImageY.value) {
imageY.value = withTiming(currentImageY);
savedImageY.value = currentImageY;
} else if (event == null ? void 0 : event.velocityY) {
const targetImageY = Math.min(
currentHeightRange,
Math.max(
-currentHeightRange,
savedImageY.value + (event.velocityY > 0 ? 100 : -100) * savedImageScale.value
)
);
imageY.value = withDecay(
{
velocity: event.velocityY,
clamp: event.velocityY > 0 ? [savedImageY.value, targetImageY] : [targetImageY, savedImageY.value]
},
() => {
savedImageY.value = imageY.value;
}
);
}
}),
[
activeIndex,
data,
imageSize,
imageX,
imageY,
savedImageScale,
savedImageX,
savedImageY,
screenDimensions.height,
screenDimensions.width
]
);
const resetScale = useWorkletCallback(() => {
imageScale.value = withTiming(1);
imageX.value = withTiming(0);
imageY.value = withTiming(0);
savedImageScale.value = 1;
savedImageX.value = 0;
savedImageY.value = 0;
}, []);
const imageOriginalTapGesture = useMemo(
() => Gesture.Tap().onEnd(() => {
runOnJS(onCloseFinish)("TAP" /* TAP */);
}),
[onCloseFinish]
);
const imageSingleTapGesture = useMemo(
() => Gesture.Tap().onStart(() => {
if (imageScale.value === 1) {
runOnJS(hideOriginalImage)();
}
onClose("TAP" /* TAP */);
}),
[onClose, hideOriginalImage, imageScale]
);
const imageDoubleTapGesture = useMemo(
() => Gesture.Tap().numberOfTaps(2).onStart((event) => {
if (imageScale.value !== 1) {
resetScale();
} else {
imageScale.value = withTiming(doubleTapScale);
savedImageScale.value = doubleTapScale;
const currentX = (screenDimensions.width / 2 - event.x) * doubleTapScale;
imageX.value = withTiming(currentX);
savedImageX.value = currentX;
const currentY = (screenDimensions.height / 2 - event.y) * doubleTapScale;
imageY.value = withTiming(currentY);
savedImageY.value = currentY;
}
}),
[
imageScale,
resetScale,
doubleTapScale,
savedImageScale,
screenDimensions.width,
screenDimensions.height,
imageX,
savedImageX,
imageY,
savedImageY
]
);
const imageTapGesture = useMemo(
() => Gesture.Exclusive(imageDoubleTapGesture, imageSingleTapGesture),
[imageDoubleTapGesture, imageSingleTapGesture]
);
const imagePinchGesture = useMemo(
() => Gesture.Pinch().onUpdate((event) => {
imageScale.value = savedImageScale.value * event.scale;
imageX.value = savedImageX.value * event.scale;
imageY.value = savedImageY.value * event.scale;
}).onEnd(() => {
const currentScale = Math.min(Math.max(1, imageScale.value), maxScale);
if (currentScale === 1) {
resetScale();
} else {
imageScale.value = withTiming(currentScale);
const changedScale = currentScale / savedImageScale.value;
savedImageScale.value = currentScale;
const currentImageX = savedImageX.value * changedScale;
const currentImageY = savedImageY.value * changedScale;
imageX.value = withTiming(currentImageX);
imageY.value = withTiming(currentImageY);
savedImageX.value = currentImageX;
savedImageY.value = currentImageY;
}
}),
[imageScale, savedImageScale, imageX, imageY, savedImageX, savedImageY, maxScale, resetScale]
);
const imageLongPressGesture = useMemo(
() => Gesture.LongPress().onStart(() => {
if (onLongPress) {
runOnJS(onLongPress)({ index: activeIndex.value, item: data[activeIndex.value] });
}
}),
[onLongPress, activeIndex, data]
);
const imageGesture = useMemo(
() => Gesture.Race(
imageDragGestureY,
imageDragGestureX,
imageTapGesture,
imagePinchGesture,
imageLongPressGesture
),
[
imageDragGestureY,
imageDragGestureX,
imageTapGesture,
imagePinchGesture,
imageLongPressGesture
]
);
const imageGestureWithScale = useMemo(
() => Gesture.Race(imageDragGestureMove, imageTapGesture, imagePinchGesture, imageLongPressGesture),
[imageDragGestureMove, imageTapGesture, imagePinchGesture, imageLongPressGesture]
);
useImperativeHandle(ref, () => ({
init: ({ itemRef, index }) => {
imageItemRef.current[index] = itemRef;
},
show: ({ index, source = data[index].source }) => {
var _a, _b;
const _screenDimensions = Dimensions.get("screen");
initIndexRef.current = index;
activeIndex.value = index;
setActiveIndexState(index);
setSourceData(source);
const startShow = () => {
var _a2;
if ((_a2 = imageItemRef.current[index]) == null ? void 0 : _a2.current) {
imageItemRef.current[index].current.measure((_x, _y, width, height, pageX, pageY) => {
activeLayout.value = {
width,
height,
pageX: pageX + ((originalLayoutOffset == null ? void 0 : originalLayoutOffset.pageX) || 0),
pageY: pageY + ((originalLayoutOffset == null ? void 0 : originalLayoutOffset.pageY) || 0)
};
setTimeout(() => {
animatedRate.value = withTiming(1, void 0, (finished) => {
finished && runOnJS(setAnimatedOver)(true);
});
}, 0);
});
} else {
setTimeout(() => {
animatedRate.value = withTiming(1, void 0, (finished) => {
finished && runOnJS(setAnimatedOver)(true);
});
}, 0);
}
};
if (source.width && source.height) {
originalImageSize.value = { width: source.width, height: source.height };
startShow();
} else if (imageMemoSizeRef.current[source.uri || ""]) {
originalImageSize.value = imageMemoSizeRef.current[source.uri || ""];
startShow();
} else {
(_b = (_a = Image.getSize(
source.uri || "",
// 0.77.0 开始支持 后续可以移除
(width, height) => {
imageMemoSizeRef.current[source.uri || ""] = originalImageSize.value = {
width,
height
};
startShow();
},
() => {
originalImageSize.value = _screenDimensions;
startShow();
}
)) == null ? void 0 : _a.then) == null ? void 0 : _b.call(_a, ({ width, height }) => {
imageMemoSizeRef.current[source.uri || ""] = originalImageSize.value = {
width,
height
};
startShow();
}).catch(() => {
originalImageSize.value = _screenDimensions;
startShow();
});
}
}
}));
return <Modal
visible={!!activeSource}
animationType="fade"
transparent
onRequestClose={onRequestClose}
statusBarTranslucent
><GestureHandlerRootView style={styles.full}>
{activeSource ? <GestureDetector
gesture={!animatedOver || !finishInit ? imageOriginalTapGesture : isScale ? imageGestureWithScale : imageGesture}
><View>
<Animated.View style={[styles.animatedContainer, imageContainerStyle]}>
{Array.from(new Array(3)).map((_, index) => {
const relativeActiveIndex = (activeIndexState % 3 + 3) % 3;
const currentIndex = Math.floor(activeIndexState / 3) * 3 + (relativeActiveIndex > index && activeIndexState > 0 ? 3 : 0) + index - 1;
if (currentIndex < 0 || currentIndex >= data.length) {
return null;
}
const currentData = data[currentIndex];
return <Animated.Image
key={`image-viewer-${currentIndex}`}
resizeMode={imageResizeMode}
source={typeof currentData.source === "object" ? __spreadValues({}, currentData.source) : currentData.source}
onLoadStart={() => {
if (relativeActiveIndex === index - 1 && !loadedIndexListRef.current.includes(activeIndexState)) {
setLoading(true);
}
}}
onLoad={({ nativeEvent: { source } }) => {
runOnUI(setImageSize)(currentData.key, source || currentData.source);
if (relativeActiveIndex === index - 1) {
setLoading(false);
setFinishInit(true);
if (!loadedIndexListRef.current.includes(activeIndexState)) {
loadedIndexListRef.current.push(activeIndexState);
}
}
}}
style={[styles.absolute, imageStyleList[index]]}
/>;
})}
{loading && animatedOver ? <ActivityIndicator
style={[StyleSheet.absoluteFill, styles.loading]}
color="#fff"
/> : null}
</Animated.View>
{!animatedOver || !finishInit ? <View style={[StyleSheet.absoluteFill, styles.animatedContainer]}>
<Animated.Image
source={typeof activeSource === "object" ? __spreadValues({}, activeSource) : activeSource}
resizeMode={imageResizeMode}
style={[styles.absolute, originalImageStyle]}
/>
{!finishInit && animatedOver ? <ActivityIndicator
style={[StyleSheet.absoluteFill, styles.loading]}
color="#fff"
/> : null}
</View> : null}
</View></GestureDetector> : null}
{renderCustomComponent == null ? void 0 : renderCustomComponent({ item: data[activeIndexState], index: activeIndexState })}
</GestureHandlerRootView></Modal>;
});
var ImageViewer_default = React.memo(ImageViewer);
// src/components/ImageWrapper.tsx
import React2, { useCallback as useCallback2, useEffect, useRef as useRef2 } from "react";
import {
TouchableOpacity
} from "react-native";
var ImageWrapper = (props) => {
const { viewerRef, index, children, source, style, onPress, wrapperProps } = props;
const containerRef = useRef2(null);
const _onPress = useCallback2(() => {
var _a;
if ((onPress == null ? void 0 : onPress()) === false) {
return;
}
(_a = viewerRef.current) == null ? void 0 : _a.show({
index,
source
});
}, [index, source, viewerRef, onPress]);
useEffect(() => {
var _a;
(_a = viewerRef.current) == null ? void 0 : _a.init({ itemRef: containerRef, index });
}, [index, viewerRef]);
return <TouchableOpacity
activeOpacity={1}
onPress={_onPress}
{...wrapperProps}
ref={containerRef}
style={[{ alignSelf: "flex-start" }, style]}
>{children}</TouchableOpacity>;
};
var ImageWrapper_default = React2.memo(ImageWrapper);
export {
GestureEnum,
ImageViewer_default as ImageViewer,
ImageWrapper_default as ImageWrapper
};
//# sourceMappingURL=index.js.map