No Description

index.android.current.js 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. 'use strict';
  2. import React, { PureComponent } from 'react';
  3. import { Animated, Easing, StyleSheet, WebView, Dimensions } from 'react-native';
  4. import PropTypes from 'prop-types';
  5. import { androidPropTypes } from 'react-native-autoheight-webview/autoHeightWebView/propTypes';
  6. import {
  7. isEqual,
  8. setState,
  9. isSizeChanged,
  10. handleSizeUpdated,
  11. getWidth,
  12. getScript,
  13. domMutationObserveScript,
  14. getCurrentSize
  15. } from 'react-native-autoheight-webview/autoHeightWebView/common';
  16. import momoize from 'react-native-autoheight-webview/autoHeightWebView/momoize';
  17. const getUpdatedState = momoize(setState, isEqual);
  18. function getRenderSize(enableAnimation, height, width, heightOffset, heightValue, widthValue) {
  19. return {
  20. height: enableAnimation ? heightValue : height ? height + heightOffset : 0,
  21. width: enableAnimation ? widthValue : width
  22. };
  23. }
  24. export default class AutoHeightWebView extends PureComponent {
  25. static propTypes = {
  26. ...androidPropTypes,
  27. animationEasing: PropTypes.func
  28. };
  29. static defaultProps = {
  30. baseUrl: 'file:///android_asset/web/',
  31. scalesPageToFit: true,
  32. enableAnimation: true,
  33. animationDuration: 255,
  34. heightOffset: 20,
  35. animationEasing: Easing.out(Easing.quad)
  36. };
  37. constructor(props) {
  38. super(props);
  39. const { baseUrl, enableAnimation, style, source, heightOffset } = props;
  40. this.webView = React.createRef();
  41. this.finishInterval = true;
  42. const initWidth = getWidth(style);
  43. const initHeight = style ? (style.height ? style.height : 0) : 0;
  44. let state = {
  45. isSizeChanged: false,
  46. isSizeMayChange: false,
  47. height: initHeight,
  48. width: initWidth,
  49. script: getScript(props, getBaseScript),
  50. source: Object.assign({}, source, { baseUrl })
  51. };
  52. if (enableAnimation) {
  53. Object.assign(state, {
  54. heightValue: new Animated.Value(initHeight ? initHeight + heightOffset : 0),
  55. widthValue: new Animated.Value(initWidth)
  56. });
  57. }
  58. this.state = state;
  59. }
  60. componentDidMount() {
  61. this.startInterval();
  62. }
  63. static getDerivedStateFromProps(props, state) {
  64. const { height: oldHeight, width: oldWidth, source: prevSource, script: prevScript } = state;
  65. const { style } = props;
  66. const { source, script } = getUpdatedState(props, getBaseScript);
  67. const height = style ? style.height : null;
  68. const width = style ? style.width : null;
  69. if (source.html !== prevSource.html || source.uri !== prevSource.uri || script !== prevScript) {
  70. return {
  71. source,
  72. script,
  73. isSizeMayChange: true
  74. };
  75. }
  76. if (isSizeChanged(height, oldHeight, width, oldWidth)) {
  77. return {
  78. height: height || oldHeight,
  79. width: width || oldWidth,
  80. isSizeChanged: true
  81. };
  82. }
  83. return null;
  84. }
  85. componentDidUpdate() {
  86. const { height, width, isSizeChanged, isSizeMayChange, heightValue, widthValue } = this.state;
  87. if (isSizeMayChange) {
  88. this.startInterval();
  89. this.setState({ isSizeMayChange: false });
  90. }
  91. if (isSizeChanged) {
  92. const { enableAnimation, animationDuration, animationEasing, onSizeUpdated, heightOffset } = this.props;
  93. if (enableAnimation) {
  94. Animated.parallel([
  95. Animated.timing(heightValue, {
  96. toValue: height ? height + heightOffset : 0,
  97. easing: animationEasing,
  98. duration: animationDuration
  99. }),
  100. Animated.timing(widthValue, {
  101. toValue: width,
  102. easing: animationEasing,
  103. duration: animationDuration
  104. })
  105. ]).start(() => {
  106. handleSizeUpdated(height, width, onSizeUpdated);
  107. });
  108. } else {
  109. handleSizeUpdated(height, width, onSizeUpdated);
  110. }
  111. this.setState({ isSizeChanged: false });
  112. }
  113. }
  114. componentWillUnmount() {
  115. this.stopInterval();
  116. }
  117. startInterval() {
  118. if (this.finishInterval === false) {
  119. return;
  120. }
  121. this.finishInterval = false;
  122. this.setState({
  123. interval: setInterval(() => !this.finishInterval && this.webView.current.postMessage('getBodyHeight'), 205)
  124. });
  125. }
  126. stopInterval() {
  127. this.finishInterval = true;
  128. clearInterval(this.state.interval);
  129. }
  130. onMessage = event => {
  131. if (!event.nativeEvent) {
  132. return;
  133. }
  134. let data = {};
  135. // Sometimes the message is invalid JSON, so we ignore that case
  136. try {
  137. data = JSON.parse(event.nativeEvent.data);
  138. } catch (error) {
  139. console.error(error);
  140. return;
  141. }
  142. const { height, width } = data;
  143. const { height: oldHeight, width: oldWidth } = this.state;
  144. if (isSizeChanged(height, oldHeight, width, oldWidth)) {
  145. this.stopInterval();
  146. this.setState({
  147. isSizeChanged: true,
  148. height,
  149. width
  150. });
  151. }
  152. const { onMessage } = this.props;
  153. onMessage && onMessage(event);
  154. };
  155. render() {
  156. const { height, width, script, source, heightValue, widthValue } = this.state;
  157. const {
  158. domStorageEnabled,
  159. thirdPartyCookiesEnabled,
  160. userAgent,
  161. geolocationEnabled,
  162. allowUniversalAccessFromFileURLs,
  163. mixedContentMode,
  164. onNavigationStateChange,
  165. renderError,
  166. originWhitelist,
  167. mediaPlaybackRequiresUserAction,
  168. scalesPageToFit,
  169. style,
  170. heightOffset,
  171. enableAnimation,
  172. onError,
  173. onLoad,
  174. onLoadStart,
  175. onLoadEnd
  176. } = this.props;
  177. return (
  178. <Animated.View
  179. style={[
  180. styles.container,
  181. getRenderSize(enableAnimation, height, width, heightOffset, heightValue, widthValue),
  182. style
  183. ]}
  184. >
  185. <WebView
  186. onNavigationStateChange={onNavigationStateChange}
  187. domStorageEnabled={domStorageEnabled}
  188. thirdPartyCookiesEnabled={thirdPartyCookiesEnabled}
  189. userAgent={userAgent}
  190. geolocationEnabled={geolocationEnabled}
  191. allowUniversalAccessFromFileURLs={allowUniversalAccessFromFileURLs}
  192. mixedContentMode={mixedContentMode}
  193. renderError={renderError}
  194. mediaPlaybackRequiresUserAction={mediaPlaybackRequiresUserAction}
  195. originWhitelist={originWhitelist || ['*']}
  196. ref={this.webView}
  197. onMessage={this.onMessage}
  198. onError={onError}
  199. onLoad={onLoad}
  200. onLoadStart={onLoadStart}
  201. onLoadEnd={onLoadEnd}
  202. style={styles.webView}
  203. scalesPageToFit={scalesPageToFit}
  204. javaScriptEnabled={true}
  205. injectedJavaScript={script}
  206. source={source}
  207. />
  208. </Animated.View>
  209. );
  210. }
  211. }
  212. const styles = StyleSheet.create({
  213. container: {
  214. height: 50,
  215. width: Dimensions.get('window').width,
  216. backgroundColor: 'transparent'
  217. },
  218. webView: {
  219. flex: 1,
  220. backgroundColor: 'transparent'
  221. }
  222. });
  223. const commonScript = `
  224. ${getCurrentSize}
  225. var wrapper = document.createElement("div");
  226. wrapper.id = "wrapper";
  227. while (document.body.firstChild instanceof Node) {
  228. wrapper.appendChild(document.body.firstChild);
  229. }
  230. document.body.appendChild(wrapper);
  231. var height = 0;
  232. `;
  233. const getBaseScript = function(style) {
  234. return `
  235. ;
  236. ${commonScript}
  237. var width = ${getWidth(style)};
  238. function updateSize() {
  239. var size = getSize(document.body.firstChild);
  240. height = size.height;
  241. width = size.width;
  242. window.postMessage(JSON.stringify({ width: width, height: height }), '*');
  243. }
  244. (function () {
  245. document.addEventListener("message", updateSize);
  246. ${domMutationObserveScript}
  247. } ());
  248. `;
  249. };