1import React from 'react'; 2import { 3 ScrollView, 4 StyleSheet, 5 TouchableOpacity, 6 Text, 7 View, 8 Image, 9 TouchableOpacityProps, 10} from 'react-native'; 11import * as ImagePicker from 'expo-image-picker'; 12import * as Permissions from 'expo-permissions'; 13import * as ImageManipulator from 'expo-image-manipulator'; 14import { Asset } from 'expo-asset'; 15import { Ionicons } from '@expo/vector-icons'; 16 17import Colors from '../constants/Colors'; 18 19interface State { 20 ready: boolean; 21 image?: Asset | ImageManipulator.ImageResult; 22 original?: Asset; 23} 24 25export default class ImageManipulatorScreen extends React.Component<{}, State> { 26 static navigationOptions = { 27 title: 'ImageManipulator', 28 }; 29 30 readonly state: State = { 31 ready: false, 32 }; 33 34 componentWillMount() { 35 (async () => { 36 const image = Asset.fromModule( 37 require('../../assets/images/example2.jpg') 38 ); 39 await image.downloadAsync(); 40 this.setState({ 41 ready: true, 42 image, 43 original: image, 44 }); 45 })(); 46 } 47 48 render() { 49 return ( 50 <ScrollView style={styles.container}> 51 <View style={{ padding: 10 }}> 52 <View style={styles.actionsButtons}> 53 <Button style={styles.button} onPress={() => this._rotate(90)}> 54 <Ionicons name="ios-refresh" size={16} color="#ffffff" /> 90 55 </Button> 56 <Button style={styles.button} onPress={() => this._rotate(45)}> 57 45 58 </Button> 59 <Button style={styles.button} onPress={() => this._rotate(-90)}> 60 -90 61 </Button> 62 <Button 63 style={styles.button} 64 onPress={() => this._flip(ImageManipulator.FlipType.Horizontal)} 65 > 66 Flip horizontal 67 </Button> 68 <Button 69 style={styles.button} 70 onPress={() => this._flip(ImageManipulator.FlipType.Vertical)} 71 > 72 Flip vertical 73 </Button> 74 <Button 75 style={styles.button} 76 onPress={() => this._resize({ width: 250 })} 77 > 78 Resize width 79 </Button> 80 <Button 81 style={styles.button} 82 onPress={() => this._resize({ width: 300, height: 300 })} 83 > 84 Resize both to square 85 </Button> 86 <Button style={styles.button} onPress={() => this._compress(0.1)}> 87 90% compression 88 </Button> 89 <Button style={styles.button} onPress={this._crop}> 90 Crop - half image 91 </Button> 92 <Button style={styles.button} onPress={this._combo}> 93 Cccombo 94 </Button> 95 </View> 96 97 {this.state.ready && this._renderImage()} 98 <View style={styles.footerButtons}> 99 <Button style={styles.button} onPress={this._pickPhoto}> 100 Pick a photo 101 </Button> 102 <Button style={styles.button} onPress={this._reset}> 103 Reset photo 104 </Button> 105 </View> 106 </View> 107 </ScrollView> 108 ); 109 } 110 111 _renderImage = () => { 112 return ( 113 <View style={styles.imageContainer}> 114 <Image 115 source={{ uri: (this.state.image! as Asset).localUri || this.state.image!.uri }} 116 style={styles.image} 117 /> 118 </View> 119 ); 120 } 121 122 _pickPhoto = async () => { 123 const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL); 124 if (status !== 'granted') { 125 alert('Permission to CAMERA_ROLL not granted!'); 126 return; 127 } 128 const result = await ImagePicker.launchImageLibraryAsync({ 129 allowsEditing: false, 130 }); 131 if (result.cancelled) { 132 alert('No image selected!'); 133 return; 134 } 135 this.setState({ image: result }); 136 } 137 138 _rotate = async (deg: number) => { 139 await this._manipulate([{ rotate: deg }], { 140 format: ImageManipulator.SaveFormat.PNG, 141 }); 142 } 143 144 _resize = async (size: { width?: number; height?: number }) => { 145 await this._manipulate([{ resize: size }]); 146 } 147 148 _flip = async (flip: ImageManipulator.FlipType) => { 149 await this._manipulate([{ flip }]); 150 } 151 152 _compress = async (compress: number) => { 153 await this._manipulate([], { compress }); 154 } 155 156 _crop = async () => { 157 await this._manipulate([ 158 { 159 crop: { 160 originX: 0, 161 originY: 0, 162 width: this.state.image!.width! / 2, 163 height: this.state.image!.height!, 164 }, 165 }, 166 ]); 167 } 168 169 _combo = async () => { 170 await this._manipulate([ 171 { rotate: 180 }, 172 { flip: ImageManipulator.FlipType.Vertical }, 173 { 174 crop: { 175 originX: this.state.image!.width! / 4, 176 originY: this.state.image!.height! / 4, 177 width: this.state.image!.width! / 2, 178 height: this.state.image!.width! / 2, 179 }, 180 }, 181 ]); 182 } 183 184 _reset = () => { 185 this.setState({ image: this.state.original }); 186 } 187 188 _manipulate = async (actions: ImageManipulator.Action[], saveOptions?: ImageManipulator.SaveOptions) => { 189 const manipResult = await ImageManipulator.manipulateAsync( 190 (this.state.image! as Asset).localUri || this.state.image!.uri, 191 actions, 192 saveOptions 193 ); 194 this.setState({ image: manipResult }); 195 } 196} 197 198const Button: React.FunctionComponent<TouchableOpacityProps> = ({ onPress, style, children }) => ( 199 <TouchableOpacity 200 onPress={onPress} 201 style={[styles.button, style]} 202 > 203 <Text style={styles.buttonText}>{children}</Text> 204 </TouchableOpacity> 205); 206 207const styles = StyleSheet.create({ 208 container: { 209 flex: 1, 210 }, 211 imageContainer: { 212 marginVertical: 10, 213 alignItems: 'center', 214 justifyContent: 'center', 215 }, 216 image: { 217 width: 300, 218 height: 300, 219 resizeMode: 'contain', 220 }, 221 button: { 222 padding: 8, 223 borderRadius: 3, 224 backgroundColor: Colors.tintColor, 225 marginRight: 10, 226 marginBottom: 10, 227 }, 228 actionsButtons: { 229 flexDirection: 'row', 230 flexWrap: 'wrap', 231 }, 232 footerButtons: { 233 flexDirection: 'row', 234 flexWrap: 'wrap', 235 }, 236 buttonText: { 237 color: '#fff', 238 fontSize: 12, 239 }, 240}); 241