import React, { Component } from 'react'; import { Animated, PanResponder, Platform, PanResponderInstance, PanResponderGestureState } from 'react-native'; import ImageResizer from '@bam.tech/react-native-image-resizer'; import ImageEditor from '@react-native-community/image-editor'; import { Q } from '../constants'; import Cropper from './Cropper'; import { getCropperLimits } from '../utils'; type CropperPageProps = { footerComponent: JSX.Element; onDone: (croppedImageUri: string, garbageUris: string[]) => void; onError: (err: Error) => void; onCancel: () => void; imageUri: string; imageWidth: number; imageHeight: number; TOP_VALUE: number; LEFT_VALUE: number; BOTTOM_VALUE: number; RIGHT_VALUE: number; initialRotation: number; NOT_SELECTED_AREA_OPACITY: number; BORDER_WIDTH: number; COMPONENT_WIDTH: number; COMPONENT_HEIGHT: number; }; interface ExtendedAnimatedValue extends Animated.Value { _value: number; _offset: number; } interface ExtendedAnimatedValueXY extends Animated.AnimatedValueXY { x: ExtendedAnimatedValue; y: ExtendedAnimatedValue; } type Position = 'topPosition' | 'leftPosition' | 'bottomPosition' | 'rightPosition'; interface State { topOuterPosition: ExtendedAnimatedValueXY; topOuterPanResponder: PanResponderInstance; leftOuterPosition: ExtendedAnimatedValueXY; leftOuterPanResponder: PanResponderInstance; bottomOuterPosition: ExtendedAnimatedValueXY; bottomOuterPanResponder: PanResponderInstance; rightOuterPosition: ExtendedAnimatedValueXY; rightOuterPanResponder: PanResponderInstance; topPosition: ExtendedAnimatedValueXY; topPanResponder: PanResponderInstance; leftPosition: ExtendedAnimatedValueXY; leftPanResponder: PanResponderInstance; bottomPosition: ExtendedAnimatedValueXY; bottomPanResponder: PanResponderInstance; rightPosition: ExtendedAnimatedValueXY; rightPanResponder: PanResponderInstance; topLeftPosition: ExtendedAnimatedValueXY; topLeftPanResponder: PanResponderInstance; bottomLeftPosition: ExtendedAnimatedValueXY; bottomLeftPanResponder: PanResponderInstance; bottomRightPosition: ExtendedAnimatedValueXY; bottomRightPanResponder: PanResponderInstance; topRightPosition: ExtendedAnimatedValueXY; topRightPanResponder: PanResponderInstance; rectanglePosition: ExtendedAnimatedValueXY; rectanglePanResponder: PanResponderInstance; TOP_LIMIT: number; LEFT_LIMIT: number; BOTTOM_LIMIT: number; RIGHT_LIMIT: number; TOP_VALUE: number; LEFT_VALUE: number; BOTTOM_VALUE: number; RIGHT_VALUE: number; rotation: number; } class CropperPage extends Component { constructor(props: CropperPageProps) { super(props); const { imageWidth, imageHeight, BORDER_WIDTH, COMPONENT_WIDTH, COMPONENT_HEIGHT } = props; const W_INT = this.W - 2 * BORDER_WIDTH; const H_INT = this.H - 2 * BORDER_WIDTH; const { TOP_LIMIT, LEFT_LIMIT, BOTTOM_LIMIT, RIGHT_LIMIT, DIFF } = getCropperLimits( imageWidth, imageHeight, props.initialRotation, W_INT, H_INT, this.W, this.H, BORDER_WIDTH, Q, ); const TOP_VALUE = props.TOP_VALUE !== 0 ? props.TOP_VALUE : TOP_LIMIT; const LEFT_VALUE = props.LEFT_VALUE !== 0 ? props.LEFT_VALUE : LEFT_LIMIT; const BOTTOM_VALUE = props.BOTTOM_VALUE !== 0 ? props.BOTTOM_VALUE : BOTTOM_LIMIT; const RIGHT_VALUE = props.RIGHT_VALUE !== 0 ? props.RIGHT_VALUE : RIGHT_LIMIT; const topOuterPosition = new Animated.ValueXY({ x: LEFT_VALUE - BORDER_WIDTH, y: TOP_VALUE - BORDER_WIDTH }) as ExtendedAnimatedValueXY; const topOuterPanResponder = PanResponder.create({ onStartShouldSetPanResponder: () => false }); const leftOuterPosition = new Animated.ValueXY({ x: LEFT_VALUE - BORDER_WIDTH, y: TOP_VALUE - BORDER_WIDTH }) as ExtendedAnimatedValueXY; const leftOuterPanResponder = PanResponder.create({ onStartShouldSetPanResponder: () => false }); const bottomOuterPosition = new Animated.ValueXY({ x: LEFT_VALUE - BORDER_WIDTH, y: COMPONENT_HEIGHT - BOTTOM_VALUE }) as ExtendedAnimatedValueXY; const bottomOuterPanResponder = PanResponder.create({ onStartShouldSetPanResponder: () => false }); const rightOuterPosition = new Animated.ValueXY({ x: COMPONENT_WIDTH - RIGHT_VALUE, y: TOP_VALUE - BORDER_WIDTH }) as ExtendedAnimatedValueXY; const rightOuterPanResponder = PanResponder.create({ onStartShouldSetPanResponder: () => false }); const topPosition = new Animated.ValueXY({ x: LEFT_VALUE - BORDER_WIDTH, y: TOP_VALUE - BORDER_WIDTH }) as ExtendedAnimatedValueXY; const topPanResponder = this.initSidePanResponder('topPosition'); const leftPosition = new Animated.ValueXY({ x: LEFT_VALUE - BORDER_WIDTH, y: TOP_VALUE - BORDER_WIDTH }) as ExtendedAnimatedValueXY; const leftPanResponder = this.initSidePanResponder('leftPosition'); const bottomPosition = new Animated.ValueXY({ x: LEFT_VALUE - BORDER_WIDTH, y: COMPONENT_HEIGHT - BOTTOM_VALUE }) as ExtendedAnimatedValueXY; const bottomPanResponder = this.initSidePanResponder('bottomPosition'); const rightPosition = new Animated.ValueXY({ x: COMPONENT_WIDTH - RIGHT_VALUE, y: TOP_VALUE - BORDER_WIDTH - DIFF / 2, }) as ExtendedAnimatedValueXY; const rightPanResponder = this.initSidePanResponder('rightPosition'); const topLeftPosition = new Animated.ValueXY({ x: LEFT_VALUE - BORDER_WIDTH, y: TOP_VALUE - BORDER_WIDTH }) as ExtendedAnimatedValueXY; const topLeftPanResponder = this.initCornerPanResponder('topPosition', 'leftPosition'); const bottomLeftPosition = new Animated.ValueXY({ x: LEFT_VALUE - BORDER_WIDTH, y: COMPONENT_HEIGHT - BOTTOM_VALUE }) as ExtendedAnimatedValueXY; const bottomLeftPanResponder = this.initCornerPanResponder('bottomPosition', 'leftPosition'); const bottomRightPosition = new Animated.ValueXY({ x: COMPONENT_WIDTH - RIGHT_VALUE, y: COMPONENT_HEIGHT - BOTTOM_VALUE, }) as ExtendedAnimatedValueXY; const bottomRightPanResponder = this.initCornerPanResponder('bottomPosition', 'rightPosition'); const topRightPosition = new Animated.ValueXY({ x: COMPONENT_WIDTH - RIGHT_VALUE, y: TOP_VALUE - BORDER_WIDTH }) as ExtendedAnimatedValueXY; const topRightPanResponder = this.initCornerPanResponder('topPosition', 'rightPosition'); const rectanglePosition = new Animated.ValueXY({ x: LEFT_VALUE, y: TOP_VALUE }) as ExtendedAnimatedValueXY; const rectanglePanResponder = this.initRectanglePanResponder(); this.state = { topOuterPosition, topOuterPanResponder, leftOuterPosition, leftOuterPanResponder, bottomOuterPosition, bottomOuterPanResponder, rightOuterPosition, rightOuterPanResponder, topPosition, topPanResponder, leftPosition, leftPanResponder, bottomPosition, bottomPanResponder, rightPosition, rightPanResponder, topLeftPosition, topLeftPanResponder, bottomLeftPosition, bottomLeftPanResponder, bottomRightPosition, bottomRightPanResponder, topRightPosition, topRightPanResponder, rectanglePosition, rectanglePanResponder, TOP_LIMIT, LEFT_LIMIT, BOTTOM_LIMIT, RIGHT_LIMIT, TOP_VALUE, LEFT_VALUE, BOTTOM_VALUE, RIGHT_VALUE, rotation: props.initialRotation, }; } isRectangleMoving = false; topOuter = undefined; leftOuter = undefined; bottomOuter = undefined; rightOuter = undefined; W = this.props.COMPONENT_WIDTH; H = this.props.COMPONENT_HEIGHT - Q; onCancel = () => { this.props.onCancel(); }; getTopOuterStyle = () => { return { ...this.state.topOuterPosition.getLayout(), top: this.state.TOP_LIMIT, left: this.state.LEFT_LIMIT, height: Animated.add(this.props.BORDER_WIDTH - this.state.TOP_LIMIT, this.state.topPosition.y), width: this.W, backgroundColor: `rgba(0, 0, 0, ${this.props.NOT_SELECTED_AREA_OPACITY})`, }; }; getLeftOuterStyle = () => { return { ...this.state.leftOuterPosition.getLayout(), top: Animated.add(this.props.BORDER_WIDTH, this.state.topPosition.y), left: this.state.LEFT_LIMIT, height: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.bottomPosition.y, Animated.multiply(-1, this.state.topPosition.y))), width: Animated.add(this.props.BORDER_WIDTH - this.state.LEFT_LIMIT, this.state.leftPosition.x), backgroundColor: `rgba(0, 0, 0, ${this.props.NOT_SELECTED_AREA_OPACITY})`, }; }; getBottomOuterStyle = () => { return { ...this.state.bottomOuterPosition.getLayout(), top: this.state.bottomPosition.y, left: this.state.LEFT_LIMIT, height: Animated.add(this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT, Animated.multiply(-1, this.state.bottomPosition.y)), width: this.W, backgroundColor: `rgba(0, 0, 0, ${this.props.NOT_SELECTED_AREA_OPACITY})`, }; }; getRightOuterStyle = () => { return { ...this.state.rightOuterPosition.getLayout(), top: Animated.add(this.props.BORDER_WIDTH, this.state.topPosition.y), left: this.state.rightPosition.x, height: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.bottomPosition.y, Animated.multiply(-1, this.state.topPosition.y))), right: this.state.RIGHT_LIMIT, backgroundColor: `rgba(0, 0, 0, ${this.props.NOT_SELECTED_AREA_OPACITY})`, }; }; getTopLeftStyle = () => { return { ...this.state.topLeftPosition.getLayout(), top: this.state.topPosition.y, left: this.state.leftPosition.x, width: this.props.BORDER_WIDTH, paddingBottom: this.props.BORDER_WIDTH, }; }; getBottomLeftStyle = () => { return { ...this.state.bottomLeftPosition.getLayout(), top: this.state.bottomPosition.y, left: this.state.leftPosition.x, width: this.props.BORDER_WIDTH, paddingTop: this.props.BORDER_WIDTH, }; }; getBottomRightStyle = () => { return { ...this.state.bottomRightPosition.getLayout(), top: this.state.bottomPosition.y, left: this.state.rightPosition.x, height: this.props.BORDER_WIDTH, paddingLeft: this.props.BORDER_WIDTH, }; }; getTopRightStyle = () => { return { ...this.state.topRightPosition.getLayout(), top: this.state.topPosition.y, left: this.state.rightPosition.x, height: this.props.BORDER_WIDTH, paddingLeft: this.props.BORDER_WIDTH, }; }; getTopSideStyle = () => { return { ...this.state.topPosition.getLayout(), left: Animated.add(this.props.BORDER_WIDTH, this.state.leftPosition.x), width: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.rightPosition.x, Animated.multiply(-1, this.state.leftPosition.x))), paddingBottom: this.props.BORDER_WIDTH, }; }; getLeftSideStyle = () => { return { ...this.state.leftPosition.getLayout(), top: Animated.add(this.props.BORDER_WIDTH, this.state.topPosition.y), height: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.bottomPosition.y, Animated.multiply(-1, this.state.topPosition.y))), paddingLeft: this.props.BORDER_WIDTH, }; }; getBottomSideStyle = () => { return { ...this.state.bottomPosition.getLayout(), left: Animated.add(this.props.BORDER_WIDTH, this.state.leftPosition.x), width: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.rightPosition.x, Animated.multiply(-1, this.state.leftPosition.x))), paddingTop: this.props.BORDER_WIDTH, }; }; getRightSideStyle = () => { return { ...this.state.rightPosition.getLayout(), top: Animated.add(this.props.BORDER_WIDTH, this.state.topPosition.y), height: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.bottomPosition.y, Animated.multiply(-1, this.state.topPosition.y))), paddingLeft: this.props.BORDER_WIDTH, }; }; getRectangleStyle = () => { return { ...this.state.rectanglePosition.getLayout(), top: Animated.add(this.props.BORDER_WIDTH, this.state.topPosition.y), left: Animated.add(this.props.BORDER_WIDTH, this.state.leftPosition.x), width: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.rightPosition.x, Animated.multiply(-1, this.state.leftPosition.x))), height: Animated.add(-this.props.BORDER_WIDTH, Animated.add(this.state.bottomPosition.y, Animated.multiply(-1, this.state.topPosition.y))), zIndex: 3, }; }; getImageStyle = () => { const DIFF = this.state.topPosition.y._value - this.state.rightPosition.y._value; return { position: 'absolute', top: this.state.TOP_LIMIT - DIFF, left: this.state.LEFT_LIMIT + DIFF, bottom: this.state.BOTTOM_LIMIT - DIFF, right: this.state.RIGHT_LIMIT + DIFF, resizeMode: 'stretch', transform: [{ rotate: `${this.state.rotation.toString()}deg` }], }; }; isAllowedToMoveTopSide = (gesture: PanResponderGestureState) => { return ( this.state.topPosition.y._offset + gesture.dy >= this.state.TOP_LIMIT - this.props.BORDER_WIDTH && this.state.topPosition.y._offset + gesture.dy + this.props.BORDER_WIDTH + 1 < this.state.bottomPosition.y._offset ); }; isAllowedToMoveLeftSide = (gesture: PanResponderGestureState) => { return ( this.state.leftPosition.x._offset + gesture.dx >= this.state.LEFT_LIMIT - this.props.BORDER_WIDTH && this.state.leftPosition.x._offset + gesture.dx + this.props.BORDER_WIDTH + 1 < this.state.rightPosition.x._offset ); }; isAllowedToMoveBottomSide = (gesture: PanResponderGestureState) => { return ( this.state.bottomPosition.y._offset + gesture.dy <= this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT && this.state.topPosition.y._offset + this.props.BORDER_WIDTH + 1 < this.state.bottomPosition.y._offset + gesture.dy ); }; isAllowedToMoveRightSide = (gesture: PanResponderGestureState) => { return ( this.state.rightPosition.x._offset + gesture.dx <= this.props.COMPONENT_WIDTH - this.state.RIGHT_LIMIT && this.state.leftPosition.x._offset + this.props.BORDER_WIDTH + 1 < this.state.rightPosition.x._offset + gesture.dx ); }; isAllowedToMove = (position: Position, gesture: PanResponderGestureState) => { if (position === 'topPosition') { return this.isAllowedToMoveTopSide(gesture); } if (position === 'leftPosition') { return this.isAllowedToMoveLeftSide(gesture); } if (position === 'bottomPosition') { return this.isAllowedToMoveBottomSide(gesture); } if (position === 'rightPosition') { return this.isAllowedToMoveRightSide(gesture); } }; initSidePanResponder = (position: Position) => { return PanResponder.create({ onStartShouldSetPanResponder: () => !this.isRectangleMoving, onPanResponderMove: (_event, gesture) => { if (this.isAllowedToMove(position, gesture)) { this.state[position].setValue({ x: gesture.dx, y: gesture.dy }); } }, onPanResponderRelease: () => { // make to not reset position this.state.topPosition.flattenOffset(); this.state.leftPosition.flattenOffset(); this.state.bottomPosition.flattenOffset(); this.state.rightPosition.flattenOffset(); }, onPanResponderGrant: () => { this.state.topPosition.setOffset({ x: this.state.topPosition.x._value, y: this.state.topPosition.y._value, }); this.state.leftPosition.setOffset({ x: this.state.leftPosition.x._value, y: this.state.leftPosition.y._value, }); this.state.bottomPosition.setOffset({ x: this.state.bottomPosition.x._value, y: this.state.bottomPosition.y._value, }); this.state.rightPosition.setOffset({ x: this.state.rightPosition.x._value, y: this.state.rightPosition.y._value, }); this.state.topPosition.setValue({ x: 0, y: 0 }); this.state.leftPosition.setValue({ x: 0, y: 0 }); this.state.bottomPosition.setValue({ x: 0, y: 0 }); this.state.rightPosition.setValue({ x: 0, y: 0 }); }, }); }; initRectanglePanResponder = () => { return PanResponder.create({ onStartShouldSetPanResponder: () => !this.isRectangleMoving, onPanResponderMove: (_event, gesture) => { this.state.topPosition.setValue({ x: gesture.dx, y: gesture.dy }); this.state.leftPosition.setValue({ x: gesture.dx, y: gesture.dy }); this.state.bottomPosition.setValue({ x: gesture.dx, y: gesture.dy }); this.state.rightPosition.setValue({ x: gesture.dx, y: gesture.dy }); }, onPanResponderRelease: () => { this.isRectangleMoving = true; // make to not reset position this.state.topPosition.flattenOffset(); this.state.leftPosition.flattenOffset(); this.state.bottomPosition.flattenOffset(); this.state.rightPosition.flattenOffset(); const width = this.state.rightPosition.x._value - this.state.leftPosition.x._value - this.props.BORDER_WIDTH; const height = this.state.bottomPosition.y._value - this.state.topPosition.y._value - this.props.BORDER_WIDTH; let isOutside = false; if (this.state.leftPosition.x._value < this.state.LEFT_LIMIT - this.props.BORDER_WIDTH) { isOutside = true; Animated.parallel([ Animated.spring(this.state.leftPosition.x, { toValue: this.state.LEFT_LIMIT - this.props.BORDER_WIDTH, useNativeDriver: false }), Animated.spring(this.state.rightPosition.x, { toValue: this.state.LEFT_LIMIT + width, useNativeDriver: false }), ]).start(() => { this.isRectangleMoving = false; }); } if (this.state.topPosition.y._value < this.state.TOP_LIMIT - this.props.BORDER_WIDTH) { isOutside = true; Animated.parallel([ Animated.spring(this.state.topPosition.y, { toValue: this.state.TOP_LIMIT - this.props.BORDER_WIDTH, useNativeDriver: false }), Animated.spring(this.state.bottomPosition.y, { toValue: this.state.TOP_LIMIT + height, useNativeDriver: false }), ]).start(() => { this.isRectangleMoving = false; }); } if (width + this.state.leftPosition.x._value + this.props.BORDER_WIDTH > this.props.COMPONENT_WIDTH - this.state.RIGHT_LIMIT) { isOutside = true; Animated.parallel([ Animated.spring(this.state.leftPosition.x, { toValue: this.props.COMPONENT_WIDTH - this.state.RIGHT_LIMIT - width - this.props.BORDER_WIDTH, useNativeDriver: false, }), Animated.spring(this.state.rightPosition.x, { toValue: this.props.COMPONENT_WIDTH - this.state.RIGHT_LIMIT, useNativeDriver: false }), ]).start(() => { this.isRectangleMoving = false; }); } if (height + this.state.topPosition.y._value + this.props.BORDER_WIDTH > this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT) { isOutside = true; Animated.parallel([ Animated.spring(this.state.topPosition.y, { toValue: this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT - height - this.props.BORDER_WIDTH, useNativeDriver: false, }), Animated.spring(this.state.bottomPosition.y, { toValue: this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT, useNativeDriver: false }), ]).start(() => { this.isRectangleMoving = false; }); } if (!isOutside) { this.isRectangleMoving = false; } }, onPanResponderGrant: () => { this.state.topPosition.setOffset({ x: this.state.topPosition.x._value, y: this.state.topPosition.y._value, }); this.state.leftPosition.setOffset({ x: this.state.leftPosition.x._value, y: this.state.leftPosition.y._value, }); this.state.bottomPosition.setOffset({ x: this.state.bottomPosition.x._value, y: this.state.bottomPosition.y._value, }); this.state.rightPosition.setOffset({ x: this.state.rightPosition.x._value, y: this.state.rightPosition.y._value, }); this.state.topPosition.setValue({ x: 0, y: 0 }); this.state.leftPosition.setValue({ x: 0, y: 0 }); this.state.bottomPosition.setValue({ x: 0, y: 0 }); this.state.rightPosition.setValue({ x: 0, y: 0 }); }, }); }; initCornerPanResponder = (pos1: Position, pos2: Position) => { return PanResponder.create({ onStartShouldSetPanResponder: () => !this.isRectangleMoving, onPanResponderMove: (_event, gesture) => { if (this.isAllowedToMove(pos1, gesture)) { this.state[pos1].setValue({ x: gesture.dx, y: gesture.dy }); } if (this.isAllowedToMove(pos2, gesture)) { this.state[pos2].setValue({ x: gesture.dx, y: gesture.dy }); } }, onPanResponderRelease: () => { this.state.topPosition.flattenOffset(); this.state.leftPosition.flattenOffset(); this.state.bottomPosition.flattenOffset(); this.state.rightPosition.flattenOffset(); }, onPanResponderGrant: () => { this.state.topPosition.setOffset({ x: this.state.topPosition.x._value, y: this.state.topPosition.y._value }); this.state.leftPosition.setOffset({ x: this.state.leftPosition.x._value, y: this.state.leftPosition.y._value }); this.state.bottomPosition.setOffset({ x: this.state.bottomPosition.x._value, y: this.state.bottomPosition.y._value }); this.state.rightPosition.setOffset({ x: this.state.rightPosition.x._value, y: this.state.rightPosition.y._value }); this.state.topPosition.setValue({ x: 0, y: 0 }); this.state.leftPosition.setValue({ x: 0, y: 0 }); this.state.bottomPosition.setValue({ x: 0, y: 0 }); this.state.rightPosition.setValue({ x: 0, y: 0 }); }, }); }; setCropBoxLimits = ({ TOP_LIMIT, LEFT_LIMIT, BOTTOM_LIMIT, RIGHT_LIMIT, }: { TOP_LIMIT: number; LEFT_LIMIT: number; BOTTOM_LIMIT: number; RIGHT_LIMIT: number; }) => { this.setState({ TOP_LIMIT, LEFT_LIMIT, BOTTOM_LIMIT, RIGHT_LIMIT, }); }; setCropBoxValues = ({ TOP_VALUE, LEFT_VALUE, BOTTOM_VALUE, RIGHT_VALUE, }: { TOP_VALUE: number; LEFT_VALUE: number; BOTTOM_VALUE: number; RIGHT_VALUE: number; }) => { this.setState({ TOP_VALUE, LEFT_VALUE, BOTTOM_VALUE, RIGHT_VALUE, }); }; setCropBoxRotation = (rotation: number) => { this.setState({ rotation }); }; rotate90 = () => { this.setCropBoxRotation((360 + (this.state.rotation - 90)) % 360); }; onRotate = () => { const W_INT = this.W - 2 * this.props.BORDER_WIDTH; const H_INT = this.H - 2 * this.props.BORDER_WIDTH; let imageWidth = 0; let imageHeight = 0; let rotation = 0; if (this.state.rotation % 180 === 90) { imageWidth = this.props.imageWidth > 0 ? this.props.imageWidth : 1280; // 340 imageHeight = this.props.imageHeight > 0 ? this.props.imageHeight : 747; // 500 rotation = 0; } else { imageWidth = this.props.COMPONENT_WIDTH - this.state.LEFT_LIMIT - this.state.RIGHT_LIMIT; imageHeight = this.props.COMPONENT_HEIGHT - this.state.TOP_LIMIT - this.state.BOTTOM_LIMIT; rotation = 90; } const { TOP_LIMIT, LEFT_LIMIT, BOTTOM_LIMIT, RIGHT_LIMIT, DIFF } = getCropperLimits( imageWidth, imageHeight, rotation, W_INT, H_INT, this.W, this.H, this.props.BORDER_WIDTH, Q, ); this.rotate90(); this.setCropBoxLimits({ TOP_LIMIT, LEFT_LIMIT, BOTTOM_LIMIT, RIGHT_LIMIT }); const startPositionBeforeRotationX = this.state.leftPosition.x._value - this.state.LEFT_LIMIT + this.props.BORDER_WIDTH; const startPositionBeforeRotationY = this.state.topPosition.y._value - this.state.TOP_LIMIT + this.props.BORDER_WIDTH; const imageWidthBeforeRotation = this.props.COMPONENT_WIDTH - this.state.RIGHT_LIMIT - this.state.LEFT_LIMIT; const imageHeightBeforeRotation = this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT - this.state.TOP_LIMIT; const rectangleWidthBeforeRotation = this.state.rightPosition.x._value - this.state.leftPosition.x._value - this.props.BORDER_WIDTH; const rectangleHeightBeforeRotation = this.state.bottomPosition.y._value - this.state.topPosition.y._value - this.props.BORDER_WIDTH; const imageWidthAfterRotation = this.props.COMPONENT_WIDTH - RIGHT_LIMIT - LEFT_LIMIT; const imageHeightAfterRotation = this.props.COMPONENT_HEIGHT - BOTTOM_LIMIT - TOP_LIMIT; const rectangleWidthAfterRotation = (imageWidthAfterRotation * rectangleHeightBeforeRotation) / imageHeightBeforeRotation; const rectangleHeightAfterRotation = (imageHeightAfterRotation * rectangleWidthBeforeRotation) / imageWidthBeforeRotation; const startPositionAfterRotationX = (startPositionBeforeRotationY * imageWidthAfterRotation) / imageHeightBeforeRotation; const startPositionAfterRotationY = ((imageWidthBeforeRotation - startPositionBeforeRotationX - rectangleWidthBeforeRotation) * imageHeightAfterRotation) / imageWidthBeforeRotation; this.state.topPosition.setValue({ x: LEFT_LIMIT + startPositionAfterRotationX - this.props.BORDER_WIDTH, y: TOP_LIMIT + startPositionAfterRotationY - this.props.BORDER_WIDTH, }); this.state.leftPosition.setValue({ x: LEFT_LIMIT + startPositionAfterRotationX - this.props.BORDER_WIDTH, y: TOP_LIMIT + startPositionAfterRotationY - this.props.BORDER_WIDTH, }); this.state.bottomPosition.setValue({ x: LEFT_LIMIT + startPositionAfterRotationX - this.props.BORDER_WIDTH, y: TOP_LIMIT + startPositionAfterRotationY + rectangleHeightAfterRotation, }); this.state.rightPosition.setValue({ x: LEFT_LIMIT + startPositionAfterRotationX + rectangleWidthAfterRotation, y: TOP_LIMIT + startPositionAfterRotationY - this.props.BORDER_WIDTH - DIFF / 2, }); // @ts-ignore this.topOuter.setNativeProps({ style: { top: TOP_LIMIT, height: 0 } }); // @ts-ignore this.leftOuter.setNativeProps({ style: { left: LEFT_LIMIT, width: 0 } }); // @ts-ignore this.bottomOuter.setNativeProps({ style: { top: BOTTOM_LIMIT, height: 0 } }); // @ts-ignore this.rightOuter.setNativeProps({ style: { top: TOP_LIMIT, height: 0 } }); }; onDone = () => { if (this.isRectangleMoving) { return null; } const IMAGE_W = this.props.COMPONENT_WIDTH - this.state.RIGHT_LIMIT - this.state.LEFT_LIMIT; const IMAGE_H = this.props.COMPONENT_HEIGHT - this.state.BOTTOM_LIMIT - this.state.TOP_LIMIT; let x = this.state.leftPosition.x._value - this.state.LEFT_LIMIT + this.props.BORDER_WIDTH; let y = this.state.topPosition.y._value - this.state.TOP_LIMIT + this.props.BORDER_WIDTH; let width = this.state.rightPosition.x._value - this.state.leftPosition.x._value - this.props.BORDER_WIDTH; let height = this.state.bottomPosition.y._value - this.state.topPosition.y._value - this.props.BORDER_WIDTH; let imageWidth = this.props.imageWidth > 0 ? this.props.imageWidth : 1280; // 340 let imageHeight = this.props.imageHeight > 0 ? this.props.imageHeight : 747; // 500 if (this.state.rotation % 180 === 90) { const pivot = imageWidth; imageWidth = imageHeight; imageHeight = pivot; } width = (width * imageWidth) / IMAGE_W; height = (height * imageHeight) / IMAGE_H; x = (x * imageWidth) / IMAGE_W; y = (y * imageHeight) / IMAGE_H; const cropData = { offset: { x, y }, size: { width, height }, resizeMode: 'stretch' as 'stretch', }; const garbageUris: string[] = []; ImageResizer.createResizedImage( this.props.imageUri, this.state.rotation % 180 === 0 ? imageWidth : imageHeight, this.state.rotation % 180 === 0 ? imageHeight : imageWidth, 'JPEG', 100, this.state.rotation, undefined, false, { mode: 'cover', onlyScaleDown: true, }, ) .then((res) => { garbageUris.push(res.uri); return ImageEditor.cropImage(res.uri, cropData); }) .then((cropResult) => { this.props.onDone(cropResult.uri, garbageUris); }) .catch((err: Error) => { this.props.onError(err); }); }; render() { return ( (this.topOuter = ref)} leftOuterRef={(ref) => (this.leftOuter = ref)} bottomOuterRef={(ref) => (this.bottomOuter = ref)} rightOuterRef={(ref) => (this.rightOuter = ref)} COMPONENT_WIDTH={this.props.COMPONENT_WIDTH} COMPONENT_HEIGHT={this.props.COMPONENT_HEIGHT} W={this.W} H={this.H} /> ); } } export default CropperPage;