ios fixed bottom定位键盘收起后底部有间隙未恢复

91 阅读1分钟

主要方式:使用 scrollTo(0,1) -> scrollTo(0,0) 技巧强制 Safari 重新计算 viewport`

import` `{ useEffect } from 'react'`

/**

 ``* 修复 iOS 键盘收起后页面不恢复的问题

 ``*

 ``* 问题:iOS 在键盘收起后,visualViewport.pageTop 不会自动归零,

 ``* 导致页面视觉上仍然被推上去

 ``*

 ``* 解决方案:

 ``* 1. 监听 visualViewport.resize 事件

 ``* 2. 检测键盘弹起/收起(viewport 高度变化超过 100px)

 ``* 3. 键盘弹起时:滚动到顶部消除空白

 ``* 4. 键盘收起时:使用 scrollTo(0,1) -> scrollTo(0,0) 技巧强制 Safari 重新计算 viewport

 ``*/

export const useIOSKeyboardFix = () => {

  ``useEffect(() => {

    ``if (``typeof window === ``'undefined' || !window.visualViewport) ``return

 

    ``let previousHeight = window.visualViewport.height

 

    ``const handleViewportResize = () => {

      ``const currentHeight = window.visualViewport!.height

      ``const heightDiff = currentHeight - previousHeight

 

      ``// 键盘弹起:高度减少超过 100px,滚动到顶部消除空白

      ``if (heightDiff < -100) {

        ``setTimeout(() => {

          ``window.scrollTo(0, 0)

          ``document.documentElement.scrollTop = 0

          ``document.body.scrollTop = 0

        ``}, 500)

      ``}

      ``// 键盘收起:高度增加超过 100px,重置 viewport 位置

      ``else if (heightDiff > 100) {

        ``requestAnimationFrame(() => {

          ``const viewportOffsetTop = window.visualViewport?.pageTop || 0

           

          ``if (viewportOffsetTop !== 0) {

            ``// 使用经典的 iOS scrollTo(0,1) -> scrollTo(0,0) 技巧强制 Safari 重新计算 viewport

            ``window.scrollTo(0, 1)

             

            ``requestAnimationFrame(() => {

              ``window.scrollTo(0, 0)

              ``document.documentElement.scrollTop = 0

              ``document.body.scrollTop = 0

            ``})

          ``} ``else {

            ``// viewport 正常,直接重置

            ``window.scrollTo(0, 0)

            ``document.documentElement.scrollTop = 0

            ``document.body.scrollTop = 0

          ``}

        ``})

      ``}

 

      ``previousHeight = currentHeight

    ``}

 

    ``window.visualViewport.addEventListener(``'resize'``, handleViewportResize)

 

    ``return () => {

      ``window.visualViewport?.removeEventListener(``'resize'``, handleViewportResize)

    ``}

  ``}, [])

`}``