react native 实现 WebViewAutoHeight (兼容H5、已实际用于expo项目中)

使用 HtmlComponent 组件

<HtmlComponent
   html={'html'}
   onMessage={(message) => {
     console.log("Received message===>:", message);
   }}
 />

创建 HtmlComponent.tsx

import { WebView, WebViewMessageEvent } from "react-native-webview";
import AutoHeightWebView from "react-native-autoheight-webview";
import css from "./css";
import js from "./javascript";
import { Platform, View, ViewStyle } from "react-native";
import { useEffect } from "react";
import WebViewAutoHeight from "./WebViewAutoHeight";

type Props = {
  html: string;
  onLayout?: any;
  styles?: ViewStyle;
  htmlCss?: string;
  baseCss?: boolean;
  onMessage?: (message: { type: string; data: any }) => void;
};

export function HtmlComponent({
  html,
  onLayout,
  styles,
  htmlCss,
  baseCss,
  onMessage,
}: Props) {
  // web端onMessage事件回调
  const postMessage = (message: { type: string; data: any }) => {
    if (Platform.OS === "web") {
      onMessage && onMessage(message);
    }
  };

  useEffect(() => {
    if (Platform.OS === "web") {
      eval(js);
    }
  }, []);

  return Platform.OS === "web" ? (
    <View
      style={[{ width: "100%" }, styles]}
      onLayout={onLayout}>
      {baseCss != false && <style>{css}</style>}
      <style>{htmlCss}</style>
      <div
        className={baseCss != false ? "article-detail-html" : ""}
        dangerouslySetInnerHTML={{ __html: html }}
      />
    </View>
  ) : (
    <WebViewAutoHeight
      style={styles}
      onLayout={onLayout}
      source={{
        html:
          "<style>" +
          htmlCss +
          "</style>" +
          `<div class="${baseCss != false ? "article-detail-html" : ""}">` +
          html +
          "</div>",
      }}
      webStyle={baseCss != false ? css : ""}
      webScript={js}
      onMessage={(event) => {
        const message = JSON.parse(
          event.nativeEvent?.data || "{type: '', data: {}}"
        );
        onMessage && onMessage(message);
      }}
    />
  );
}

创建 WebViewAutoHeight.tsx

用于替代 import AutoHeightWebView from “react-native-autoheight-webview”
注意 react-native-autoheight-webview 加载内容动态变化时存在闪问题

import WebView, { WebViewProps } from "react-native-webview";
import { StyleProp, ViewStyle, View } from "react-native";
import { useState } from "react";
import { WebViewSourceHtml } from "react-native-webview/lib/WebViewTypes";

export interface SizeUpdate {
  width: number;
  height: number;
}

export interface Props extends WebViewProps {
  onSizeUpdated?: (size: SizeUpdate) => void;
  style?: StyleProp<ViewStyle>;
  webScript?: string;
  webStyle?: string;
}

export default function WebViewAutoHeight({
  style,
  source,
  webScript,
  webStyle,
  onMessage,
  onSizeUpdated,
}: Props) {
  let [height, setHeight] = useState(0);

  // size更新
  const sizeUpdateScript = `
    (function () {
        getCurrentSize();
        function getCurrentSize() {
            let lastHeight = null;
            let lastWidth = null;
            const fun = () => {
                const currentHeight = document.getElementById("webview_root_html").offsetHeight;
                const currentWidth = document.getElementById("webview_root_html").offsetHeight;
                if (lastHeight !== currentHeight) {
                if (window.ReactNativeWebView?.postMessage) {
                    window.ReactNativeWebView.postMessage(JSON.stringify({
                        type: "size",
                        data: { height: currentHeight, width: currentWidth },
                    }));
                }
                lastHeight = currentHeight;
                lastWidth = currentWidth;
                }
            };
            setInterval(fun, 500);
            fun();
        }
    })();
  `;

  // 注入html、css、js
  const injectHtml = (html: string) => {
    return `
        <!DOCTYPE html>
            <html>
            <head>
            <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
            <style>
                html, body {
                    margin: 0;
                    padding: 0;
                    width: 100%;
                    height: 100%;
                    overflow: hidden;
                }

                ${webStyle}
            </style>
            </head>
            <body>
            <div id="webview_root_html">
                ${html}
            </div>
            <script>
                ${sizeUpdateScript}
                ${webScript}
                true;
            </script>
            </body>
        </html>
    `;
  };

  return (
    <View style={[{ width: "100%", height: height || "100%" }]}>
      <WebView
        style={[{ width: "100%", height: "100%" }, style]}
        source={
          (source as WebViewSourceHtml)?.html != undefined
            ? { html: injectHtml((source as WebViewSourceHtml).html) }
            : source
        }
        scalesPageToFit={false}
        onMessage={(event) => {
          const message = JSON.parse(
            event.nativeEvent?.data || "{type: '', data: {}}"
          );
          // console.log("Received from web:", message);
          if (message && message.type && message.data) {
            // 尺寸更新
            if (message.type == "size") {
              setHeight(message.data?.height);
              onSizeUpdated && onSizeUpdated(message.data);
            } else {
              onMessage && onMessage(event);
            }
          } else {
            onMessage && onMessage(event);
          }
        }}
      />
    </View>
  );
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值