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