/* * Copyright (c) 2013-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * * Facebook reserves all rights not expressly granted. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * @flow * @providesModule ImageEditingExample */ 'use strict'; var React = require('react'); var ReactNative = require('react-native'); var { CameraRoll, Image, ImageEditor, Platform, ScrollView, StyleSheet, Text, TouchableHighlight, View, } = ReactNative; var PAGE_SIZE = 20; type ImageOffset = { x: number; y: number; }; type ImageSize = { width: number; height: number; }; type ImageCropData = { offset: ImageOffset; size: ImageSize; displaySize?: ?ImageSize; resizeMode?: ?any; }; class SquareImageCropper extends React.Component { state: any; _isMounted: boolean; _transformData: ImageCropData; constructor(props) { super(props); this._isMounted = true; this.state = { randomPhoto: null, measuredSize: null, croppedImageURI: null, cropError: null, }; this._fetchRandomPhoto(); } async _fetchRandomPhoto() { try { const data = await CameraRoll.getPhotos({first: PAGE_SIZE}); if (!this._isMounted) { return; } var edges = data.edges; var edge = edges[Math.floor(Math.random() * edges.length)]; var randomPhoto = edge && edge.node && edge.node.image; if (randomPhoto) { this.setState({randomPhoto}); } } catch (error) { console.warn("Can't get a photo from camera roll", error); } } componentWillUnmount() { this._isMounted = false; } render() { if (!this.state.measuredSize) { return ( { var measuredWidth = event.nativeEvent.layout.width; if (!measuredWidth) { return; } this.setState({ measuredSize: {width: measuredWidth, height: measuredWidth}, }); }} /> ); } if (!this.state.croppedImageURI) { return this._renderImageCropper(); } return this._renderCroppedImage(); } _renderImageCropper() { if (!this.state.randomPhoto) { return ( ); } var error = null; if (this.state.cropError) { error = ( {this.state.cropError.message} ); } return ( Drag the image within the square to crop: this._transformData = data} /> Crop {error} ); } _renderCroppedImage() { return ( Here is the cropped image: Try again ); } _crop() { ImageEditor.cropImage( this.state.randomPhoto.uri, this._transformData, (croppedImageURI) => this.setState({croppedImageURI}), (cropError) => this.setState({cropError}) ); } _reset() { this.setState({ randomPhoto: null, croppedImageURI: null, cropError: null, }); this._fetchRandomPhoto(); } } class ImageCropper extends React.Component { _contentOffset: ImageOffset; _maximumZoomScale: number; _minimumZoomScale: number; _scaledImageSize: ImageSize; _horizontal: boolean; componentWillMount() { // Scale an image to the minimum size that is large enough to completely // fill the crop box. var widthRatio = this.props.image.width / this.props.size.width; var heightRatio = this.props.image.height / this.props.size.height; this._horizontal = widthRatio > heightRatio; if (this._horizontal) { this._scaledImageSize = { width: this.props.image.width / heightRatio, height: this.props.size.height, }; } else { this._scaledImageSize = { width: this.props.size.width, height: this.props.image.height / widthRatio, }; if (Platform.OS === 'android') { // hack to work around Android ScrollView a) not supporting zoom, and // b) not supporting vertical scrolling when nested inside another // vertical ScrollView (which it is, when displayed inside UIExplorer) this._scaledImageSize.width *= 2; this._scaledImageSize.height *= 2; this._horizontal = true; } } this._contentOffset = { x: (this._scaledImageSize.width - this.props.size.width) / 2, y: (this._scaledImageSize.height - this.props.size.height) / 2, }; this._maximumZoomScale = Math.min( this.props.image.width / this._scaledImageSize.width, this.props.image.height / this._scaledImageSize.height ); this._minimumZoomScale = Math.max( this.props.size.width / this._scaledImageSize.width, this.props.size.height / this._scaledImageSize.height ); this._updateTransformData( this._contentOffset, this._scaledImageSize, this.props.size ); } _onScroll(event) { this._updateTransformData( event.nativeEvent.contentOffset, event.nativeEvent.contentSize, event.nativeEvent.layoutMeasurement ); } _updateTransformData(offset, scaledImageSize, croppedImageSize) { var offsetRatioX = offset.x / scaledImageSize.width; var offsetRatioY = offset.y / scaledImageSize.height; var sizeRatioX = croppedImageSize.width / scaledImageSize.width; var sizeRatioY = croppedImageSize.height / scaledImageSize.height; var cropData: ImageCropData = { offset: { x: this.props.image.width * offsetRatioX, y: this.props.image.height * offsetRatioY, }, size: { width: this.props.image.width * sizeRatioX, height: this.props.image.height * sizeRatioY, }, }; this.props.onTransformDataChange && this.props.onTransformDataChange(cropData); } render() { return ( ); } } exports.framework = 'React'; exports.title = 'ImageEditor'; exports.description = 'Cropping and scaling with ImageEditor'; exports.examples = [{ title: 'Image Cropping', render() { return ; } }]; var styles = StyleSheet.create({ container: { flex: 1, alignSelf: 'stretch', }, imageCropper: { alignSelf: 'center', marginTop: 12, }, cropButtonTouchable: { alignSelf: 'center', marginTop: 12, }, cropButton: { padding: 12, backgroundColor: 'blue', borderRadius: 4, }, cropButtonLabel: { color: 'white', fontSize: 16, fontWeight: '500', }, });