Skip to content

Commit 6dcadca

Browse files
gengjiawenfacebook-github-bot
authored andcommitted
Add back ImageEditingExample example (facebook#19972)
Summary: add back missing file. pass all current ci. none [GENERAL] [INTERNAL] [RNTester] - Add back ImageEditingExample example. Closes facebook#19972 Differential Revision: D8711888 Pulled By: TheSavior fbshipit-source-id: 14070bfbf747f7f59c4039981a9bc83c1c10862b
1 parent 2166d2b commit 6dcadca

File tree

1 file changed

+302
-1
lines changed

1 file changed

+302
-1
lines changed

RNTester/js/ImageEditingExample.js

Lines changed: 302 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,306 @@
55
* LICENSE file in the root directory of this source tree.
66
*
77
* @format
8-
* @flow strict
8+
* @flow
99
*/
10+
'use strict';
11+
12+
var React = require('react');
13+
var ReactNative = require('react-native');
14+
var {
15+
CameraRoll,
16+
Image,
17+
ImageEditor,
18+
Platform,
19+
ScrollView,
20+
StyleSheet,
21+
Text,
22+
TouchableHighlight,
23+
View,
24+
} = ReactNative;
25+
26+
var PAGE_SIZE = 20;
27+
28+
type ImageOffset = {
29+
x: number,
30+
y: number,
31+
};
32+
33+
type ImageSize = {
34+
width: number,
35+
height: number,
36+
};
37+
38+
type ImageCropData = {
39+
offset: ImageOffset,
40+
size: ImageSize,
41+
displaySize?: ?ImageSize,
42+
resizeMode?: ?any,
43+
};
44+
45+
class SquareImageCropper extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
46+
state: any;
47+
_isMounted: boolean;
48+
_transformData: ImageCropData;
49+
50+
constructor(props) {
51+
super(props);
52+
this._isMounted = true;
53+
this.state = {
54+
randomPhoto: null,
55+
measuredSize: null,
56+
croppedImageURI: null,
57+
cropError: null,
58+
};
59+
this._fetchRandomPhoto();
60+
}
61+
62+
async _fetchRandomPhoto() {
63+
try {
64+
const data = await CameraRoll.getPhotos({first: PAGE_SIZE});
65+
if (!this._isMounted) {
66+
return;
67+
}
68+
var edges = data.edges;
69+
var edge = edges[Math.floor(Math.random() * edges.length)];
70+
var randomPhoto = edge && edge.node && edge.node.image;
71+
if (randomPhoto) {
72+
this.setState({randomPhoto});
73+
}
74+
} catch (error) {
75+
console.warn("Can't get a photo from camera roll", error);
76+
}
77+
}
78+
79+
componentWillUnmount() {
80+
this._isMounted = false;
81+
}
82+
83+
render() {
84+
if (!this.state.measuredSize) {
85+
return (
86+
<View
87+
style={styles.container}
88+
onLayout={event => {
89+
var measuredWidth = event.nativeEvent.layout.width;
90+
if (!measuredWidth) {
91+
return;
92+
}
93+
this.setState({
94+
measuredSize: {width: measuredWidth, height: measuredWidth},
95+
});
96+
}}
97+
/>
98+
);
99+
}
100+
101+
if (!this.state.croppedImageURI) {
102+
return this._renderImageCropper();
103+
}
104+
return this._renderCroppedImage();
105+
}
106+
107+
_renderImageCropper() {
108+
if (!this.state.randomPhoto) {
109+
return <View style={styles.container} />;
110+
}
111+
var error = null;
112+
if (this.state.cropError) {
113+
error = <Text>{this.state.cropError.message}</Text>;
114+
}
115+
return (
116+
<View style={styles.container}>
117+
<Text>Drag the image within the square to crop:</Text>
118+
<ImageCropper
119+
image={this.state.randomPhoto}
120+
size={this.state.measuredSize}
121+
style={[styles.imageCropper, this.state.measuredSize]}
122+
onTransformDataChange={data => (this._transformData = data)}
123+
/>
124+
<TouchableHighlight
125+
style={styles.cropButtonTouchable}
126+
onPress={this._crop.bind(this)}>
127+
<View style={styles.cropButton}>
128+
<Text style={styles.cropButtonLabel}>Crop</Text>
129+
</View>
130+
</TouchableHighlight>
131+
{error}
132+
</View>
133+
);
134+
}
135+
136+
_renderCroppedImage() {
137+
return (
138+
<View style={styles.container}>
139+
<Text>Here is the cropped image:</Text>
140+
<Image
141+
source={{uri: this.state.croppedImageURI}}
142+
style={[styles.imageCropper, this.state.measuredSize]}
143+
/>
144+
<TouchableHighlight
145+
style={styles.cropButtonTouchable}
146+
onPress={this._reset.bind(this)}>
147+
<View style={styles.cropButton}>
148+
<Text style={styles.cropButtonLabel}>Try again</Text>
149+
</View>
150+
</TouchableHighlight>
151+
</View>
152+
);
153+
}
154+
155+
_crop() {
156+
ImageEditor.cropImage(
157+
this.state.randomPhoto.uri,
158+
this._transformData,
159+
croppedImageURI => this.setState({croppedImageURI}),
160+
cropError => this.setState({cropError}),
161+
);
162+
}
163+
164+
_reset() {
165+
this.setState({
166+
randomPhoto: null,
167+
croppedImageURI: null,
168+
cropError: null,
169+
});
170+
this._fetchRandomPhoto();
171+
}
172+
}
173+
174+
class ImageCropper extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
175+
_contentOffset: ImageOffset;
176+
_maximumZoomScale: number;
177+
_minimumZoomScale: number;
178+
_scaledImageSize: ImageSize;
179+
_horizontal: boolean;
180+
181+
componentWillMount() {
182+
// Scale an image to the minimum size that is large enough to completely
183+
// fill the crop box.
184+
var widthRatio = this.props.image.width / this.props.size.width;
185+
var heightRatio = this.props.image.height / this.props.size.height;
186+
this._horizontal = widthRatio > heightRatio;
187+
if (this._horizontal) {
188+
this._scaledImageSize = {
189+
width: this.props.image.width / heightRatio,
190+
height: this.props.size.height,
191+
};
192+
} else {
193+
this._scaledImageSize = {
194+
width: this.props.size.width,
195+
height: this.props.image.height / widthRatio,
196+
};
197+
if (Platform.OS === 'android') {
198+
// hack to work around Android ScrollView a) not supporting zoom, and
199+
// b) not supporting vertical scrolling when nested inside another
200+
// vertical ScrollView (which it is, when displayed inside UIExplorer)
201+
this._scaledImageSize.width *= 2;
202+
this._scaledImageSize.height *= 2;
203+
this._horizontal = true;
204+
}
205+
}
206+
this._contentOffset = {
207+
x: (this._scaledImageSize.width - this.props.size.width) / 2,
208+
y: (this._scaledImageSize.height - this.props.size.height) / 2,
209+
};
210+
this._maximumZoomScale = Math.min(
211+
this.props.image.width / this._scaledImageSize.width,
212+
this.props.image.height / this._scaledImageSize.height,
213+
);
214+
this._minimumZoomScale = Math.max(
215+
this.props.size.width / this._scaledImageSize.width,
216+
this.props.size.height / this._scaledImageSize.height,
217+
);
218+
this._updateTransformData(
219+
this._contentOffset,
220+
this._scaledImageSize,
221+
this.props.size,
222+
);
223+
}
224+
225+
_onScroll(event) {
226+
this._updateTransformData(
227+
event.nativeEvent.contentOffset,
228+
event.nativeEvent.contentSize,
229+
event.nativeEvent.layoutMeasurement,
230+
);
231+
}
232+
233+
_updateTransformData(offset, scaledImageSize, croppedImageSize) {
234+
var offsetRatioX = offset.x / scaledImageSize.width;
235+
var offsetRatioY = offset.y / scaledImageSize.height;
236+
var sizeRatioX = croppedImageSize.width / scaledImageSize.width;
237+
var sizeRatioY = croppedImageSize.height / scaledImageSize.height;
238+
239+
var cropData: ImageCropData = {
240+
offset: {
241+
x: this.props.image.width * offsetRatioX,
242+
y: this.props.image.height * offsetRatioY,
243+
},
244+
size: {
245+
width: this.props.image.width * sizeRatioX,
246+
height: this.props.image.height * sizeRatioY,
247+
},
248+
};
249+
this.props.onTransformDataChange &&
250+
this.props.onTransformDataChange(cropData);
251+
}
252+
253+
render() {
254+
return (
255+
<ScrollView
256+
alwaysBounceVertical={true}
257+
automaticallyAdjustContentInsets={false}
258+
contentOffset={this._contentOffset}
259+
decelerationRate="fast"
260+
horizontal={this._horizontal}
261+
maximumZoomScale={this._maximumZoomScale}
262+
minimumZoomScale={this._minimumZoomScale}
263+
onMomentumScrollEnd={this._onScroll.bind(this)}
264+
onScrollEndDrag={this._onScroll.bind(this)}
265+
showsHorizontalScrollIndicator={false}
266+
showsVerticalScrollIndicator={false}
267+
style={this.props.style}
268+
scrollEventThrottle={16}>
269+
<Image source={this.props.image} style={this._scaledImageSize} />
270+
</ScrollView>
271+
);
272+
}
273+
}
274+
275+
exports.framework = 'React';
276+
exports.title = 'ImageEditor';
277+
exports.description = 'Cropping and scaling with ImageEditor';
278+
exports.examples = [
279+
{
280+
title: 'Image Cropping',
281+
render() {
282+
return <SquareImageCropper />;
283+
},
284+
},
285+
];
286+
287+
var styles = StyleSheet.create({
288+
container: {
289+
flex: 1,
290+
alignSelf: 'stretch',
291+
},
292+
imageCropper: {
293+
alignSelf: 'center',
294+
marginTop: 12,
295+
},
296+
cropButtonTouchable: {
297+
alignSelf: 'center',
298+
marginTop: 12,
299+
},
300+
cropButton: {
301+
padding: 12,
302+
backgroundColor: 'blue',
303+
borderRadius: 4,
304+
},
305+
cropButtonLabel: {
306+
color: 'white',
307+
fontSize: 16,
308+
fontWeight: '500',
309+
},
310+
});

0 commit comments

Comments
 (0)