React-h5输入的弹窗键盘内容超出屏幕怎么解决

314 阅读1分钟
import { useState, useEffect } from "react";

interface Options {
  height?: number;
  tapSelector?: string; // 多个用逗号隔开
  yScrollSelector?: string; // 纵向滚动条,多个用逗号隔开
  xScrollSelector?: string; // 横向滚动条,多个用逗号隔开
}

export default function useVisualViewport(options?: Options) {
  const [height, setHeight] = useState(options.height || 0);
  const [debugInfo, setDebugInfo] = useState([]);

  useEffect(() => {
    let pendingUpdate = false;

    function viewportHandler(event) {
      if (pendingUpdate) return;
      pendingUpdate = true;

      requestAnimationFrame(() => {
        pendingUpdate = false;

        setHeight(window.visualViewport.offsetTop);
      });
    }

    window.visualViewport.addEventListener("scroll", viewportHandler);
    window.visualViewport.addEventListener("resize", viewportHandler);

    var startY = 0;
    var minScrollTop = -Infinity;

    function handleTouchstart(e) {
      // setDebugInfo(['start', height]);
      // console.log('handleTouchstart', e)
      const tap = Array.from(document.querySelectorAll(options.tapSelector));
      const hasTap = tap.some((item) => item.contains(e.target));
      const yScroll = Array.from(
        document.querySelectorAll(options.yScrollSelector)
      );
      const yScrollContainer = yScroll.find((item) => item.contains(e.target));
      const xScroll = Array.from(
        document.querySelectorAll(options.xScrollSelector)
      )
      const xScrollContainer = xScroll.find((item) => item.contains(e.target));

      if (hasTap || xScrollContainer) {
        // 不做任何事情
      } else if (yScrollContainer) {
        startY = e.changedTouches[0].clientY;
        minScrollTop =
          yScrollContainer.clientHeight - yScrollContainer.scrollHeight + 1;
        // console.log('startY', startY, minScrollTop)
      } else {
        e.preventDefault();
      }
    }

    function handleTouchmove(e) {
      // console.log('handleTouchmove', e)
      const yScroll = Array.from(
        document.querySelectorAll(options.yScrollSelector)
      );
      const yScrollContainer = yScroll.find((item) => item.contains(e.target));
      const clientY = e.changedTouches[0].clientY;
      const end = clientY - startY;
      const xScroll = Array.from(
        document.querySelectorAll(options.xScrollSelector)
      )
      const xScrollContainer = xScroll.find((item) => item.contains(e.target));

      e.stopPropagation();

      if (xScrollContainer) {
        // 不做任何事情
      } else if (yScrollContainer) {
        var scrollTop = yScrollContainer.scrollTop;
        var clientHeight = yScrollContainer.clientHeight;
        var scrollHeight = yScrollContainer.scrollHeight;
        const isTextarea = yScrollContainer.tagName === "TEXTAREA";

        // setDebugInfo(['move', scrollTop, clientHeight, scrollHeight, minScrollTop]);
        // 下拉
        if (end > 0) {
          // 到顶了
          if (isTextarea ? scrollTop <= 0 : scrollTop < minScrollTop) {
            // console.log('到顶了')
            e.preventDefault();
          }
        } else if (end < 0) {
          // 到底了
          if (
            isTextarea ? scrollTop >= Math.abs(minScrollTop) : scrollTop === 0
          ) {
            // console.log('到底了')
            e.preventDefault();
          }
        }
      } else {
        e.preventDefault();
      }
    }

    window.addEventListener("touchstart", handleTouchstart, { passive: false });
    window.addEventListener("touchmove", handleTouchmove, { passive: false });

    return () => {
      window.visualViewport.removeEventListener("scroll", viewportHandler);
      window.visualViewport.removeEventListener("resize", viewportHandler);

      window.removeEventListener("touchstart", handleTouchstart);
      window.removeEventListener("touchmove", handleTouchmove);
    };
  }, []);

  return [height, debugInfo];
}

调用方法

const [height] = useVisualViewport({
  height: 0,
  // 如果弹出键盘还需要操作可以添加以下属性
  tapSelector: '.btn,.btn1', // 可点击区域, 多个用逗号分隔
  yScrollSelector: '.y-scroll,.y-scroll1', // 纵向滚动条, 多个用逗号分隔
  xScrollSelector: '.x-scroll,.x-scroll1' // 横向滚动条, 多个用逗号分隔
})
return <>
  <div style={{height: height + 'px'}} />
  <div className='container'>
  </div>
</>
body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.container {
  flex: 1;
  min-height: 0;
}