Bez popisu

index.ios.js 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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 { needChangeSource, 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. width: getWidth(style),
  50. height: style && style.height ? style.height : 0,
  51. script: getScript(props, getBaseScript, getIframeBaseScript)
  52. };
  53. }
  54. componentWillReceiveProps(nextProps) {
  55. if (nextProps.style) {
  56. const { width, height } = nextProps.style;
  57. width && this.setState({ width });
  58. height && this.setState({ height });
  59. }
  60. this.setState({ script: getScript(nextProps, getBaseScript, getIframeBaseScript) });
  61. this.needChangeSource = needChangeSource(nextProps, this.props);
  62. }
  63. handleNavigationStateChange = navState => {
  64. const { title } = navState;
  65. if (!title) {
  66. return;
  67. }
  68. const [heightValue, widthValue] = title.split(',');
  69. const width = Number(widthValue);
  70. const height = Number(heightValue);
  71. const { height: oldHeight, width: oldWidth } = this.state;
  72. if ((height && height !== oldHeight) || (width && width !== oldWidth)) {
  73. // if ((height && height !== oldHeight)) {
  74. const { enableAnimation, animationDuration, onSizeUpdated } = this.props;
  75. enableAnimation && this.opacityAnimatedValue.setValue(0);
  76. this.setState(
  77. {
  78. height,
  79. width
  80. },
  81. () => {
  82. enableAnimation
  83. ? Animated.timing(this.opacityAnimatedValue, {
  84. toValue: 1,
  85. duration: animationDuration
  86. }).start(() => handleSizeUpdated(height, width, onSizeUpdated))
  87. : handleSizeUpdated(height, width, onSizeUpdated);
  88. }
  89. );
  90. }
  91. };
  92. getWebView = webView => (this.webView = webView);
  93. stopLoading() {
  94. this.webView.stopLoading();
  95. }
  96. render() {
  97. const { height, width, script } = this.state;
  98. const {
  99. onError,
  100. onLoad,
  101. onLoadStart,
  102. onLoadEnd,
  103. onShouldStartLoadWithRequest,
  104. scalesPageToFit,
  105. enableAnimation,
  106. source,
  107. heightOffset,
  108. style,
  109. scrollEnabled
  110. } = this.props;
  111. let webViewSource = Object.assign({}, source, { baseUrl: 'web/' });
  112. if (this.needChangeSource) {
  113. this.changeSourceFlag = !this.changeSourceFlag;
  114. webViewSource = Object.assign(webViewSource, { changeSourceFlag: this.changeSourceFlag });
  115. }
  116. return (
  117. <Animated.View
  118. style={[
  119. styles.container,
  120. {
  121. opacity: enableAnimation ? this.opacityAnimatedValue : 1,
  122. width,
  123. height: height + heightOffset
  124. },
  125. style
  126. ]}
  127. >
  128. <WebView
  129. ref={this.getWebView}
  130. onError={onError}
  131. onLoad={onLoad}
  132. onLoadStart={onLoadStart}
  133. onLoadEnd={onLoadEnd}
  134. onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
  135. style={styles.webView}
  136. injectedJavaScript={script}
  137. scrollEnabled={!!scrollEnabled}
  138. scalesPageToFit={scalesPageToFit}
  139. source={webViewSource}
  140. onNavigationStateChange={this.handleNavigationStateChange}
  141. />
  142. </Animated.View>
  143. );
  144. }
  145. }
  146. const styles = StyleSheet.create({
  147. container: {
  148. backgroundColor: 'transparent'
  149. },
  150. webView: {
  151. flex: 1,
  152. backgroundColor: 'transparent'
  153. }
  154. });
  155. const commonScript = `
  156. updateSize();
  157. window.addEventListener('load', updateSize);
  158. window.addEventListener('resize', updateSize);
  159. `;
  160. const getSize = `
  161. function getSize(container) {
  162. var height = container.clientHeight || document.body.offsetHeight;
  163. var width = container.clientWidth || document.body.offsetWidth;
  164. return {
  165. height,
  166. width
  167. };
  168. }
  169. `;
  170. function getBaseScript(style) {
  171. return `
  172. ;
  173. ${getSize}
  174. (function () {
  175. var height = 0;
  176. var width = ${getWidth(style)};
  177. var wrapper = document.createElement('div');
  178. wrapper.id = 'rnahw-wrapper';
  179. while (document.body.firstChild instanceof Node) {
  180. wrapper.appendChild(document.body.firstChild);
  181. }
  182. document.body.appendChild(wrapper);
  183. function updateSize() {
  184. if(document.body.offsetHeight !== height || document.body.offsetWidth !== width) {
  185. var size = getSize(wrapper);
  186. height = size.height;
  187. width = size.width;
  188. document.title = height.toString() + ',' + width.toString();
  189. }
  190. }
  191. ${commonScript}
  192. ${domMutationObserveScript}
  193. } ());
  194. `;
  195. }
  196. function getIframeBaseScript(style) {
  197. return `
  198. ;
  199. ${getSize}
  200. (function () {
  201. var height = 0;
  202. var width = ${getWidth(style)};
  203. function updateSize() {
  204. if(document.body.offsetHeight !== height || document.body.offsetWidth !== width) {
  205. var size = getSize(document.body.firstChild);
  206. height = size.height;
  207. width = size.width;
  208. document.title = height.toString() + ',' + width.toString();
  209. }
  210. }
  211. ${commonScript}
  212. ${domMutationObserveScript}
  213. } ());
  214. `;
  215. }