UNPKG

react-native-zoom-toolkit

Version:

Smoothly zoom any image, video or component you want!

150 lines (149 loc) 5.75 kB
import React, { useState } from 'react'; import { StyleSheet } from 'react-native'; import Animated, { measure, runOnJS, useAnimatedRef, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming } from 'react-native-reanimated'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import { useVector } from '../../commons/hooks/useVector'; import { useSizeVector } from '../../commons/hooks/useSizeVector'; import { resizeToAspectRatio } from '../../commons/utils/resizeToAspectRatio'; import withSnapbackValidation from '../../commons/hoc/withSnapbackValidation'; const DEFAULT_HITSLOP = { vertical: 0, horizontal: 0 }; const SnapbackZoom = ({ children, hitSlop = DEFAULT_HITSLOP, resizeConfig, timingConfig, gesturesEnabled = true, onTap, onDoubleTap, onPinchStart, onPinchEnd, onGestureActive, onGestureEnd }) => { const [internalGesturesEnabled, setGesturesEnabled] = useState(true); const switchGestureStatus = enabled => { setGesturesEnabled(enabled); }; const position = useVector(0, 0); const translate = useVector(0, 0); const origin = useVector(0, 0); const scale = useSharedValue(1); const containerSize = useSizeVector((resizeConfig === null || resizeConfig === void 0 ? void 0 : resizeConfig.size.width) ?? 0, (resizeConfig === null || resizeConfig === void 0 ? void 0 : resizeConfig.size.height) ?? 0); const childrenSize = useDerivedValue(() => { return resizeToAspectRatio({ resizeConfig, width: containerSize.width.value, height: containerSize.height.value, scale: scale.value }); }, [resizeConfig, scale, containerSize]); const containerRef = useAnimatedRef(); const measurePinchContainer = () => { 'worklet'; const measuremet = measure(containerRef); if (measuremet !== null) { containerSize.width.value = measuremet.width; containerSize.height.value = measuremet.height; position.x.value = measuremet.pageX; position.y.value = measuremet.pageY; } }; useDerivedValue(() => { const { width, height } = childrenSize.value; onGestureActive === null || onGestureActive === void 0 || onGestureActive({ x: position.x.value, y: position.y.value, width: containerSize.width.value, height: containerSize.height.value, resizedWidth: resizeConfig ? width : undefined, resizedHeight: resizeConfig ? height : undefined, translateX: translate.x.value, translateY: translate.y.value, scale: scale.value }); }, [position, translate, scale, containerSize, childrenSize]); const pinch = Gesture.Pinch().hitSlop(hitSlop).enabled(gesturesEnabled && internalGesturesEnabled).onStart(e => { onPinchStart && runOnJS(onPinchStart)(e); measurePinchContainer(); origin.x.value = e.focalX - containerSize.width.value / 2; origin.y.value = e.focalY - containerSize.height.value / 2; }).onUpdate(e => { const deltaX = e.focalX - containerSize.width.value / 2 - origin.x.value; const deltaY = e.focalY - containerSize.height.value / 2 - origin.y.value; const toX = -1 * (origin.x.value * e.scale - origin.x.value) + deltaX; const toY = -1 * (origin.y.value * e.scale - origin.y.value) + deltaY; translate.x.value = toX; translate.y.value = toY; scale.value = e.scale; }).onEnd(e => { runOnJS(switchGestureStatus)(false); onPinchEnd && runOnJS(onPinchEnd)(e); translate.x.value = withTiming(0, timingConfig); translate.y.value = withTiming(0, timingConfig); scale.value = withTiming(1, timingConfig, _ => { runOnJS(switchGestureStatus)(true); onGestureEnd && runOnJS(onGestureEnd)(); }); }); const tap = Gesture.Tap().enabled(gesturesEnabled && internalGesturesEnabled).maxDuration(250).numberOfTaps(1).runOnJS(true).onEnd(e => onTap === null || onTap === void 0 ? void 0 : onTap(e)); const doubleTap = Gesture.Tap().enabled(gesturesEnabled && internalGesturesEnabled).numberOfTaps(2).maxDuration(250).runOnJS(true).onEnd(e => onDoubleTap === null || onDoubleTap === void 0 ? void 0 : onDoubleTap(e)); const containerStyle = useAnimatedStyle(() => { const width = containerSize.width.value; const height = containerSize.height.value; return { width: width === 0 ? undefined : width, height: height === 0 ? undefined : height }; }, [containerSize]); const childrenStyle = useAnimatedStyle(() => { const { width, height, deltaX, deltaY } = childrenSize.value; return { width: width === 0 ? undefined : width, height: height === 0 ? undefined : height, transform: [{ translateX: translate.x.value - deltaX }, { translateY: translate.y.value - deltaY }, { scale: scale.value }] }; }, [resizeConfig, containerSize, childrenSize, translate, scale]); const composedTapGesture = Gesture.Exclusive(doubleTap, tap); return /*#__PURE__*/React.createElement(Animated.View, { style: [containerStyle, styles.center] }, /*#__PURE__*/React.createElement(Animated.View, { ref: containerRef, style: childrenStyle }, children), /*#__PURE__*/React.createElement(GestureDetector, { gesture: Gesture.Race(pinch, composedTapGesture) }, /*#__PURE__*/React.createElement(Animated.View, { collapsable: false, pointerEvents: gesturesEnabled ? undefined : 'none', style: styles.absolute }))); }; const styles = StyleSheet.create({ center: { justifyContent: 'center', alignItems: 'center' }, absolute: { height: '100%', width: '100%', position: 'absolute' } }); export default withSnapbackValidation(SnapbackZoom); //# sourceMappingURL=SnapbackZoom.js.map