UNPKG

react-native-zoom-toolkit

Version:

Smoothly zoom any image, video or component you want!

356 lines (352 loc) 14.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _reactNative = require("react-native"); var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated")); var _reactNativeGestureHandler = require("react-native-gesture-handler"); var _useSizeVector = require("../../commons/hooks/useSizeVector"); var _getCropRotatedSize = require("../../commons/utils/getCropRotatedSize"); var _usePanCommons = require("../../commons/hooks/usePanCommons"); var _usePinchCommons = require("../../commons/hooks/usePinchCommons"); var _getMaxScale = require("../../commons/utils/getMaxScale"); var _useVector = require("../../commons/hooks/useVector"); var _types = require("../../commons/types"); var _crop = require("../../commons/utils/crop"); var _types2 = require("./types"); var _withCropValidation = _interopRequireDefault(require("../../commons/hoc/withCropValidation")); var _getPinchPanningStatus = require("../../commons/utils/getPinchPanningStatus"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } const detectorColor = 'rgba(50, 168, 82, 0.5)'; const containerColor = 'rgba(238, 66, 102, 0.5)'; const RAD2DEG = 180 / Math.PI; const CropZoom = props => { const ref = props.reference; const { children, cropSize, resolution, debug = false, minScale = 1, maxScale: userMaxScale, scaleMode = _types.ScaleMode.BOUNCE, panMode = _types.PanMode.FREE, allowPinchPanning: pinchPanning, mode = _types2.CropMode.MANAGED, onGestureActive, onGestureEnd, OverlayComponent, onPanStart: onUserPanStart, onPanEnd: onUserPanEnd, onPinchStart: onUserPinchStart, onPinchEnd: onUserPinchEnd, onTap } = props; const allowPinchPanning = pinchPanning ?? (0, _getPinchPanningStatus.getPinchPanningStatus)(); const translate = (0, _useVector.useVector)(0, 0); const offset = (0, _useVector.useVector)(0, 0); const origin = (0, _useVector.useVector)(0, 0); const rotation = (0, _reactNativeReanimated.useSharedValue)(0); const rotate = (0, _useVector.useVector)(0, 0); const delta = (0, _useVector.useVector)(0, 0); const scale = (0, _reactNativeReanimated.useSharedValue)(minScale); const scaleOffset = (0, _reactNativeReanimated.useSharedValue)(minScale); const container = (0, _useSizeVector.useSizeVector)(1, 1); const detector = (0, _useSizeVector.useSizeVector)(1, 1); const sizeAngle = (0, _reactNativeReanimated.useSharedValue)(0); const detectorTranslate = (0, _useVector.useVector)(0, 0); const detectorScale = (0, _reactNativeReanimated.useSharedValue)(1); const maxScale = (0, _reactNativeReanimated.useDerivedValue)(() => { const { width, height } = container; const scaleValue = (0, _getMaxScale.getMaxScale)({ width: width.value, height: height.value }, resolution); return userMaxScale ?? scaleValue; }, [container, userMaxScale, resolution]); (0, _reactNativeReanimated.useDerivedValue)(() => { const size = (0, _getCropRotatedSize.getCropRotatedSize)({ crop: cropSize, resolution: resolution, angle: sizeAngle.value }); const isFlipped = rotation.value % Math.PI !== 0; const render1 = container.width.value === 1 && container.height.value === 1; container.width.value = render1 ? size.width : (0, _reactNativeReanimated.withTiming)(size.width); container.height.value = render1 ? size.height : (0, _reactNativeReanimated.withTiming)(size.height); detector.width.value = isFlipped ? size.height : size.width; detector.height.value = isFlipped ? size.width : size.height; }, [cropSize, resolution, sizeAngle, rotation, container, detector]); (0, _reactNativeReanimated.useDerivedValue)(() => { onGestureActive === null || onGestureActive === void 0 || onGestureActive({ width: container.width.value, height: container.height.value, translateX: translate.x.value, translateY: translate.y.value, scale: scale.value, rotate: rotation.value, rotateX: rotate.x.value, rotateY: rotate.y.value }); }, [container, translate, scale, rotate, rotation]); const boundsFn = scaleValue => { 'worklet'; let size = { width: container.width.value, height: container.height.value }; const isInInverseAspectRatio = rotation.value % Math.PI !== 0; if (isInInverseAspectRatio) { size = { width: size.height, height: size.width }; } const boundX = Math.max(0, size.width * scaleValue - cropSize.width) / 2; const boundY = Math.max(0, size.height * scaleValue - cropSize.height) / 2; return { x: boundX, y: boundY }; }; const { gesturesEnabled, onPinchStart, onPinchUpdate, onPinchEnd } = (0, _usePinchCommons.usePinchCommons)({ container: detector, detectorTranslate, detectorScale, translate, offset, origin, scale, scaleOffset, minScale, maxScale, delta, allowPinchPanning, scaleMode, pinchCenteringMode: _types.PinchCenteringMode.INTERACTION, boundFn: boundsFn, userCallbacks: { onGestureEnd: onGestureEnd, onPinchStart: onUserPinchStart, onPinchEnd: onUserPinchEnd } }); const { onPanStart, onPanChange, onPanEnd } = (0, _usePanCommons.usePanCommons)({ container: detector, translate, offset, scale, detectorTranslate, panMode, boundFn: boundsFn, userCallbacks: { onGestureEnd: onGestureEnd, onPanStart: onUserPanStart, onPanEnd: onUserPanEnd } }); const pinch = _reactNativeGestureHandler.Gesture.Pinch().onStart(onPinchStart).onUpdate(onPinchUpdate).onEnd(onPinchEnd); const pan = _reactNativeGestureHandler.Gesture.Pan().enabled(gesturesEnabled).maxPointers(1).onStart(onPanStart).onChange(onPanChange).onEnd(onPanEnd); const tap = _reactNativeGestureHandler.Gesture.Tap().enabled(gesturesEnabled).maxDuration(250).numberOfTaps(1).runOnJS(true).onEnd(e => onTap === null || onTap === void 0 ? void 0 : onTap(e)); const detectorStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => { return { width: detector.width.value, height: detector.height.value, position: 'absolute', backgroundColor: debug ? detectorColor : undefined, transform: [{ translateX: detectorTranslate.x.value }, { translateY: detectorTranslate.y.value }, { scale: detectorScale.value }] }; }, [detector, debug, detectorTranslate, detectorScale]); const containerStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => { return { width: container.width.value, height: container.height.value, transform: [{ translateX: translate.x.value }, { translateY: translate.y.value }, { scale: scale.value }, { rotate: `${rotation.value}rad` }, { rotateX: `${rotate.x.value}rad` }, { rotateY: `${rotate.y.value}rad` }] }; }, [container, translate, scale, rotation, rotate]); // Reference handling section const canRotate = (0, _reactNativeReanimated.useSharedValue)(true); const handleRotate = (animate = true, clockwise = true, cb) => { if (!canRotate.value) return; if (animate) canRotate.value = false; // Determine the direction multiplier based on clockwise or counterclockwise rotation const direction = clockwise ? 1 : -1; const toAngle = rotation.value + Math.PI / 2 * direction; sizeAngle.value = toAngle; cb === null || cb === void 0 || cb(toAngle % (Math.PI * 2)); translate.x.value = animate ? (0, _reactNativeReanimated.withTiming)(0) : 0; translate.y.value = animate ? (0, _reactNativeReanimated.withTiming)(0) : 0; detectorTranslate.x.value = animate ? (0, _reactNativeReanimated.withTiming)(0) : 0; detectorTranslate.y.value = animate ? (0, _reactNativeReanimated.withTiming)(0) : 0; scale.value = animate ? (0, _reactNativeReanimated.withTiming)(1) : 1; detectorScale.value = animate ? (0, _reactNativeReanimated.withTiming)(1) : 1; rotation.value = animate ? (0, _reactNativeReanimated.withTiming)(toAngle, undefined, _ => { canRotate.value = true; if (rotation.value === Math.PI * 2) rotation.value = 0; }) : toAngle; }; const flipHorizontal = (animate = true, cb) => { const toAngle = rotate.y.value !== Math.PI ? Math.PI : 0; cb === null || cb === void 0 || cb(toAngle * RAD2DEG); rotate.y.value = animate ? (0, _reactNativeReanimated.withTiming)(toAngle) : toAngle; }; const flipVertical = (animate = true, cb) => { const toAngle = rotate.x.value !== Math.PI ? Math.PI : 0; cb === null || cb === void 0 || cb(toAngle * RAD2DEG); rotate.x.value = animate ? (0, _reactNativeReanimated.withTiming)(toAngle) : toAngle; }; const handleReset = (animate = true) => { translate.x.value = animate ? (0, _reactNativeReanimated.withTiming)(0) : 0; translate.y.value = animate ? (0, _reactNativeReanimated.withTiming)(0) : 0; rotation.value = animate ? (0, _reactNativeReanimated.withTiming)(0) : 0; rotate.x.value = animate ? (0, _reactNativeReanimated.withTiming)(0) : 0; rotate.y.value = animate ? (0, _reactNativeReanimated.withTiming)(0) : 0; scale.value = animate ? (0, _reactNativeReanimated.withTiming)(minScale) : minScale; }; const handleCrop = fixedWidth => { return (0, _crop.crop)({ cropSize: cropSize, resolution: resolution, canvas: { width: container.width.value, height: container.height.value }, position: { x: translate.x.value, y: translate.y.value }, scale: scale.value, context: { rotationAngle: rotation.value * RAD2DEG, flipHorizontal: rotate.y.value === Math.PI, flipVertical: rotate.x.value === Math.PI }, fixedWidth }); }; const handleRequestState = () => ({ width: container.width.value, height: container.height.value, translateX: translate.x.value, translateY: translate.y.value, scale: scale.value, rotate: rotation.value, rotateX: rotate.x.value, rotateY: rotate.y.value }); const handleAssignState = (state, animate = true) => { const toScale = (0, _reactNativeReanimated.clamp)(state.scale, minScale, maxScale.value); const { x: boundX, y: boundY } = boundsFn(toScale); const toX = (0, _reactNativeReanimated.clamp)(state.translateX, -1 * boundX, boundX); const toY = (0, _reactNativeReanimated.clamp)(state.translateY, -1 * boundY, boundY); const DEG90 = Math.PI / 2; const toRotate = Math.floor(state.rotate % (Math.PI * 2) / DEG90) * DEG90; const toRotateX = Math.sign(state.rotateX - DEG90) === 1 ? Math.PI : 0; const toRotateY = Math.sign(state.rotateY - DEG90) === 1 ? Math.PI : 0; translate.x.value = animate ? (0, _reactNativeReanimated.withTiming)(toX) : toX; translate.y.value = animate ? (0, _reactNativeReanimated.withTiming)(toY) : toY; scale.value = animate ? (0, _reactNativeReanimated.withTiming)(toScale) : toScale; rotation.value = animate ? (0, _reactNativeReanimated.withTiming)(toRotate) : toRotate; rotate.x.value = animate ? (0, _reactNativeReanimated.withTiming)(toRotateX) : toRotateX; rotate.y.value = animate ? (0, _reactNativeReanimated.withTiming)(toRotateY) : toRotateY; }; (0, _react.useImperativeHandle)(ref, () => ({ rotate: (animate, cb) => handleRotate(animate, true, cb), rotateWithDirection: handleRotate, flipHorizontal: flipHorizontal, flipVertical: flipVertical, reset: handleReset, crop: handleCrop, requestState: handleRequestState, assignState: handleAssignState })); const root = { minWidth: cropSize.width, minHeight: cropSize.height }; const cropStyle = { width: cropSize.width, height: cropSize.height }; const reflectionSyle = { backgroundColor: debug ? containerColor : undefined }; if (mode === _types2.CropMode.MANAGED) { return /*#__PURE__*/_react.default.createElement(_reactNativeGestureHandler.GestureHandlerRootView, { style: [root, styles.root, styles.center] }, /*#__PURE__*/_react.default.createElement(_reactNative.View, { style: [cropStyle, styles.center] }, /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, { style: containerStyle }, children), /*#__PURE__*/_react.default.createElement(_reactNative.View, { style: [reflectionSyle, _reactNative.StyleSheet.absoluteFill] })), /*#__PURE__*/_react.default.createElement(_reactNative.View, { style: [styles.absolute, styles.center] }, OverlayComponent === null || OverlayComponent === void 0 ? void 0 : OverlayComponent()), /*#__PURE__*/_react.default.createElement(_reactNativeGestureHandler.GestureDetector, { gesture: _reactNativeGestureHandler.Gesture.Race(pinch, pan, tap) }, /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, { style: detectorStyle }))); } return /*#__PURE__*/_react.default.createElement(_reactNativeGestureHandler.GestureHandlerRootView, { style: [cropStyle, styles.center] }, /*#__PURE__*/_react.default.createElement(_reactNative.View, { style: [reflectionSyle, _reactNative.StyleSheet.absoluteFill] }), /*#__PURE__*/_react.default.createElement(_reactNativeGestureHandler.GestureDetector, { gesture: _reactNativeGestureHandler.Gesture.Race(pinch, pan) }, /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, { style: detectorStyle }))); }; const styles = _reactNative.StyleSheet.create({ root: { flex: 1 }, absolute: { flex: 1, position: 'absolute' }, center: { justifyContent: 'center', alignItems: 'center' } }); var _default = exports.default = (0, _withCropValidation.default)(CropZoom); //# sourceMappingURL=CropZoom.js.map