123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. import React, { Component } from "react";
  2. import {
  3. StyleSheet,
  4. Text,
  5. View,
  6. ScrollView,
  7. Image,
  8. Switch,
  9. TextInput,
  10. Picker,
  11. Slider,
  12. WebView
  13. } from "react-native";
  14. import SvgUri from "react-native-svg-uri";
  15. import omit from "lodash/omit";
  16. import { captureRef } from "react-native-view-shot";
  17. import { Surface } from "gl-react-native";
  18. import GL from "gl-react";
  19. import MapView from "react-native-maps";
  20. import Video from "react-native-video";
  21. import Btn from "./Btn";
  22. const catsSource = {
  23. uri: "https://i.imgur.com/5EOyTDQ.jpg"
  24. };
  25. const shaders = GL.Shaders.create({
  26. helloGL: {
  27. frag: `
  28. precision highp float;
  29. varying vec2 uv;
  30. uniform float blue;
  31. void main () {
  32. gl_FragColor = vec4(uv.x, uv.y, blue, 1.0);
  33. }`
  34. }
  35. });
  36. const HelloGL = GL.createComponent(
  37. ({ blue }) => <GL.Node shader={shaders.helloGL} uniforms={{ blue }} />,
  38. { displayName: "HelloGL" }
  39. );
  40. export default class App extends Component {
  41. state = {
  42. previewSource: catsSource,
  43. error: null,
  44. res: null,
  45. value: {
  46. format: "png",
  47. quality: 0.9,
  48. result: "tmpfile",
  49. snapshotContentContainer: false
  50. }
  51. };
  52. snapshot = refname => () =>
  53. captureRef(this.refs[refname], this.state.value)
  54. .then(
  55. res =>
  56. this.state.value.result !== "tmpfile"
  57. ? res
  58. : new Promise((success, failure) =>
  59. // just a test to ensure res can be used in Image.getSize
  60. Image.getSize(
  61. res,
  62. (width, height) => (
  63. console.log(res, width, height), success(res)
  64. ),
  65. failure
  66. )
  67. )
  68. )
  69. .then(res =>
  70. this.setState({
  71. error: null,
  72. res,
  73. previewSource: {
  74. uri:
  75. this.state.value.result === "base64"
  76. ? "data:image/" + this.state.value.format + ";base64," + res
  77. : res
  78. }
  79. })
  80. )
  81. .catch(
  82. error => (
  83. console.warn(error),
  84. this.setState({ error, res: null, previewSource: null })
  85. )
  86. );
  87. render() {
  88. const { value, previewSource, error, res } = this.state;
  89. const {
  90. format,
  91. quality,
  92. width,
  93. height,
  94. result,
  95. snapshotContentContainer
  96. } = value;
  97. return (
  98. <ScrollView
  99. ref="full"
  100. style={styles.root}
  101. contentContainerStyle={styles.container}
  102. >
  103. <View ref="header" style={styles.header}>
  104. <Text style={styles.title}>😃 ViewShot Example 😜</Text>
  105. <View style={styles.p1}>
  106. <Text style={styles.text}>This is a </Text>
  107. <Text style={styles.code}>react-native-view-shot</Text>
  108. <Text style={styles.text}> showcase.</Text>
  109. </View>
  110. <View style={styles.preview}>
  111. {error ? (
  112. <Text style={styles.previewError}>
  113. {"" + (error.message || error)}
  114. </Text>
  115. ) : (
  116. <Image
  117. fadeDuration={0}
  118. resizeMode="contain"
  119. style={styles.previewImage}
  120. source={previewSource}
  121. />
  122. )}
  123. </View>
  124. <Text numberOfLines={1} style={styles.previewUriText}>
  125. {res ? res.slice(0, 200) : ""}
  126. </Text>
  127. </View>
  128. <View ref="form" style={styles.form}>
  129. <View style={styles.btns}>
  130. <Btn
  131. label="😻 Reset"
  132. onPress={() => this.setState({ previewSource: catsSource })}
  133. />
  134. <Btn label="📷 Head Section" onPress={this.snapshot("header")} />
  135. <Btn label="📷 Form" onPress={this.snapshot("form")} />
  136. <Btn
  137. label="📷 Experimental Section"
  138. onPress={this.snapshot("complex")}
  139. />
  140. <Btn label="📷 All (ScrollView)" onPress={this.snapshot("full")} />
  141. <Btn label="📷 SVG" onPress={this.snapshot("svg")} />
  142. <Btn label="📷 GL React" onPress={this.snapshot("gl")} />
  143. <Btn label="📷 MapView" onPress={this.snapshot("mapview")} />
  144. <Btn label="📷 WebView" onPress={this.snapshot("webview")} />
  145. <Btn label="📷 Video" onPress={this.snapshot("video")} />
  146. <Btn
  147. label="📷 Empty View (should crash)"
  148. onPress={this.snapshot("empty")}
  149. />
  150. </View>
  151. <View style={styles.field}>
  152. <Text style={styles.label}>Format</Text>
  153. <Picker
  154. style={styles.input}
  155. selectedValue={format}
  156. onValueChange={format =>
  157. this.setState({ value: { ...value, format } })}
  158. >
  159. <Picker.Item label="PNG" value="png" />
  160. <Picker.Item label="JPEG" value="jpeg" />
  161. <Picker.Item label="WEBM (android only)" value="webm" />
  162. <Picker.Item label="INVALID" value="_invalid_" />
  163. </Picker>
  164. </View>
  165. <View style={styles.field}>
  166. <Text style={styles.label}>Quality</Text>
  167. <Slider
  168. style={styles.input}
  169. value={quality}
  170. onValueChange={quality =>
  171. this.setState({ value: { ...value, quality } })}
  172. />
  173. <Text>{(quality * 100).toFixed(0)}%</Text>
  174. </View>
  175. <View style={styles.field}>
  176. <Text style={styles.label}>Size</Text>
  177. <Switch
  178. style={styles.switch}
  179. value={width !== undefined}
  180. onValueChange={checked =>
  181. this.setState({
  182. value: omit(
  183. {
  184. ...value,
  185. width: 300,
  186. height: 300
  187. },
  188. checked ? [] : ["width", "height"]
  189. )
  190. })}
  191. />
  192. {width !== undefined ? (
  193. <TextInput
  194. style={styles.inputText}
  195. value={"" + width}
  196. keyboardType="number-pad"
  197. onChangeText={txt =>
  198. !isNaN(txt) &&
  199. this.setState({
  200. value: { ...value, width: parseInt(txt, 10) }
  201. })}
  202. />
  203. ) : (
  204. <Text style={styles.inputText}>(auto)</Text>
  205. )}
  206. <Text>x</Text>
  207. {height !== undefined ? (
  208. <TextInput
  209. style={styles.inputText}
  210. value={"" + height}
  211. keyboardType="number-pad"
  212. onChangeText={txt =>
  213. !isNaN(txt) &&
  214. this.setState({
  215. value: { ...value, height: parseInt(txt, 10) }
  216. })}
  217. />
  218. ) : (
  219. <Text style={styles.inputText}>(auto)</Text>
  220. )}
  221. </View>
  222. <View style={styles.field}>
  223. <Text style={styles.label}>Result</Text>
  224. <Picker
  225. style={styles.input}
  226. selectedValue={result}
  227. onValueChange={result =>
  228. this.setState({ value: { ...value, result } })}
  229. >
  230. <Picker.Item label="tmpfile" value="tmpfile" />
  231. <Picker.Item label="base64" value="base64" />
  232. <Picker.Item label="data URI" value="data-uri" />
  233. <Picker.Item label="INVALID" value="_invalid_" />
  234. </Picker>
  235. </View>
  236. <View style={styles.field}>
  237. <Text style={styles.label}>snapshotContentContainer</Text>
  238. <Switch
  239. style={styles.switch}
  240. value={snapshotContentContainer}
  241. onValueChange={snapshotContentContainer =>
  242. this.setState({
  243. value: { ...value, snapshotContentContainer }
  244. })}
  245. />
  246. </View>
  247. </View>
  248. <View ref="empty" collapsable={false} />
  249. <View style={styles.experimental} ref="complex" collapsable={false}>
  250. <Text style={styles.experimentalTitle}>Experimental Stuff</Text>
  251. <View ref="svg" collapsable={false}>
  252. <SvgUri
  253. width={200}
  254. height={200}
  255. source={require("./homer-simpson.svg")}
  256. />
  257. </View>
  258. <View ref="gl" collapsable={false}>
  259. <Surface width={300} height={300}>
  260. <HelloGL blue={0.5} />
  261. </Surface>
  262. </View>
  263. <MapView
  264. ref="mapview"
  265. initialRegion={{
  266. latitude: 37.78825,
  267. longitude: -122.4324,
  268. latitudeDelta: 0.0922,
  269. longitudeDelta: 0.0421
  270. }}
  271. style={{ width: 300, height: 300 }}
  272. />
  273. <View
  274. ref="webview"
  275. collapsable={false}
  276. style={{ width: 300, height: 300 }}
  277. >
  278. <WebView
  279. source={{
  280. uri: "https://github.com/gre/react-native-view-shot"
  281. }}
  282. />
  283. </View>
  284. <Video
  285. ref="video"
  286. style={{ width: 300, height: 300 }}
  287. source={require("./broadchurch.mp4")}
  288. volume={0}
  289. repeat
  290. />
  291. </View>
  292. </ScrollView>
  293. );
  294. }
  295. }
  296. const styles = StyleSheet.create({
  297. root: {
  298. flex: 1,
  299. backgroundColor: "#f6f6f6"
  300. },
  301. container: {
  302. paddingVertical: 20,
  303. backgroundColor: "#f6f6f6"
  304. },
  305. title: {
  306. fontSize: 20,
  307. textAlign: "center",
  308. margin: 10
  309. },
  310. experimental: {
  311. padding: 10,
  312. flexDirection: "column",
  313. alignItems: "center"
  314. },
  315. experimentalTitle: {
  316. fontSize: 16,
  317. margin: 10
  318. },
  319. p1: {
  320. marginBottom: 10,
  321. flexDirection: "row",
  322. flexWrap: "wrap",
  323. justifyContent: "center",
  324. alignItems: "center"
  325. },
  326. text: {
  327. color: "#333"
  328. },
  329. code: {
  330. fontWeight: "bold",
  331. color: "#000"
  332. },
  333. field: {
  334. flexDirection: "row",
  335. alignItems: "center",
  336. paddingVertical: 4,
  337. paddingHorizontal: 10
  338. },
  339. label: {
  340. minWidth: 80,
  341. fontStyle: "italic",
  342. color: "#888"
  343. },
  344. switch: {
  345. marginRight: 50
  346. },
  347. input: {
  348. flex: 1,
  349. marginHorizontal: 5
  350. },
  351. inputText: {
  352. flex: 1,
  353. marginHorizontal: 5,
  354. color: "red",
  355. textAlign: "center"
  356. },
  357. preview: {
  358. flexDirection: "row",
  359. alignItems: "center",
  360. justifyContent: "space-around"
  361. },
  362. previewImage: {
  363. width: 375,
  364. height: 300
  365. },
  366. previewUriText: {
  367. fontSize: 12,
  368. fontStyle: "italic",
  369. color: "#666",
  370. textAlign: "center",
  371. padding: 10,
  372. paddingBottom: 0
  373. },
  374. previewError: {
  375. width: 375,
  376. height: 300,
  377. paddingTop: 20,
  378. textAlign: "center",
  379. fontSize: 20,
  380. fontWeight: "bold",
  381. color: "#fff",
  382. backgroundColor: "#c00"
  383. },
  384. header: {
  385. backgroundColor: "#f6f6f6",
  386. borderColor: "#000",
  387. borderWidth: 1,
  388. paddingBottom: 20
  389. },
  390. form: {
  391. backgroundColor: "#fff"
  392. },
  393. btns: {
  394. flexDirection: "row",
  395. flexWrap: "wrap",
  396. alignItems: "center",
  397. justifyContent: "center",
  398. paddingVertical: 10,
  399. margin: 4
  400. }
  401. });