Keine Beschreibung

index.ios.js 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. 'use strict';
  2. import React, { PureComponent } from 'react';
  3. import { Animated, StyleSheet, ViewPropTypes, WebView } from 'react-native';
  4. import PropTypes from 'prop-types';
  5. import { isScriptChanged, getSize, getWidth, getScript, handleSizeUpdated, domMutationObserveScript } from './common.js';
  6. export default class AutoHeightWebView extends PureComponent {
  7. static propTypes = {
  8. hasIframe: PropTypes.bool,
  9. source: WebView.propTypes.source,
  10. customScript: PropTypes.string,
  11. customStyle: PropTypes.string,
  12. enableAnimation: PropTypes.bool,
  13. style: ViewPropTypes.style,
  14. scrollEnabled: PropTypes.bool,
  15. // either height or width updated will trigger this
  16. onSizeUpdated: PropTypes.func,
  17. // if set to true may cause some layout issues (smaller font size)
  18. scalesPageToFit: PropTypes.bool,
  19. // only works on enable animation
  20. animationDuration: PropTypes.number,
  21. // offset of rn webview margin
  22. heightOffset: PropTypes.number,
  23. // rn WebView callback
  24. onError: PropTypes.func,
  25. onLoad: PropTypes.func,
  26. onLoadStart: PropTypes.func,
  27. onLoadEnd: PropTypes.func,
  28. onShouldStartLoadWithRequest: PropTypes.func,
  29. // add web/files... to project root
  30. files: PropTypes.arrayOf(
  31. PropTypes.shape({
  32. href: PropTypes.string,
  33. type: PropTypes.string,
  34. rel: PropTypes.string
  35. })
  36. )
  37. };
  38. static defaultProps = {
  39. scalesPageToFit: false,
  40. enableAnimation: true,
  41. animationDuration: 555,
  42. heightOffset: 12
  43. };
  44. constructor(props) {
  45. super(props);
  46. const { enableAnimation, style } = props;
  47. enableAnimation && (this.opacityAnimatedValue = new Animated.Value(0));
  48. this.state = {
  49. isScriptChanged: false,
  50. width: getWidth(style),
  51. height: style && style.height ? style.height : 0,
  52. script: getScript(props, getBaseScript, getIframeBaseScript)
  53. };
  54. }
  55. componentWillReceiveProps(nextProps) {
  56. const size = getSize(nextProps, this.props);
  57. size && this.setState(size);
  58. this.isScriptChanged = isScriptChanged(nextProps, this.props);
  59. this.isScriptChanged && this.setState({ script: getScript(nextProps, getBaseScript, getIframeBaseScript) });
  60. }
  61. handleNavigationStateChange = navState => {
  62. const { title } = navState;
  63. if (!title) {
  64. return;
  65. }
  66. const [heightValue, widthValue] = title.split(',');
  67. const width = Number(widthValue);
  68. const height = Number(heightValue);
  69. const { height: oldHeight, width: oldWidth } = this.state;
  70. if ((height && height !== oldHeight) || (width && width !== oldWidth)) {
  71. // if ((height && height !== oldHeight)) {
  72. const { enableAnimation, animationDuration, onSizeUpdated } = this.props;
  73. enableAnimation && this.opacityAnimatedValue.setValue(0);
  74. this.setState(
  75. {
  76. height,
  77. width
  78. },
  79. () => {
  80. enableAnimation
  81. ? Animated.timing(this.opacityAnimatedValue, {
  82. toValue: 1,
  83. duration: animationDuration
  84. }).start(() => handleSizeUpdated(height, width, onSizeUpdated))
  85. : handleSizeUpdated(height, width, onSizeUpdated);
  86. }
  87. );
  88. }
  89. };
  90. getWebView = webView => (this.webView = webView);
  91. stopLoading() {
  92. this.webView.stopLoading();
  93. }
  94. render() {
  95. const { height, width, script } = this.state;
  96. const {
  97. onError,
  98. onLoad,
  99. onLoadStart,
  100. onLoadEnd,
  101. onShouldStartLoadWithRequest,
  102. scalesPageToFit,
  103. enableAnimation,
  104. source,
  105. heightOffset,
  106. style,
  107. scrollEnabled
  108. } = this.props;
  109. let webViewSource = Object.assign({}, source, { baseUrl: 'web/' });
  110. if (this.isScriptChanged) {
  111. this.changeSourceFlag = !this.changeSourceFlag;
  112. webViewSource = Object.assign(webViewSource, { changeSourceFlag: this.changeSourceFlag });
  113. }
  114. return (
  115. <Animated.View
  116. style={[
  117. styles.container,
  118. {
  119. opacity: enableAnimation ? this.opacityAnimatedValue : 1,
  120. width,
  121. height: height + heightOffset
  122. },
  123. style
  124. ]}
  125. >
  126. <WebView
  127. originWhitelist={['*']}
  128. ref={this.getWebView}
  129. onError={onError}
  130. onLoad={onLoad}
  131. onLoadStart={onLoadStart}
  132. onLoadEnd={onLoadEnd}
  133. onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
  134. style={styles.webView}
  135. injectedJavaScript={script}
  136. scrollEnabled={!!scrollEnabled}
  137. scalesPageToFit={scalesPageToFit}
  138. source={webViewSource}
  139. onNavigationStateChange={this.handleNavigationStateChange}
  140. />
  141. </Animated.View>
  142. );
  143. }
  144. }
  145. const styles = StyleSheet.create({
  146. container: {
  147. backgroundColor: 'transparent'
  148. },
  149. webView: {
  150. flex: 1,
  151. backgroundColor: 'transparent'
  152. }
  153. });
  154. const commonScript = `
  155. updateSize();
  156. window.addEventListener('load', updateSize);
  157. window.addEventListener('resize', updateSize);
  158. `;
  159. const getCurrentSize = `
  160. function getSize(container) {
  161. var height = container.clientHeight || document.body.offsetHeight;
  162. var width = container.clientWidth || document.body.offsetWidth;
  163. return {
  164. height,
  165. width
  166. };
  167. }
  168. `;
  169. function getBaseScript(style) {
  170. return `
  171. ;
  172. ${getCurrentSize}
  173. (function () {
  174. var height = 0;
  175. var width = ${getWidth(style)};
  176. var wrapper = document.createElement('div');
  177. wrapper.id = 'rnahw-wrapper';
  178. while (document.body.firstChild instanceof Node) {
  179. wrapper.appendChild(document.body.firstChild);
  180. }
  181. document.body.appendChild(wrapper);
  182. function updateSize() {
  183. if(document.body.offsetHeight !== height || document.body.offsetWidth !== width) {
  184. var size = getSize(wrapper);
  185. height = size.height;
  186. width = size.width;
  187. document.title = height.toString() + ',' + width.toString();
  188. }
  189. }
  190. ${commonScript}
  191. ${domMutationObserveScript}
  192. } ());
  193. `;
  194. }
  195. function getIframeBaseScript(style) {
  196. return `
  197. ;
  198. ${getCurrentSize}
  199. (function () {
  200. var height = 0;
  201. var width = ${getWidth(style)};
  202. function updateSize() {
  203. if(document.body.offsetHeight !== height || document.body.offsetWidth !== width) {
  204. var size = getSize(document.body.firstChild);
  205. height = size.height;
  206. width = size.width;
  207. document.title = height.toString() + ',' + width.toString();
  208. }
  209. }
  210. ${commonScript}
  211. ${domMutationObserveScript}
  212. } ());
  213. `;
  214. }