移动端适配(Mobile Terminal Adaptation)

CSS

由 Whiskeyi 于 2022-02-28 发布
全文 2.3k 字, 阅读约需 8 分钟
浏览

移动端适配(mobile terminal adaptation)

前言

目前移动端的屏幕大小各异,市面上各种尺寸的机型都有,而且有 1 倍屏,2 倍屏,3 倍屏之分,这对前端界面在不同设备下的呈现效果提出挑战,我们希望找到一种完美适配各种机型的方案(元素和字体能够保持一定比例随着用户屏幕尺寸变化),在用户面前呈现想要的效果。下面这篇文章聊聊关于前端开发中移动端适配的问题。

几个基本概念

屏幕大小

屏幕对角的的长度。1 英寸 = 2.54 厘米,那么一个 5 寸的手机的对角线长度就是 5 X 2.54 = 12.7 厘米。

分辨率

屏幕的像素点个数,例如:一个屏幕的分辨率为 320 * 480 即这个屏幕上有 320 X 480 个像素点。

px(pixels)

1px = 1inch * 1/96 (在一臂观察距离下,20inch)。 px 像素是 Web 开发中常用的单位。 1px 代表屏幕上一个物理像素点,在不同分辨率的设备上像素点的大小不同,同尺寸的屏幕分辨率越高,像素点越小,反之越大。

pt(point)

是一个标准的长度单位,1pt = 1 / 72英寸, ios 开发使用的单位。 pt 同时也是印刷行业常用单位,能够使用测量设备测得的长度。

ppi(pixels per inch)

即像素密度,它标志每英寸屏幕上有多少个设备像素点,ppi 越大,屏幕的分辨率越高,显示画面细节越丰富。计算公式为:$\frac{\sqrt{(W^2+H^2)}}{S}$,其中 W 和 H 是分辨率的宽高, S 是屏幕尺寸。

dpi(dot per inch)

dpi 和 ppi 的概念相似,指打印设备每英寸印刷出来的点有多少个,值越高,图片越细腻。

dip(device independent pixels)

dp ,是设备独立像素,也叫逻辑像素,又称密度无关像素。它是一个逻辑单位,即无论图形在屏幕上如何缩放,它们始终都有一个不变的逻辑尺寸。不同坐标系或不同系统,会有自己对应的设备独立像素。

dpr(devicePixelRatio)

设备物理像素和设备独立像素比,即$dpr =\frac{物理像素}{css像素}$是指在理想布局宽度,使用多少个物理像素来渲染一个 css 像素。 dpr 越高,屏幕的单位尺寸内设备像素越多,显示内容越细腻。

相关获取

  1. 屏幕的设备独立宽度
1
const dipWidth = screen.width;
  1. 屏幕的设备独立宽度
1
const dipHight = screen.height;
  1. dpr
1
const dpr = window.devicePixelRatio;
  1. 物理像素宽度
1
const physicalWidth = dipWidth \* dpr;

css 中,通过 css 中通过-webkit-device-pixel-ratio,-webkit-min-device-pixel-ratio,-webkit-max-device-pixel-ratio进行媒体查询。

  1. 物理像素高度
1
const physicalHight = dipHeight * dpr;

关于视口(viewport)

移动端涉及布局视口(Layout Viewport)、视觉视口(Visual ViewPort)和理想视口(Ideal ViewPort)

  • 布局视口(layout viewport): 用于网⻚的布局。可以看作是 html 元素的上一级容器即顶级容器,默认情况或者将 html 元素的 width 属性设为 100% 时,会占满这个顶级容器,此时用document.documentElement.clientWidth获取到 html 元素的布局宽度也就是布局视口的宽度,使用媒体查询时 max-width 和 min-width 的值指的也是布局视口的宽。无论移动端浏览器是否进行了缩放、横纵切换,在⻚面初始化后,这个视口的大小将固定不变。

layout viewport 并不是整个网⻚内容,它只是移动端适配手机小屏的逻辑视口。布局视口的高度并不等于整个网⻚的高度,同时 position: fixed 的定位是基于布局视口的。

  • 可视视口(visual viewport): 用于展示当前屏幕内容。滑动屏幕、横纵切换、缩放操作等,实际上都是对可视视口的操作。

相关操作:滑动⻚面 -> 移动 visual viewport 的坐标;缩放⻚面 -> 修改 visual viewport 的宽高;横纵屏切换 -> 调换 visual viewport 的宽高,并映射。

  • 理想视口(Ideal ViewPort): 是屏幕分辨率的值,即对设备来说是最理想的布局视口,用户不需要对页面进行缩放就能完美的显示整个页面。最简单的做法就是使布局视口宽度设置为手机屏幕的宽度。通过设置<meta name = "viewport" content = "width = device-width, initial-scale = 1.0">实现。

关于<meta>标签

<meta>标签中定义了一些元数据信息,通过设置<meta name = "viewport">,提供有关视口初始大小的信息,供移动设备使用。属性值为:

属性 属性值 描述
width 数值 / device-width 视口宽度
height 数值 / device-height 视口高度
initial-scale 0.0 ~ 10.0 设备宽度与视口大小间缩放比例(初始值)
maximum-scale 0.0 ~ 10.0 缩放最大值
minimum-scale 0.0 ~ 10.0 缩放最小值
user-scalable Boolean 类型 能否缩放页面,默认yes|1

适配方案

多媒体查询@media

多媒体查询@media:通过给不同分辨率的设备编写不同的样式实现响应式布局,解决不同设备不同分辨率之间的兼容,一般是 PC、平板、手机设备之间较大的分辨率差异。优点是能够实现不仅仅是样式伸缩变换的样式改变。缺点是要匹配足够多的设备与屏幕,工作量大;达到断点变化明显,用户体验不好。

1
2
3
4
5
6
7
8
9
10
11
12
// 屏幕可视窗口尺寸小于 480 像素时 font-size: 16px
@media screen and (max-width: 480px) {
html {
font-size: 16px;
}
}
// 屏幕可视窗口尺寸大于 480 像素时 font-size: 24px;
@media screen and (min-width: 480px) {
html {
font-size: 24px;
}
}

vw、vh、vmin、vmax

vw、vh、vmin、vmaxvw:是 viewport width 的简写,是可视区域尺寸的相对单位,1vw 相当于 1% 的可视区域的宽度;vh: 是 viewport height 的简写,是可视区域尺寸的相对单位, 1vh 相当于 1% 的可视区域的高度;vmin: 当前 vh 和 vw 的最小值;vmax:当前 vh 和 vw 的最大值。使得元素能够随视口大小自适应调整,是纯 css 移动端适配方案,不存在脚本依赖问题。但是存在一些兼容性问题,在 Android 4.4 / iOS 8 以下不支持。

rem

rem:rem 是相对长度单位, rem 方案中的样式设计为相对于根元素(html) font-size 计算值的倍数。在布局时使用 rem 单位布局,达到自适应的目的,实现弹性布局。但是 rem 布局存在以下问题:不是纯 css 移动适配方案,需要引入 js 脚本 在头部内嵌一段 js 脚本监听分辨率的变化来动态改变根元素的字体大小,css 样式和 js 代码有一定耦合,并且必须将改变 font-size 的代码放在 css 样式前。同时浏览器渲染最小单位是像素,元素根据屏幕宽度自适应,通过 rem 计算后可能会出现小数像素,浏览器会对这部分小数四舍五入,按照整数渲染。会出现小数像素的问题,我们可以指定最小转换像素,对于比较小的像素,不转换为 rem 或 vw 解决。

1
2
document.documentElement.style.fontSize =
document.documentElement.clientWidth / 750 + "px";

通过@media 改变 font-size 值,和使用 vw / wh 设置 font-size 的大小同样可以改变上面讲到的 rem 相对长度

px 自动转换为 vw

px自动转换为vw:设计师一般给宽度大小为 750px 的视觉稿,我们采用 vw 方案的话,需要将对应的元素大小单位 px 转换为 vw 单位,这是一项影响开发效率(需要手动计算将 px 转换为 vw)且不利于后续代码维护的工作。这时可以使用 postcss-px-to-viewport 插件,将 px 自动转换为 vw。

一些适配问题

  1. 1px 的问题:1 px 问题指的是在视觉设计师眼里的 1px 是指设备像素 1px,而如果我们直接写 css 的大小 1px,那在 dpr = 2 时,则等于 2px 设备像素,dpr = 3 时,等于 3px 设备像素。所以对于要求处理 1px 的场景,我们要进行特殊处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
// 解决方案,使用 transform: scale(0.5) + :before / :after
.calss {
position: relative;
&::after {
content: "";
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
border-top: 1px solid #666;
transform: scaleY(0.5);
}
}
  1. 图片高清的问题:适用普通屏的图片在高清屏中展示模糊,适用高清屏的图片在普通屏中,展示缺少色差、没有锐利度,并且浪费带宽。
1
2
3
4
5
6
7
8
9
10
11
/* 解决方案,对不同 dpr 屏幕使用不同 image 图片 */
[data-dpr="1"] .class {
background-image: url(image@1x.jpg);
}
[data-dpr="2"] .class {
background-image: url(image@2x.jpg);
}

[data-dpr="3"] .class {
background-image: url(image@3x.jpg);
}