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