123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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. ART
  14. } from "react-native";
  15. import SvgUri from "react-native-svg-uri";
  16. import omit from "lodash/omit";
  17. import { captureRef, captureScreen } from "react-native-view-shot";
  18. import { Surface } from "gl-react-native";
  19. import GL from "gl-react";
  20. import MapView from "react-native-maps";
  21. import Video from "react-native-video";
  22. import Btn from "./Btn";
  23. const catsSource = {
  24. uri: "https://i.imgur.com/5EOyTDQ.jpg"
  25. };
  26. const shaders = GL.Shaders.create({
  27. helloGL: {
  28. frag: `
  29. precision highp float;
  30. varying vec2 uv;
  31. uniform float blue;
  32. void main () {
  33. gl_FragColor = vec4(uv.x, uv.y, blue, 1.0);
  34. }`
  35. }
  36. });
  37. const HelloGL = GL.createComponent(
  38. ({ blue }) => <GL.Node shader={shaders.helloGL} uniforms={{ blue }} />,
  39. { displayName: "HelloGL" }
  40. );
  41. export default class App extends Component {
  42. state = {
  43. previewSource: catsSource,
  44. error: null,
  45. res: null,
  46. value: {
  47. format: "png",
  48. quality: 0.9,
  49. result: "tmpfile",
  50. snapshotContentContainer: false
  51. }
  52. };
  53. snapshot = refname => () =>
  54. (refname
  55. ? captureRef(this.refs[refname], this.state.value)
  56. : captureScreen(this.state.value)
  57. )
  58. .then(
  59. res =>
  60. this.state.value.result !== "tmpfile"
  61. ? res
  62. : new Promise((success, failure) =>
  63. // just a test to ensure res can be used in Image.getSize
  64. Image.getSize(
  65. res,
  66. (width, height) => (
  67. console.log(res, width, height), success(res)
  68. ),
  69. failure
  70. )
  71. )
  72. )
  73. .then(res =>
  74. this.setState({
  75. error: null,
  76. res,
  77. previewSource: {
  78. uri:
  79. this.state.value.result === "base64"
  80. ? "data:image/" + this.state.value.format + ";base64," + res
  81. : res
  82. }
  83. })
  84. )
  85. .catch(
  86. error => (
  87. console.warn(error),
  88. this.setState({ error, res: null, previewSource: null })
  89. )
  90. );
  91. render() {
  92. const { value, previewSource, error, res } = this.state;
  93. const {
  94. format,
  95. quality,
  96. width,
  97. height,
  98. result,
  99. snapshotContentContainer
  100. } = value;
  101. return (
  102. <ScrollView
  103. ref="full"
  104. style={styles.root}
  105. contentContainerStyle={styles.container}
  106. >
  107. <View ref="header" style={styles.header}>
  108. <Text style={styles.title}>😃 ViewShot Example 😜</Text>
  109. <View style={styles.p1}>
  110. <Text style={styles.text}>This is a </Text>
  111. <Text style={styles.code}>react-native-view-shot</Text>
  112. <Text style={styles.text}> showcase.</Text>
  113. </View>
  114. <View style={styles.preview}>
  115. {error ? (
  116. <Text style={styles.previewError}>
  117. {"" + (error.message || error)}
  118. </Text>
  119. ) : (
  120. <Image
  121. fadeDuration={0}
  122. resizeMode="contain"
  123. style={styles.previewImage}
  124. source={previewSource}
  125. />
  126. )}
  127. </View>
  128. <Text numberOfLines={1} style={styles.previewUriText}>
  129. {res ? res.slice(0, 200) : ""}
  130. </Text>
  131. </View>
  132. <View ref="form" style={styles.form}>
  133. <View style={styles.btns}>
  134. <Btn
  135. label="😻 Reset"
  136. onPress={() => this.setState({ previewSource: catsSource })}
  137. />
  138. <Btn label="📷 Head Section" onPress={this.snapshot("header")} />
  139. <Btn label="📷 Form" onPress={this.snapshot("form")} />
  140. <Btn
  141. label="📷 Experimental Section"
  142. onPress={this.snapshot("complex")}
  143. />
  144. <Btn label="📷 All (ScrollView)" onPress={this.snapshot("full")} />
  145. <Btn label="📷 SVG" onPress={this.snapshot("svg")} />
  146. <Btn label="📷 Transform" onPress={this.snapshot("transformParent")} />
  147. <Btn label="📷 Transform Child" onPress={this.snapshot("transform")} />
  148. <Btn label="📷 GL React" onPress={this.snapshot("gl")} />
  149. <Btn label="📷 MapView" onPress={this.snapshot("mapview")} />
  150. <Btn label="📷 WebView" onPress={this.snapshot("webview")} />
  151. <Btn label="📷 Video" onPress={this.snapshot("video")} />
  152. <Btn label="📷 Native Screenshot" onPress={this.snapshot()} />
  153. <Btn
  154. label="📷 Empty View (should crash)"
  155. onPress={this.snapshot("empty")}
  156. />
  157. </View>
  158. <View style={styles.field}>
  159. <Text style={styles.label}>Format</Text>
  160. <Picker
  161. style={styles.input}
  162. selectedValue={format}
  163. onValueChange={format =>
  164. this.setState({ value: { ...value, format } })}
  165. >
  166. <Picker.Item label="PNG" value="png" />
  167. <Picker.Item label="JPEG" value="jpeg" />
  168. <Picker.Item label="WEBM (android only)" value="webm" />
  169. <Picker.Item label="RAW (android only)" value="raw" />
  170. <Picker.Item label="INVALID" value="_invalid_" />
  171. </Picker>
  172. </View>
  173. <View style={styles.field}>
  174. <Text style={styles.label}>Quality</Text>
  175. <Slider
  176. style={styles.input}
  177. value={quality}
  178. onValueChange={quality =>
  179. this.setState({ value: { ...value, quality } })}
  180. />
  181. <Text>{(quality * 100).toFixed(0)}%</Text>
  182. </View>
  183. <View style={styles.field}>
  184. <Text style={styles.label}>Size</Text>
  185. <Switch
  186. style={styles.switch}
  187. value={width !== undefined}
  188. onValueChange={checked =>
  189. this.setState({
  190. value: omit(
  191. {
  192. ...value,
  193. width: 300,
  194. height: 300
  195. },
  196. checked ? [] : ["width", "height"]
  197. )
  198. })}
  199. />
  200. {width !== undefined ? (
  201. <TextInput
  202. style={styles.inputText}
  203. value={"" + width}
  204. keyboardType="number-pad"
  205. onChangeText={txt =>
  206. !isNaN(txt) &&
  207. this.setState({
  208. value: { ...value, width: parseInt(txt, 10) }
  209. })}
  210. />
  211. ) : (
  212. <Text style={styles.inputText}>(auto)</Text>
  213. )}
  214. <Text>x</Text>
  215. {height !== undefined ? (
  216. <TextInput
  217. style={styles.inputText}
  218. value={"" + height}
  219. keyboardType="number-pad"
  220. onChangeText={txt =>
  221. !isNaN(txt) &&
  222. this.setState({
  223. value: { ...value, height: parseInt(txt, 10) }
  224. })}
  225. />
  226. ) : (
  227. <Text style={styles.inputText}>(auto)</Text>
  228. )}
  229. </View>
  230. <View style={styles.field}>
  231. <Text style={styles.label}>Result</Text>
  232. <Picker
  233. style={styles.input}
  234. selectedValue={result}
  235. onValueChange={result =>
  236. this.setState({ value: { ...value, result } })}
  237. >
  238. <Picker.Item label="tmpfile" value="tmpfile" />
  239. <Picker.Item label="base64" value="base64" />
  240. <Picker.Item label="zip-base64 (Android Only)" value="zip-base64" />
  241. <Picker.Item label="data URI" value="data-uri" />
  242. <Picker.Item label="INVALID" value="_invalid_" />
  243. </Picker>
  244. </View>
  245. <View style={styles.field}>
  246. <Text style={styles.label}>snapshotContentContainer</Text>
  247. <Switch
  248. style={styles.switch}
  249. value={snapshotContentContainer}
  250. onValueChange={snapshotContentContainer =>
  251. this.setState({
  252. value: { ...value, snapshotContentContainer }
  253. })}
  254. />
  255. </View>
  256. </View>
  257. <View ref="empty" collapsable={false} />
  258. <View style={styles.experimental} ref="complex" collapsable={false}>
  259. <Text style={styles.experimentalTitle}>Experimental Stuff</Text>
  260. <View ref="transformParent" collapsable={false} style={{flex: 1, flexDirection: 'row'}}>
  261. <View ref="transformInner" collapsable={false} style={styles.experimentalTransform}>
  262. <Text ref="transform" >Transform</Text>
  263. <ART.Surface ref="surface" width={20} height={20}>
  264. <ART.Text
  265. fill="#000000"
  266. font={{fontFamily:'Arial',fontSize: 6}}
  267. >Sample Text</ART.Text>
  268. <ART.Shape
  269. d='M2.876,10.6499757 L16.375,18.3966817 C16.715,18.5915989 17.011,18.4606545 17.125,18.3956822 C17.237,18.3307098 17.499,18.1367923 17.499,17.746958 L17.499,2.25254636 C17.499,1.86271212 17.237,1.66879457 17.125,1.6038222 C17.011,1.53884983 16.715,1.4079055 16.375,1.60282262 L2.876,9.34952866 C2.537,9.54544536 2.5,9.86930765 2.5,10.000252 C2.5,10.1301967 2.537,10.4550586 2.876,10.6499757 M16.749,20 C16.364,20 15.98,19.8990429 15.629,19.6971288 L2.13,11.9504227 L2.129,11.9504227 C1.422,11.5445953 1,10.8149056 1,10.000252 C1,9.18459879 1.422,8.45590864 2.129,8.04908162 L15.629,0.302375584 C16.332,-0.10245228 17.173,-0.10045313 17.876,0.306373884 C18.579,0.713200898 18.999,1.44089148 18.999,2.25254636 L18.999,17.746958 C18.999,18.5586129 18.579,19.2863035 17.876,19.6931305 C17.523,19.8970438 17.136,20 16.749,20'
  270. fill="blue"
  271. stroke="black"
  272. strokeWidth={0}
  273. >
  274. </ART.Shape>
  275. </ART.Surface>
  276. </View>
  277. <View ref="right" style={styles.experimentalTransformV2}>
  278. <ART.Surface ref="surface2" width={20} height={20}>
  279. <ART.Shape
  280. x={0}
  281. y={10}
  282. d='M2.876,10.6499757 L16.375,18.3966817 C16.715,18.5915989 17.011,18.4606545 17.125,18.3956822 C17.237,18.3307098 17.499,18.1367923 17.499,17.746958 L17.499,2.25254636 C17.499,1.86271212 17.237,1.66879457 17.125,1.6038222 C17.011,1.53884983 16.715,1.4079055 16.375,1.60282262 L2.876,9.34952866 C2.537,9.54544536 2.5,9.86930765 2.5,10.000252 C2.5,10.1301967 2.537,10.4550586 2.876,10.6499757 M16.749,20 C16.364,20 15.98,19.8990429 15.629,19.6971288 L2.13,11.9504227 L2.129,11.9504227 C1.422,11.5445953 1,10.8149056 1,10.000252 C1,9.18459879 1.422,8.45590864 2.129,8.04908162 L15.629,0.302375584 C16.332,-0.10245228 17.173,-0.10045313 17.876,0.306373884 C18.579,0.713200898 18.999,1.44089148 18.999,2.25254636 L18.999,17.746958 C18.999,18.5586129 18.579,19.2863035 17.876,19.6931305 C17.523,19.8970438 17.136,20 16.749,20'
  283. fill="red"
  284. >
  285. </ART.Shape>
  286. </ART.Surface>
  287. </View>
  288. </View>
  289. <View ref="svg" collapsable={false}>
  290. <SvgUri
  291. width={200}
  292. height={200}
  293. source={require("./homer-simpson.svg")}
  294. />
  295. </View>
  296. <View ref="gl" collapsable={false}>
  297. <Surface width={300} height={300}>
  298. <HelloGL blue={0.5} />
  299. </Surface>
  300. </View>
  301. <MapView
  302. ref="mapview"
  303. initialRegion={{
  304. latitude: 37.78825,
  305. longitude: -122.4324,
  306. latitudeDelta: 0.0922,
  307. longitudeDelta: 0.0421
  308. }}
  309. style={{ width: 300, height: 300 }}
  310. />
  311. <View
  312. ref="webview"
  313. collapsable={false}
  314. style={{ width: 300, height: 300 }}
  315. >
  316. <WebView
  317. source={{
  318. uri: "https://github.com/gre/react-native-view-shot"
  319. }}
  320. />
  321. </View>
  322. <Video
  323. ref="video"
  324. style={{ width: 300, height: 300 }}
  325. source={require("./broadchurch.mp4")}
  326. volume={0}
  327. repeat
  328. />
  329. </View>
  330. </ScrollView>
  331. );
  332. }
  333. }
  334. const styles = StyleSheet.create({
  335. root: {
  336. flex: 1,
  337. backgroundColor: "#f6f6f6"
  338. },
  339. container: {
  340. paddingVertical: 20,
  341. backgroundColor: "#f6f6f6"
  342. },
  343. title: {
  344. fontSize: 20,
  345. textAlign: "center",
  346. margin: 10
  347. },
  348. experimental: {
  349. padding: 10,
  350. flexDirection: "column",
  351. alignItems: "center"
  352. },
  353. experimentalTitle: {
  354. fontSize: 16,
  355. margin: 10
  356. },
  357. experimentalTransform: {
  358. transform: [{ rotate: '180deg' }],
  359. backgroundColor: 'powderblue',
  360. },
  361. experimentalTransformV2: {
  362. // transform: [{ rotate: '180deg' }],
  363. backgroundColor: 'skyblue',
  364. },
  365. p1: {
  366. marginBottom: 10,
  367. flexDirection: "row",
  368. flexWrap: "wrap",
  369. justifyContent: "center",
  370. alignItems: "center"
  371. },
  372. text: {
  373. color: "#333"
  374. },
  375. code: {
  376. fontWeight: "bold",
  377. color: "#000"
  378. },
  379. field: {
  380. flexDirection: "row",
  381. alignItems: "center",
  382. paddingVertical: 4,
  383. paddingHorizontal: 10
  384. },
  385. label: {
  386. minWidth: 80,
  387. fontStyle: "italic",
  388. color: "#888"
  389. },
  390. switch: {
  391. marginRight: 50
  392. },
  393. input: {
  394. flex: 1,
  395. marginHorizontal: 5
  396. },
  397. inputText: {
  398. flex: 1,
  399. marginHorizontal: 5,
  400. color: "red",
  401. textAlign: "center"
  402. },
  403. preview: {
  404. flexDirection: "row",
  405. alignItems: "center",
  406. justifyContent: "space-around"
  407. },
  408. previewImage: {
  409. width: 375,
  410. height: 300
  411. },
  412. previewUriText: {
  413. fontSize: 12,
  414. fontStyle: "italic",
  415. color: "#666",
  416. textAlign: "center",
  417. padding: 10,
  418. paddingBottom: 0
  419. },
  420. previewError: {
  421. width: 375,
  422. height: 300,
  423. paddingTop: 20,
  424. textAlign: "center",
  425. fontSize: 20,
  426. fontWeight: "bold",
  427. color: "#fff",
  428. backgroundColor: "#c00"
  429. },
  430. header: {
  431. backgroundColor: "#f6f6f6",
  432. borderColor: "#000",
  433. borderWidth: 1,
  434. paddingBottom: 20
  435. },
  436. form: {
  437. backgroundColor: "#fff"
  438. },
  439. btns: {
  440. flexDirection: "row",
  441. flexWrap: "wrap",
  442. alignItems: "center",
  443. justifyContent: "center",
  444. paddingVertical: 10,
  445. margin: 4
  446. }
  447. });