前端兼容性问题 - 屏幕尺寸兼容

写在前面

前端开发最让人头疼的莫过于兼容性问题,继上一篇 前端兼容性问题 - CSS、JS 兼容 之后,这一篇主要研究并解决屏幕尺寸的兼容性问题

目录:

  • 当前现状
  • 解决方案
  • 模拟设备测试方案

当前现状

Android 设备情况

根据OpenSignal 在 2015 年 8 月发布的基础统计数据可以知道Andoroid的设备复杂度:ANDROID FRAGMENTATION VISUALIZED

iOS 设备情况

苹果公司目前已推出42款iPhone型号:维基百科

常见屏幕分辨率

下面列出桌面端、移动端、平板电脑端的一些常见的屏幕分辨率,更多屏幕分辨率查阅:屏幕尺寸大全

桌面端常见屏幕分辨率(显示器)

  1. 1280x720:720p,高清分辨率
  2. 1280x1024:常见于 4:3 屏幕比例的显示器
  3. 1366x768:现在非常常见的低分辨率显示器
  4. 1600x900:较高的分辨率,常见于中等尺寸显示器
  5. 1920x1080:1080p,全高清分辨率,现在非常常见,适用于大多数显示器
  6. 2560x1440:2K 分辨率,适用于较大尺寸或高质量显示器
  7. 3840x2160:4K 分辨率,适用于高端大尺寸显示器

移动端常见屏幕分辨率(智能手机)

  1. iPhone 6、iPhone 6s、iPhone 7、iPhone 8 和 iPhone SE(第二代)的分辨率
  2. 828x1792:iPhone XR 和 iPhone 11 的分辨率
  3. 1080x1920:许多 Android 设备的分辨率
  4. 1125x2436:iPhone X、iPhone XS 和 iPhone 11 Pro 的分辨率
  5. 1170x2532:iPhone 12 和 iPhone 12 Pro 的分辨率
  6. 1440x2560:许多高端 Android 设备的分辨率

平板电脑端常见屏幕分辨率

  1. 1536x2048:iPad 的 Retina 分辨率,如 iPad 3、iPad 4、iPad Air 和 iPad mini(2 代及以后)
  2. 1668x2224:iPad Air(第三代)和 iPad Pro 10.5 英寸
  3. 2048x2732:iPad Pro 12.9 英寸
  4. 800x1280:许多 Android 平板电脑的分辨率
  5. 1200x1920:许多高端 Android 平板电脑的分辨率

屏幕纵横比

统计以上的屏幕分辨率得出多端各自的主流纵横比

桌面端 移动端 平板电脑端
主流纵横比 * 16 : 9 * 16 : 10 * 21 : 9 * 19 : 9 * 19.5 : 9 * 16 : 9 * 4 : 3 * 16 : 9

解决方案

设置 meta 标签

先了解:viewport

viewport 表示浏览器中用来显示网页的可视区域。viewport 包括以下三种:

  1. layout viewport

layout viewport 为网页布局的区域,它是 html 元素的父容器 ,只要不在 css 中修改 元素的宽度, 元素的宽度就会撑满 layout viewport 的宽度。

很多时候浏览器窗口没有办法显示出 layout viewport 的全貌 ,但是它确实是已经被加载出来了,这个时候滚动条就出现了,你需要通过滚动条来浏览 layout viewport 其他的部分 。

layout viewport 用 css 像素来衡量尺寸,在缩放、调整浏览器窗口的时候不会改变。缩放、调整浏览器窗口改变的只是 visual viewport。

在桌面浏览器中,缩放100% 的时候,Layout Viewport 宽度等于内容窗口的宽度。(你几乎不会在电脑上见过横向滚动条,除非你调整缩放 )

但是在移动端,缩放为 100% 的时候,Layout Viewport 不一定等于内容窗口的大小。当你用手机浏览浏览宽大的网页(这些网页没有采用响应式设计)的时候,你只能一次浏览网页的一个部分,然后通过手指滑动浏览其他部分。这就说明整个网页已经加载出来了,只不过你要一部分一部分地看。

  1. visual viewport

visual viewport 为视觉视口,就是显示在屏幕上的网页区域 ,它往往只显示 layout viewport 的一部分 visual viewport 就像一台摄像机,layout viewport 就像一张纸,摄像机对准纸的哪个部分,你就能看见哪个部分。你可以改变摄像机的拍摄区域大小(调整浏览器窗口大小 ),也可以调整摄像机的距离(调整缩放比例),这些方法都可以改变 visual viewport,但 layout viewport 始终不变

  1. ideal viewport

ideal viewport 为理想视口,不同的设备有自己不同的 ideal viewport,ideal viewport 的宽度等于移动设备的屏幕宽度,所以其是最适合移动设备的 viewport。只要在 css 中把某一元素的宽度设为 ideal viewport 的宽度(单位用 px ),那么这个元素的宽度就是设备屏幕的宽度了,也就是宽度为100% 的效果。 ideal viewport 的意义在于,无论在何种分辨率的屏幕下,那些针对ideal viewport 而设计的网站,不需要用户手动缩放,也不需要出现横向滚动条,都可以完美的呈现给用户。

利用 meta 标签控制 viewport

移动设备默认的是 layout viewport ,也就是那个比屏幕要宽的 viewport,但在进行移动端 H5 的开发时,我们需要的是 ideal viewport 。那么怎么才能得到 ideal viewport 呢?

我们在开发 h5 页面时,最经常见的标签如下所示

html 复制代码
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">

该 meta 标签的作用是让当前 viewport 的宽度等于设备的宽度,同时不允许用户手动缩放 。如果你不这样的设定的话,那就会使用那个比屏幕宽的默认 viewport(layout viewport),也就是说会出现横向滚动条。

相关的属性意义如下所示:

width 设置 layout viewport 的宽度,为一个正整数,或字符串 "width-device"
height 设置页面的初始缩放值,为一个数字,可以带小数
initial-scale 允许用户的最小缩放值,为一个数字,可以带小数
minimum-scale 允许用户的最大缩放值,为一个数字,可以带小数
maximum-scale 设置 layout viewport 的高度,这个属性对我们并不重要,很少使用
user-scalable 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes 代表允许

对比

页面在桌面端的预览效果

在移动端的预览效果【左图没有设置 viewport,右图设置了 viewport】

媒体查询 @media

媒体查询是 CSS3 中的一项特性,允许我们根据设备的特定条件(如窗口或视口的宽度、高度、设备的方向、分辨率等)来应用不同的样式。这使得我们的网页在各种设备(桌面电脑、平板、手机等)和窗口大小上都有良好的显示和交互体验,也就是响应式设计的关键。

一个基本的媒体查询语法如下:

css 复制代码
@media media_type and (media_feature) {
  /* 匹配时的 CSS 样式 */
}

其中:

  • media_type 是目标媒体设备的类型,如 screen, print 等。如果你不指定媒体类型,那意味着应用到所有媒体类型上(也就是 all)。
  • media_feature 是特定的设备特性,如 width, height等。
  • and 用于将多个媒体特性合并在一起,所有条件必须匹配,样式才会被应用。

媒体查询的具体使用可以查阅我之前写的文章:CSS系列 -- 媒体查询,在此不作赘述

自适应单位

自适应单位包括 rem,vw 和百分比%等

rem 单位

rem 是根据 根元素(html)字体大小 变化的相对单位。 rem 的全名是 root em ,其值是相对于根元素的字体大小计算的。这意味着调整根元素的字体大小后,所有使用 rem 单位的元素都会自动调整。介绍如下:

  1. rem 是基于根元素(即 html 元素)字体大小计算的
  2. 计算公式:1 rem = 根元素字体大小

示例:

HTML 复制代码
<!DOCTYPE html>
<html>
  <head>
    <style>
      html {
        font - size: 16px;
      }
      p {
        font - size: 1.25rem;
      }
    </style>
  </head>
  <body>
    <p>Hello, World!</p>
  </body>
</html>

在这个例子中,通过设置根元素字体大小为 16px,并为 <p> 元素设置颜色值为 1.25rem,计算出 <p> 的字体大小为 (16 * 1.25)px = 20px。

vw、vh 单位

vw 是基于视口宽度 的相对单位,vh 则是基于视口高度的相对单位。

视口的定义:web 页面能渲染的地方

其中 vw 是 viewport width 的缩写。vw 单位是基于浏览器窗口宽度的相对度量,可以让元素自适应调整,无论设备大小。介绍如下:

  1. vw 是基于视口的宽度计算的
  2. 计算公式:1 vw = 视口宽度的1%

示例:

CSS 复制代码
div {
  width: 50vw;
}

在这个例子中,将 <div> 的宽度设置为视口宽度的一半,即 50vw。如果视口宽度为 1200px,<div> 的宽度为 (1200 * 50/100)px = 600px。

rem 和 vw 的区别

  • "rem" 更适合用于字体大小、间距、边框等相对比较小的元素,因为这些元素大小的变化相对不会太大
  • "vw" 更适合用于相对于视口宽度而言较大的元素,例如页面的宽度或高度、容器的宽度等

百分比 % 单位

百分比单位%是相对于其父元素的尺寸来定义的。这意味着元素会根据其父元素的尺寸变化。这种单位充分利用了可用空间,可以实现自适应设计。介绍如下:

  1. % 是基于父元素的尺寸计算的
  2. 计算公式:1% = 父元素尺寸的1%

示例:

HTML 复制代码
<!DOCTYPE html>
<html>
  <head>
    <style>
      body {
        width: 800px;
      }
      div {
        width: 50%;
      }
    </style>
  </head>
  <body>
    <div>Hello, World!</div>
  </body>
</html>

在这个例子中,将 <div> 的宽度设置为其父元素(<body>)宽度的一半,即 50%。因此 <div> 的实际宽度是其父元素宽度的一半:(800 * 50/100)px = 400px

px 自动转换为 vw

设计师经常给宽度大小为 375px 或 750px 的视觉稿,单位是 px,手动转换起来就很头疼,所以可以采用社区提供的 postcss-px-to-viewport 插件,可以实现将单位 px 自动转换为 vw

  1. 安装插件
bash 复制代码
npm install postcss-px-to-viewport --save-dev
yarn add -D postcss-px-to-viewport
  1. 引入插件

我们项目用的是 React 框架 + umi 脚手架,所以参考 umi 官网文档 - API 之配置,使用 extraPostCSSPlugins 这个属性即可引入 postcss-px-to-viewport 插件

  1. 参数配置

参数配置示例:(其他配置项参考官网)

js 复制代码
import px2vw from 'postcss-px-to-viewport';

export default defineConfig({
  extraPostCSSPlugins: [
    px2vw({
      unitToConvert: 'px', // 要转化的单位
      viewportWidth: 750, // 视窗的宽度,可根据自己的需求调整(这里是以PC端为例)
      // viewportHeight: 1080, 		// 视窗的高度
      unitPrecision: 6, // 转换后的精度,即小数点位数
      propList: ['*'], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
      viewportUnit: 'vw', // 指定需要转换成的视窗单位,默认vw
      fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位,默认vw
      // selectorBlackList: ['wrap'],// 指定不转换为视窗单位的类名,
      minPixelValue: 0.1, // 默认值1,小于或等于1px则不进行转换
      mediaQuery: false, // 是否在媒体查询的css代码中也进行转换,默认false
      replace: true, // 是否转换后直接更换属性值
      exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配
      landscape: false, // 是否处理横屏情况
    }),
  ]
});
  1. 最终效果
less 复制代码
.invalid {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  position: fixed;
  top: -80px;
  left: 0;
  font-size: 32px;
}

响应式布局

flex 布局

参考 flex 布局的基本概念 - MDN 提供的定义:Flexible Box 模型 ,通常被称为 flexbox,是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布 和对齐能力 。我们说 flexbox 是一种一维的布局,是因为一个 flexbox 一次只能处理一个维度上的元素布局,一行或者一列。

flex 布局的使用可以查阅我以前写的文章:CSS系列 -- flex布局,在此不作赘述

grid 布局

参考 网格布局 - MDN 提供的定义:CSS 网格布局 擅长于将一个页面划分为几个主要区域,以及定义这些区域的大小、位置、层次等关系。像表格一样,网格布局让我们能够按行或列来对齐元素。然而在布局上,网格比表格更可能做到或更简单。例如,网格容器的子元素可以自己定位,以便它们像 CSS 定位的元素一样,真正的有重叠和层次。

grid 布局的使用也可以查阅我以前写的文章:CSS系列 -- grid布局,在此不作赘述

模拟设备测试方案

模拟桌面端屏幕尺寸

  1. 打开 Chrome 浏览器控制台,打开设备工具栏(Ctrl+Shift+M)
  1. 打开尺寸栏,选择【自适应】,拖动右下角即可随意设置屏幕尺寸宽高,模拟各种桌面端屏幕尺寸非常地简单。同时也可以直接输入宽高,更加精确

模拟移动端设备

为了模拟多端设备的纵横比,同样可以利用控制台来模拟这些设备

  1. 打开尺寸栏,点击【修改】
  1. 填写设备信息
  1. 保存设备后即可快速模拟各端的纵横比(这里只举例三个)

H5 会变"矮"?

细心的同学会发现:真机测试时在大多数移动端浏览器和 APP 端内打开的 H5 会变"矮"

这是由于大多数移动端浏览器和 APP 端内有固定高度的状态栏、标题栏、底部导航栏 ,影响了视口高度 ,所以在真机中打开的 H5 的纵横比要实际小于 上面计算出的纵横比,所以我们也要考虑到状态栏、标题栏、底部导航栏占掉的高度(特别是有一屏渲染的需求)

这里我推荐下载安装 微信开发者工具 来模拟真机效果

  1. 下载安装后打开微信开发者工具,选择公众号网页项目
  1. 把本地 dev 运行的链接直接贴进去,即可同时看到控制台打印信息和模拟器
  1. 自定义模拟器

同样的,我们也可以自定义模拟器的配置

可以看到,所有的模拟器状态栏 、标题栏都占用了 H5 的高度

而且当我们选择 iPhone X 及以上的苹果系统设备 模拟器时,还有苹果系统自带的底部安全区占用了 H5 的高度

不同的是:

  • 状态栏和标题栏"挤掉"了 H5 的高度
  • 苹果系统自带的底部安全区没有占用 H5 的高度,而是"覆盖"在 H5 上面的,所以我们要给 iPhone X 及以上的苹果系统留足一定的安全距离

值得一提的是:

  • 安卓系统真机自带的底部导航栏 也会"挤掉" H5 的高度(这在微信开发者工具的模拟器没有出现),所以不需要 H5 本身去兼容,只需要考虑到高度会变"矮"的情况即可

H5 给苹果系统留足安全距离

苹果系统中的一个可视窗口范围,受圆角、齐刘海、小黑条 影响,下图的蓝色区域才是 H5 (当然也包括 APP)的可操作区域:

也就是说,我们必须要保证页面可视、可操作区域到屏幕边界有一定的安全距离 。更详细说明参考文档:Human Interface Guidelines - iPhoneX

先了解:viewport-fit 属性

iOS11 新增特性,苹果公司为了适配 iPhoneX 对现有 viewport meta 标签的一个扩展,用于设置网页在可视窗口的布局方式,可设置三个值。

  • contain: 可视窗口完全包含网页内容(左图)
  • cover:网页内容完全覆盖可视窗口(右图)
  • auto:默认值,跟 contain 表现一致

需要注意:网页默认不添加扩展的表现是 viewport-fit=contain,需要适配 iPhoneX 必须设置 viewport-fit=cover,这是适配的关键步骤。更详细说明,参考文档:viewport-fit-descriptor

先了解:env()、constant() 函数

iOS11 新增特性,Webkit 的一个 CSS 函数,用于设定安全区域与边界的距离,有四个预定义的变量:

  • safe-area-inset-left:安全区域距离左边边界距离
  • safe-area-inset-right:安全区域距离右边边界距离
  • safe-area-inset-top:安全区域距离顶部边界距离
  • safe-area-inset-bottom:安全区域距离底部边界距离

这里我们只需要关注 safe-area-inset-bottom 这个变量,因为它对应的就是小黑条的高度(横竖屏时值不一样)。

注意:当 viewport-fit=contain 时 env() 是不起作用的,必须要配合 viewport-fit=cover 使用。对于不支持 env() 的浏览器,浏览器将会忽略它

需要注意的是之前使用的 constant() 在 iOS11.2 之后就不能使用的,但我们还是需要做向后兼容,像这样:

css 复制代码
padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom); /* 兼容 iOS >= 11.2 */

注意:env() 跟 constant() 需要同时存在 ,而且顺序不能换 。 更详细说明,参考文档:Designing Websites for iPhone X

方案

  1. 新增 viweport-fit 属性,使得页面内容完全覆盖整个窗口
html 复制代码
<meta name="viewport" content="width=device-width, viewport-fit=cover">
  1. 通过加内边距 padding 扩展高度:
css 复制代码
{
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}

注意,这个方案需要吸底条必须是有背景色的,因为扩展的部分背景是跟随外容器的,否则出现镂空情况。

还有一种方案就是,可以通过新增一个新的元素 (空的颜色块,主要用于小黑条高度的占位),然后吸底元素可以不改变高度只需要调整位置,像这样:

css 复制代码
{
  margin-bottom: constant(safe-area-inset-bottom);
  margin-bottom: env(safe-area-inset-bottom);
}

空的颜色块:

css 复制代码
{
  position: fixed;
  bottom: 0;
  width: 100%;
  height: constant(safe-area-inset-bottom);
  height: env(safe-area-inset-bottom);
  background-color: #fff;
}

参考文章

相关推荐
传人1 小时前
悬停旋转放大和位移效果如何写
前端·javascript·css
zhangzeyuaaa2 小时前
Ruby 方法参数完全指南:默认值、可变参数与关键字参数
前端·python·ruby
粥里有勺糖2 小时前
视野修炼-技术周刊第135期 | 鼠标跟随宠物
前端·github·aigc
粥里有勺糖2 小时前
拿 Codex 协助清理磁盘,Nice!
前端·后端·github
Highcharts.js2 小时前
可视化商用图表库对比,如何选择?
前端·javascript·学习·信息可视化·highcharts·前端可视化
TheITSea3 小时前
JavaScript Web APIs
开发语言·前端·javascript
恋猫de小郭3 小时前
Android CLI 支持 AI Agent 通过 Device Streaming 调试云真机
android·前端·flutter
Wang's Blog3 小时前
Java 项目实战: 外卖平台优化-前端dist部署与Nginx反向代理rewrite
java·前端·nginx
鬓戈3 小时前
Claude Code frontend-design 插件调研 与 Vue 旧系统风格一致性方案
前端·vue.js·人工智能
IT_陈寒3 小时前
SpringBoot自动配置的坑我帮你踩过了
前端·人工智能·后端