听汐AI说头像
关注
iOS 软键盘顶起与安全区域避坑:移动端 Web 视口适配方案封面图

iOS 软键盘顶起与安全区域避坑:移动端 Web 视口适配方案

iOS 软键盘顶起与安全区域避坑:移动端 Web 视口适配方案

封面信息图

在做移动端 Web App、手账工具或在线编辑器时,最让前端工程师头皮发麻的莫过于 iOS Safari 上的两个“视觉破坏王”

  1. 底部安全区域(Safe Area)与黑条遮挡:iPhone 底部的 Home Indicator 黑条会把页面最底部的按钮直接盖住。
  2. 软键盘弹出时的视口灾难(Keyboard Viewport Squish):当用户点击输入框唤起虚拟键盘时,Safari 会机械地把整个网页向上滚动并发生奇怪的视口压缩,导致原本使用 fixed 固定在底部的操作栏悬浮在屏幕正中央,或者把输入框本身顶出屏幕外。

今天我把在真机上经过几十轮测试提炼出的移动端 Web 视口与软键盘适配完整避坑指南公开出来。

+--------------------------------------------------------------------+
|               移动端 Web 视口与软键盘适配避坑图解                  |
+--------------------------------------------------------------------+
| [HTML viewport 声明: viewport-fit=cover]                           |
|       |                                                            |
|       v (解锁全屏安全区域 safe-area-inset)                          |
| [CSS 容器: min-height: 100dvh + padding-bottom: env(safe-area...)]  |
|       |                                                            |
|       v (监听 window.visualViewport 动态视口高度)                   |
| [软键盘唤起时: 动态修正 fixed 定位底边距,消除虚浮与错位]            |
+--------------------------------------------------------------------+

1. 基础门票:viewport-fit=cover 声明

index.html<head> 中,必须显式声明 viewport-fit=cover,浏览器才会允许页面内容延伸到刘海屏与底部黑条区域:

<meta 
  name="viewport" 
  content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" 
/>

配合 CSS 环境变量使用:

/* 确保底部悬浮栏避开 iPhone 底部操作条 */
.bottom-bar {
  padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
}

2. 终极解法:Visual Viewport API 监听真实键盘高度

在 iOS 13+ 现代浏览器中,window.innerHeight 在软键盘弹出时并不会实时变化,导致传统的监听 resize 事件失效。

真正能够精准捕获软键盘弹出并计算真实可用高度的是 window.visualViewport(可视视口 API)

import { useEffect, useState } from "react";

export function useVisualViewport() {
  const [viewportHeight, setViewportHeight] = useState<number>(() =>
    typeof window !== "undefined" && window.visualViewport
      ? window.visualViewport.height
      : typeof window !== "undefined"
      ? window.innerHeight
      : 800
  );

  const [keyboardHeight, setKeyboardHeight] = useState<number>(0);

  useEffect(() => {
    if (!window.visualViewport) return;

    const handleResize = () => {
      const vh = window.visualViewport!.height;
      const windowH = window.innerHeight;
      setViewportHeight(vh);
      // 计算键盘弹出的实际像素高度
      setKeyboardHeight(Math.max(0, windowH - vh));
    };

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

    return () => {
      window.visualViewport?.removeEventListener("resize", handleResize);
      window.visualViewport?.removeEventListener("scroll", handleResize);
    };
  }, []);

  return { viewportHeight, keyboardHeight };
}

3. 在 React 编辑器组件中的动态定位

有了 keyboardHeight,我们可以直接将底部操作栏的 bottom 偏移量动态绑定到真实键盘上边缘:

import React from "react";
import { useVisualViewport } from "@/hooks/useVisualViewport";

export const MobileJournalEditor: React.FC = () => {
  const { keyboardHeight } = useVisualViewport();

  return (
    <div className="flex flex-col min-h-screen bg-[#FAF7F0]">
      <textarea
        placeholder="写下当下的碎碎念..."
        className="flex-1 p-6 bg-transparent resize-none outline-none text-base text-[#2D2B2A]"
      />

      {/* 底部随键盘平滑同步贴合的操作栏 */}
      <div
        style={{ transform: `translateY(-${keyboardHeight}px)` }}
        className="fixed bottom-0 left-0 right-0 p-4 bg-[#FAF7F0]/95 backdrop-blur-md border-t-2 border-[#2D2B2A] flex justify-between items-center transition-transform duration-100 ease-out"
      >
        <span className="text-xs text-stone-500">🌿 听汐手账输入中</span>
        <button className="px-4 py-1.5 bg-[#88AA99] text-white text-xs font-bold rounded-full border-2 border-[#2D2B2A] shadow-[2px_2px_0px_#2D2B2A]">
          完成
        </button>
      </div>
    </div>
  );
};

把移动端的各种边缘畸变抹平,让 Web 应用在手机上呈现出丝毫不输原生 App 的精致与笃定。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/weixin_63764436/article/details/164465640

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--