Skip to content

Commit 50a154f

Browse files
sgnyMoOx
authored andcommitted
Add documentation for ImageEditor API (#521)
1 parent db78085 commit 50a154f

1 file changed

Lines changed: 215 additions & 38 deletions

File tree

Lines changed: 215 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,222 @@
11
---
22
id: apis/ImageEditor
33
title: ImageEditor
4-
wip: true
4+
officialDoc: https://facebook.github.io/react-native/docs/imageeditor
55
---
66

7+
## Methods
8+
9+
### `cropImage`
10+
11+
`cropImage` takes named arguments `uri` (of type `source`), `cropData` (of type
12+
`cropData`), `success` (callback function of type `string => unit`) and
13+
`failure` (callback function of type `(~code: int, ~message: string) => unit`)
14+
and returns `unit`
15+
16+
```reason
17+
cropImage: (
18+
~uri: source,
19+
~cropData: cropData,
20+
~success: string => unit,
21+
~failure: (~code: int, ~message: string) => unit
22+
) => unit
23+
```
24+
25+
If the method succeeds, the handler is passed the path of the resulting cropped
26+
image as `string`.
27+
28+
### `fromRequired`
29+
30+
To convert a `Packager.required` object into [`source`](#source).
31+
32+
```reason
33+
fromRequired: Packager.required => source
34+
```
35+
36+
### `fromUriSource`
37+
38+
To convert a URI given as a `string` into [`source`](#source).
39+
40+
```reason
41+
fromUriSource: string => source
42+
```
43+
44+
## Types
45+
46+
### `source`
47+
48+
An abstract type created using the [`fromRequired`](#fromRequired) and
49+
[`fromUriSource`](#fromUriSource) methods.
50+
51+
### `offset`
52+
53+
An abstract type created using the constructor of the same name which takes
54+
named arguments `x` and `y` of type `int`.
55+
56+
```reason
57+
offset: (~x: int, ~y: int) => offset
58+
```
59+
60+
### `size`
61+
62+
An abstract type created using the constructor of the same name which takes
63+
named arguments `width` and `height` of type `int`.
64+
65+
```reason
66+
size: (~width: int, ~height: int) => size
67+
```
68+
69+
### `cropData`
70+
71+
An abstract type created using the constructor of the same name which takes
72+
named arguments `offset` (of type `offset`) and `size` (of type `size`) and
73+
optional arguments `displaySize` (of type `size`) and `resizeMode` (one of
74+
polymorphic variants `` `contain ``, `` `cover ``, `` `stretch ``).
75+
76+
```reason
77+
cropData: (
78+
~offset: offset,
79+
~size: size,
80+
~displaySize: size=?,
81+
~resizeMode=[ | `contain | `cover | `stretch]=?,
82+
unit
83+
) => cropData
84+
```
85+
86+
## Example
87+
788
```reason
8-
type source;
9-
10-
external fromRequired: Packager.required => source = "%identity";
11-
external fromUriSource: string => source = "%identity";
12-
13-
type offset;
14-
15-
[@bs.obj] external offset: (~x: int, ~y: int) => offset = "";
16-
17-
type size;
18-
19-
[@bs.obj] external size: (~width: int, ~height: int) => size = "";
20-
21-
type cropData;
22-
23-
[@bs.obj]
24-
external cropData:
25-
(
26-
~offset: offset,
27-
~size: size,
28-
~displaySize: size=?,
29-
~resizeMode: [@bs.string] [ | `contain | `cover | `stretch]=?,
30-
unit
31-
) =>
32-
cropData =
33-
"";
34-
35-
[@bs.module "react-native"] [@bs.scope "ImageEditor"]
36-
external cropImage:
37-
(
38-
~uri: source,
39-
~cropData: cropData,
40-
~success: string => unit,
41-
~failure: (~code: int, ~message: string) => unit
42-
) =>
43-
unit =
44-
"";
89+
open ReactNative;
90+
91+
// hardcoding actual image dimensions
92+
let imageWidth = 3396.;
93+
let imageHeight = 2388.;
94+
95+
let windowWidth = Dimensions.get(`window)##width;
96+
let windowHeight = Dimensions.get(`window)##height;
97+
98+
let displayWidth = windowWidth *. 0.9;
99+
let displayHeight = windowWidth *. 0.9 *. imageHeight /. imageWidth;
100+
101+
let styles =
102+
Style.(
103+
StyleSheet.create({
104+
"container":
105+
style(
106+
~width=windowWidth->pt,
107+
~height=windowHeight->pt,
108+
~flexDirection=`column,
109+
~alignItems=`center,
110+
~justifyContent=`center,
111+
(),
112+
),
113+
"frame":
114+
style(
115+
~width=displayWidth->pt,
116+
~height=displayHeight->pt,
117+
~alignItems=`center,
118+
~justifyContent=`center,
119+
~borderWidth=StyleSheet.hairlineWidth,
120+
(),
121+
),
122+
})
123+
);
124+
125+
type state = {
126+
path: option(string),
127+
imageLoaded: bool,
128+
};
129+
130+
type action =
131+
| SetPath(option(string))
132+
| SetImageLoaded;
133+
134+
let imageUri = "https://images.unsplash.com/photo-1520453803296-c39eabe2dab4";
135+
let uri = ImageEditor.fromUriSource(imageUri);
136+
137+
let handleCropImage = (cropData, send, handler) =>
138+
ImageEditor.cropImage(
139+
~uri,
140+
~cropData,
141+
~success=successURI => send(handler(successURI)),
142+
~failure=(~code, ~message) => message->Js.Console.warn,
143+
);
144+
145+
[@react.component]
146+
let make = () => {
147+
let (state, dispatch) =
148+
React.useReducer(
149+
(state, action) =>
150+
switch (action) {
151+
| SetPath(p) => {...state, path: p}
152+
| SetImageLoaded => {...state, imageLoaded: true}
153+
},
154+
{path: None, imageLoaded: false},
155+
);
156+
157+
let size =
158+
ImageEditor.size(
159+
~width=(imageWidth *. 0.5)->floor->truncate,
160+
~height=(imageHeight *. 0.5)->floor->truncate,
161+
);
162+
163+
let cropData = offset =>
164+
ImageEditor.cropData(
165+
~offset,
166+
~size,
167+
~resizeMode=`cover,
168+
(),
169+
);
170+
171+
<View style=styles##container>
172+
<Text>
173+
"Click on a quadrant of the image to crop it."->React.string
174+
</Text>
175+
<View style=styles##frame>
176+
<TouchableOpacity
177+
onPress={e =>
178+
handleCropImage(
179+
ImageEditor.offset(
180+
~x=
181+
{e##nativeEvent##locationX /. displayWidth < 0.5
182+
? 0 : (0.5 *. imageWidth)->floor->truncate},
183+
~y=
184+
{e##nativeEvent##locationY /. displayHeight < 0.5
185+
? 0 : (0.5 *. imageHeight)->floor->truncate},
186+
)
187+
->cropData,
188+
dispatch,
189+
link =>
190+
SetPath(Some(link))
191+
)
192+
}>
193+
<Image
194+
source={Image.Source.fromUriSource(
195+
Image.uriSource(
196+
~uri=
197+
Belt.Option.getWithDefault(
198+
state.path,
199+
imageUri,
200+
),
201+
~width=displayWidth,
202+
~height=
203+
if (state.imageLoaded) {
204+
displayHeight;
205+
} else {
206+
0.;
207+
},
208+
(),
209+
),
210+
)}
211+
resizeMode=`contain
212+
onLoadEnd={() => dispatch(SetImageLoaded)}
213+
/>
214+
</TouchableOpacity>
215+
{state.imageLoaded
216+
? React.null
217+
: <Text> "Please wait while image is loaded."->React.string </Text>}
218+
</View>
219+
<Button title="Reset Image" onPress={_ => dispatch(SetPath(None))} />
220+
</View>;
221+
};
45222
```

0 commit comments

Comments
 (0)