写在前面
前端开发最让人头疼的莫过于兼容性问题,继上一篇 前端兼容性问题 - CSS、JS 兼容 之后,这一篇主要研究并解决屏幕尺寸的兼容性问题
目录:
- 当前现状
- 解决方案
- 模拟设备测试方案
当前现状
Android 设备情况
根据OpenSignal 在 2015 年 8 月发布的基础统计数据可以知道Andoroid的设备复杂度:ANDROID FRAGMENTATION VISUALIZED
iOS 设备情况
苹果公司目前已推出42款iPhone型号:维基百科
常见屏幕分辨率
下面列出桌面端、移动端、平板电脑端的一些常见的屏幕分辨率,更多屏幕分辨率查阅:屏幕尺寸大全
桌面端常见屏幕分辨率(显示器)
- 1280x720:720p,高清分辨率
- 1280x1024:常见于 4:3 屏幕比例的显示器
- 1366x768:现在非常常见的低分辨率显示器
- 1600x900:较高的分辨率,常见于中等尺寸显示器
- 1920x1080:1080p,全高清分辨率,现在非常常见,适用于大多数显示器
- 2560x1440:2K 分辨率,适用于较大尺寸或高质量显示器
- 3840x2160:4K 分辨率,适用于高端大尺寸显示器
移动端常见屏幕分辨率(智能手机)
- iPhone 6、iPhone 6s、iPhone 7、iPhone 8 和 iPhone SE(第二代)的分辨率
- 828x1792:iPhone XR 和 iPhone 11 的分辨率
- 1080x1920:许多 Android 设备的分辨率
- 1125x2436:iPhone X、iPhone XS 和 iPhone 11 Pro 的分辨率
- 1170x2532:iPhone 12 和 iPhone 12 Pro 的分辨率
- 1440x2560:许多高端 Android 设备的分辨率
平板电脑端常见屏幕分辨率
- 1536x2048:iPad 的 Retina 分辨率,如 iPad 3、iPad 4、iPad Air 和 iPad mini(2 代及以后)
- 1668x2224:iPad Air(第三代)和 iPad Pro 10.5 英寸
- 2048x2732:iPad Pro 12.9 英寸
- 800x1280:许多 Android 平板电脑的分辨率
- 1200x1920:许多高端 Android 平板电脑的分辨率
屏幕纵横比
统计以上的屏幕分辨率得出多端各自的主流纵横比
桌面端 | 移动端 | 平板电脑端 | |
---|---|---|---|
主流纵横比 | * 16 : 9 * 16 : 10 * 21 : 9 | * 19 : 9 * 19.5 : 9 * 16 : 9 | * 4 : 3 * 16 : 9 |
解决方案
设置 meta 标签
先了解:viewport
viewport 表示浏览器中用来显示网页的可视区域。viewport 包括以下三种:
- layout viewport
layout viewport 为网页布局的区域,它是 html 元素的父容器 ,只要不在 css 中修改 元素的宽度, 元素的宽度就会撑满 layout viewport 的宽度。
很多时候浏览器窗口没有办法显示出 layout viewport 的全貌 ,但是它确实是已经被加载出来了,这个时候滚动条就出现了,你需要通过滚动条来浏览 layout viewport 其他的部分 。
layout viewport 用 css 像素来衡量尺寸,在缩放、调整浏览器窗口的时候不会改变。缩放、调整浏览器窗口改变的只是 visual viewport。
在桌面浏览器中,缩放100% 的时候,Layout Viewport 宽度等于内容窗口的宽度。(你几乎不会在电脑上见过横向滚动条,除非你调整缩放 )
但是在移动端,缩放为 100% 的时候,Layout Viewport 不一定等于内容窗口的大小。当你用手机浏览浏览宽大的网页(这些网页没有采用响应式设计)的时候,你只能一次浏览网页的一个部分,然后通过手指滑动浏览其他部分。这就说明整个网页已经加载出来了,只不过你要一部分一部分地看。
- visual viewport
visual viewport 为视觉视口,就是显示在屏幕上的网页区域 ,它往往只显示 layout viewport 的一部分 visual viewport 就像一台摄像机,layout viewport 就像一张纸,摄像机对准纸的哪个部分,你就能看见哪个部分。你可以改变摄像机的拍摄区域大小(调整浏览器窗口大小 ),也可以调整摄像机的距离(调整缩放比例),这些方法都可以改变 visual viewport,但 layout viewport 始终不变
- 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
单位的元素都会自动调整。介绍如下:
rem
是基于根元素(即html
元素)字体大小计算的- 计算公式:
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
单位是基于浏览器窗口宽度的相对度量,可以让元素自适应调整,无论设备大小。介绍如下:
vw
是基于视口的宽度计算的- 计算公式:
1 vw = 视口宽度的1%
示例:
CSS
div {
width: 50vw;
}
在这个例子中,将 <div>
的宽度设置为视口宽度的一半,即 50vw
。如果视口宽度为 1200px
,<div>
的宽度为 (1200 * 50/100)px = 600px
。
rem 和 vw 的区别
- "rem" 更适合用于
字体大小、间距、边框
等相对比较小的元素,因为这些元素大小的变化相对不会太大- "vw" 更适合用于相对于视口宽度而言较大的元素,例如页面的宽度或高度、
容器的宽度
等
百分比 % 单位
百分比单位%
是相对于其父元素的尺寸来定义的。这意味着元素会根据其父元素的尺寸变化。这种单位充分利用了可用空间,可以实现自适应设计。介绍如下:
%
是基于父元素的尺寸计算的- 计算公式:
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
- 安装插件
bash
npm install postcss-px-to-viewport --save-dev
yarn add -D postcss-px-to-viewport
- 引入插件
我们项目用的是 React 框架 + umi 脚手架,所以参考 umi 官网文档 - API 之配置,使用 extraPostCSSPlugins 这个属性即可引入 postcss-px-to-viewport 插件
- 参数配置
参数配置示例:(其他配置项参考官网)
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, // 是否处理横屏情况
}),
]
});
- 最终效果
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布局,在此不作赘述
模拟设备测试方案
模拟桌面端屏幕尺寸
- 打开 Chrome 浏览器控制台,打开设备工具栏(Ctrl+Shift+M)
- 打开尺寸栏,选择【自适应】,拖动右下角即可随意设置屏幕尺寸宽高,模拟各种桌面端屏幕尺寸非常地简单。同时也可以直接输入宽高,更加精确
模拟移动端设备
为了模拟多端设备的纵横比,同样可以利用控制台来模拟这些设备
- 打开尺寸栏,点击【修改】
- 填写设备信息
- 保存设备后即可快速模拟各端的纵横比(这里只举例三个)
H5 会变"矮"?
细心的同学会发现:真机测试时在大多数移动端浏览器和 APP 端内打开的 H5 会变"矮"
这是由于大多数移动端浏览器和 APP 端内有固定高度的状态栏、标题栏、底部导航栏 ,影响了视口高度 ,所以在真机中打开的 H5 的纵横比要实际小于 上面计算出的纵横比,所以我们也要考虑到状态栏、标题栏、底部导航栏占掉的高度(特别是有一屏渲染的需求)
这里我推荐下载安装 微信开发者工具 来模拟真机效果
- 下载安装后打开微信开发者工具,选择公众号网页项目
- 把本地 dev 运行的链接直接贴进去,即可同时看到控制台打印信息和模拟器
- 自定义模拟器
同样的,我们也可以自定义模拟器的配置
可以看到,所有的模拟器状态栏 、标题栏都占用了 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
方案
- 新增 viweport-fit 属性,使得页面内容完全覆盖整个窗口
html
<meta name="viewport" content="width=device-width, viewport-fit=cover">
- 通过加内边距 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;
}