Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

熟悉又陌生的移动端适配 #24

Open
gauseen opened this issue Sep 9, 2020 · 0 comments
Open

熟悉又陌生的移动端适配 #24

gauseen opened this issue Sep 9, 2020 · 0 comments
Labels
原理 原理 面试 大厂面试

Comments

@gauseen
Copy link
Owner

gauseen commented Sep 9, 2020

移动端适配及原理

什么是移动端适配

在不同尺寸的移动设备上,让页面达到合理的展示或者说等比缩放展示

分辨率

  • 逻辑分辨率:它是软件和硬件(物理分辨率)之间的一个转换层,可通过 window.screen.width 获取宽度,视口大小就是参照该值进行缩放。
  • 物理分辨率:它是设备硬件固有的分辨率,出厂后就固定,可通过查看设备屏幕参数获取,也可通过 window.screen.width * window.devicePixelRatio 获取。

历代 iphone 分辨率:

设备 逻辑分辨率 物理分辨率 dpr = 物理分辨率 / 逻辑分辨率 PPI
iphone 3G 320 x 480 320 x 480 @1x 163
iphone 4/4s 320 x 480 640 x 960 @2x 163
iphone 5/5s 320 x 568 640 x 1136 @2x 326
iphone 6/6s 375 × 667 750 × 1334 @2x 326
iphone X/Xs 375 × 812 1125 × 2436 @3x 458

像素密度 PPI

像素密度 PPI (Pixel Per Inch) 是指每英寸的长度(2.54 cm)中容纳的像素个数。

PPI

问:一像素到底有多大?

答:1 像素长度 = 2.54 / PPI

例:1 像素长度(iphone 6) = 2.54 / 326 = 0.0779 mm = 头发直径(0.06 ~ 0.09 mm)

viewport 相关

概念

移动设备上的 viewport 就是设备屏幕上能用来显示网页的那一块区域,又叫「视口」,代码就是根据视口宽度进行排列布局的。但 viewport 又不仅仅是浏览器可视区域的大小,它可能比浏览器可视区域大,也可能比它小。
默认情况下,移动设备的 viewport 要大于浏览器可视区域,这主要是用来兼容显示 PC 端的网站(iPhone 默认宽度 980px)。

控制 viewport

通过 meta 标签控制 viewport 大小,下面代码是将 viewport 的宽度等于逻辑分辨率

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

width 和 initial-scale 的取值有冲突,width=device-widthinitial-scale=1.0 是等效的,这两个值都可以设置初始视口大小,如果同时设置时在 Safari 和部分 Android 浏览器上会选择使用较大值。各个浏览器表现可能不一致,为了统一应该只使用其中一个值,使用 initial-scale=1.0 更方便计算。

视口宽度 = 逻辑分辨率 / initial-scale

可以通过 document.documentElement.clientWidth 来获取 viewport 的宽度(不包括滚动条)。

移动端适配的方式

所谓的移动端适配,就是说页面内的元素宽高,是根据不同逻辑分辨率自动改变的。那什么样的长度单位能够满足呢?
答案是通过 vw 或 rem(需要 js 根据设备宽度计算得出值) 单位来达到适配的目的。

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

rem

兼容 IOS 4.1+, Android 2.1+ 可看这里

通过 meta 标签设置视口的宽度等于设备宽度,如下:

<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />

rem 这个单位代表根元素(通常为<html> 元素)的 font-size 大小,通过当前设备宽度来设置跟元素的字体大小,来达到移动端适配的目的。

一般是参照设计稿来进行开发,假如设计稿的宽度为 375px,那怎么通过 rem 来进行适配呢?

在不同尺寸的设备上,通过 js 按照设计稿的宽度等比例计算根元素的字体大小,这样才不会失真。

比如,设计稿的宽度为 375px 时的根字体大小为 100px 也就是 1rem,那当前设备下的根字体为多少?
因为是等比缩放,所以通过如下等式可计算得出当前根字体(currentRootFontSize)值。

设计稿根字体大小(随意设定) / 设计稿宽度 = 当前根字体大小 / 视口宽度

因为:100px / designWidth = currentRootFontSize / viewportWidth

所以:currentRootFontSize = viewportWidth * 100px / designWidth

用代码可表示为:

const designWidth = 375 // 设计稿宽度
const viewportWidth = document.documentElement.clientWidth // 当前视口宽度
document.documentElement.style.fontSize = (viewportWidth / designWidth) * 100 + 'px' // 根字体大小

上面是设备像素比(dpr)为 1 的计算方式。但是移动适配往往存在 1 像素问题,因为 px 是逻辑像素,根屏幕上的物理像素有可能不是 1:1 的关系,所以就有可能出现,1px 对应 2 个或多个物理像素的情况。

当 dpr > 1 时,同时想保证 1px 始终对应 1 个物理像素,那就需要让根字体扩大 dpr 的倍数,同时通过 meta 标签让视口缩小 1/dpr 倍即可。

例如:当 dpr = 2 时

<!-- 缩小视口 1/dpr (0.5) 倍 -->
<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, user-scalable=0" />

优点:

  • 使用 meta initial-scale 来缩放,解决了 1px 问题

缺点:

  • 需要额外的 js 逻辑来控制根字体大小
  • 逻辑不直观,需要根字体大小进行转换
  • initial-scale 进行视口缩放后,第三方 UI 库组件显示有影响,需要额外处理

采用 rem 进行移动端适配,是个历史过程,因为早期 rem 兼容性比 vw 好,但现在而言,项目没有要求太高兼容性的情况下,完全可以使用 vw 进行移动端适配。

vw

1vw 等于视口宽度的 1%,它是天生的移动端适配单位,无需多余转换。

兼容 IOS 8+, Android 4.4+ 可看这里

优点:

  • 无需引入多余 js 逻辑
  • 语义化,vw 逻辑清晰,不需要像 rem 那样需要换算一下

缺点:

  • 1px 问题需要额外处理

react 项目应用

postcss-px-to-viewport 是将 px 转换成视口单位(vw、vh)的 PostCss 插件

// 项目中配置

addPostcssPlugins([
  require('postcss-px-to-viewport')({
    viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度
  }),
]),

参考

@gauseen gauseen added 原理 原理 面试 大厂面试 labels Sep 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
原理 原理 面试 大厂面试
Projects
None yet
Development

No branches or pull requests

1 participant