移动端适配原理-解决网页内容自适应屏幕尺寸进行显示的问题

1,238 阅读9分钟

概述

通过amfe-flexible和pxtorem来实现,两者的功能分别是动态设置根元素大小为设备宽的1/10配置px转化rem的基数,编译时把px计算为rem

背景

在做移动端app内展示插件时,不同尺寸屏幕适配踩坑。 设计稿一般按750的尺寸设计。但移动终端设备种类繁多,物理分辨率、逻辑分辨率不尽相同,相同逻辑像素(px)展示大小也不同。做好适配逻辑,好的实现方案,可以在后续开发阶段,直接按照设计图尺寸用px进行布局,省心省事,这是目的

像素和分辨率

像素(Pel, pixel, pictureelement),为组成一幅图像的全部亮度和色度的最小图像单元。电视的图像是由按一定间隔排列的亮度不同的像点构成的,形成像点的单位也就是像素,组成图像的最小单位就是像素。注意每个像素的大小是不固定的,根据设备的分辨率决定的

屏幕分辨率是指纵横向上的像素点数,单位是 px。屏幕分辨率确定计算机屏幕上显示多少信息的设置,以水平和垂直像素来衡量。就相同大小的屏幕而言,当屏幕分辨率低时(例如 640 x 480),在屏幕上显示的像素少,单个像素尺寸比较大。屏幕分辨率高时(例如 1600 x 1200),在屏幕上显示的像素多,单个像素尺寸比较小。

设备物理分辨率(设备像素)

物理分辨率也叫设备像素,物理分辨率是LED显示屏显示的图像原始分辨率。从最初的颗粒感相当大的屏幕,到 720p 再到 1080p再到2k、4k,物理分辨率在变得原来越大。(很多手机支持手动调节分辨率)

 逻辑分辨率(设备独立像素)

微信图片_20220401235221.png

谷歌浏览器开发者工具, xr的这个414x896 这个就是设备独立像素,实际上的物理分辨率要比这个高。

iOSAndroidReact Native开发中样式单位其实都使用的是设备独立像素。

iOS的尺寸单位为ptAndroid的尺寸单位为dp

乔布斯在 iPhone4 的发布会上首次提出了 Retina Display(视网膜屏幕)的概念,在 iPhone4 使用的视网膜屏幕中,把 2x2 个像素当 1 个像素使用,这样让屏幕看起来更精致,但是元素的大小却不会改变。从此以后高分辨率的设备,多了一个逻辑像素。这些设备逻辑像素的差别虽然不会跨度很大,但是仍然有点差别,于是便诞生了移动端页面需要适配这个问题。

设备像素比

设备像素比device pixel ratio简称dpr,即物理像素和设备独立像素的比值。

web中,浏览器为我们提供了window.devicePixelRatio来帮助我们获取dpr。 实际上,从苹果提出视网膜屏幕开始,才出现设备像素比这个概念,因为在这之前,移动设备都是直接使用物理像素来进行展示。(物理像素和设备独立像素是1比1)

WEB端开发

在写CSS时,我们用到最多的单位是px,即CSS像素,当页面缩放比例为100%时,一个CSS像素等于一个设备独立像素。

但是CSS像素是很容易被改变的,当用户对浏览器进行了放大,CSS像素会被放大,这时一个CSS像素会跨越更多的物理像素。

页面的缩放系数 = CSS像素 / 设备独立像素

视口

视口(viewport)代表当前可见的计算机图形区域。在Web浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的UI, 菜单栏等——即指你正在浏览的文档的那一部分。

一般我们所说的视口共包括三种:布局视口、视觉视口和理想视口

布局视口

c6b0d44cc2e8245a87c654ba27e1d382.png

布局视口(layout viewport):当我们以百分比来指定一个元素的大小时,它的计算值是由这个元素的包含块计算而来的。当这个元素是最顶级的元素时,它就是基于布局视口来计算的。

所以,布局视口是网页布局的基准窗口,在PC浏览器上,布局视口就等于当前浏览器的窗口大小(不包括borders 、margins、滚动条)。

在移动端,布局视口被赋予一个默认值,大部分为980px,这保证PC的网页可以在手机浏览器上呈现,但是非常小,用户可以手动对网页进行放大。

我调用document.documentElement.clientWidth / clientHeight来获取布局视口大小。

视觉视口

267d366aa32fe448550d488e1e2e4443.png

视觉视口(visual viewport):用户通过屏幕真实看到的区域。

视觉视口默认等于当前浏览器的窗口大小(包括滚动条宽度)。

当用户对浏览器进行缩放时,不会改变布局视口的大小,所以页面布局是不变的,但是缩放会改变视觉视口的大小。

例如:用户将浏览器窗口放大了200%,这时浏览器窗口中的CSS像素会随着视觉视口的放大而放大,这时一个CSS像素会跨越更多的物理像素。

所以,布局视口会限制你的CSS布局而视觉视口决定用户具体能看到什么。

通过调用window.innerWidth / innerHeight来获取视觉视口大小。

理想视口

e5a22edca2209449dfb2f7013fc9949c.png

布局视口在移动端展示的效果并不是一个理想的效果,所以理想视口(ideal viewport)就诞生了:网站页面在移动端展示的理想大小。

如上,在浏览器调试移动端时页面上给定的像素大小就是理想视口大小,单位正是设备独立像素。

介绍CSS像素时提到页面的缩放系数 = CSS像素 / 设备独立像素,实际上说页面的缩放系数 = 理想视口宽度 / 视觉视口宽度更为准确。

页面缩放比例为100%时,CSS像素 = 设备独立像素理想视口 = 视觉视口

调用screen.width / height来获取理想视口大小。

适配原理

Meta viewport设置

<meta> 可以告诉浏览器如何解析页面。

借助<meta>元素的viewport设置视口、缩放等,移动端得到更好的展示效果。

<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="telephone=no" name="format-detection">

第一个meta标签表示:强制让文档的宽度(viewport宽度)与设备的宽度保持1:1,并且文档最大的宽度比例是1.0,且不允许用户点击屏幕放大浏览;

initial-scale - 初始的缩放比例
minimum-scale - 允许用户缩放到的最小比例
maximum-scale - 允许用户缩放到的最大比例
user-scalable - 用户是否可以手动缩放,这里有的资料写成no有的写成0

第二个meta标签是iphone设备中的safari私有meta标签,它表示:允许全屏模式浏览;

第三个meta标签也是iphone的私有标签,它指定的iphone中safari顶端的状态条的样式;

第四个meta标签表示:告诉设备忽略将页面中的数字识别为电话号码。

设置的含义和目的

为了在移动端让页面获得更好的显示效果,我们必须让布局视口、视觉视口都尽可能等于理想视口。

device-width就等于理想视口的宽度,所以设置width=device-width就相当于让布局视口等于理想视口。

由于initial-scale = 理想视口宽度 / 视觉视口宽度,所以我们设置initial-scale=1;就相当于让视觉视口等于理想视口。

这时,1个CSS像素就等于1个设备独立像素,而且我们也是基于理想视口来进行布局的,所以呈现出来的页面布局在各种设备上都能大致相似。

适配的最终代码实现

步骤一

通过监听onresize事件,动态更改根元素大小。一般设为屏幕宽clientWidth(设备独立像素)的1/10。(注意前提meta中设置width=device-width、initial-scale=1让三个视口为1:1:1,这时,1个CSS像素px就等于1个设备独立像素)

  function setRemUnit () {
    var rem = docEl.clientWidth / 10
    docEl.style.fontSize = rem + 'px'
  }

  setRemUnit()

  // reset rem unit on page resize
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function (e) {
    if (e.persisted) {
      setRemUnit()
    }
  })

上面是amfe-flexible的部分源码。也可不用amfe-flexible插件,将上面逻辑单独写个js引入即可

步骤二

安装postcss-pxtorem,设置postcss。通过 .postcssrc 或任何 postcss-load-config 支持的配置源来配置 PostCSS。也可以通过 vue.config.js 中的 css.loaderOptions.postcss 配置 postcss-loader。 默认开启autoprefixer(自动添加浏览器前缀的 PostCss 插件)。

根据设计图来定rootValue,第一步已将跟元素设为宽的1/10, 那750设计稿,根元素就是75px(要判断是否是vant文件,是就要使用37.5(因为vant使用的设计标准为375 但是市场现在的主流设置尺寸是750)

 'postcss-pxtorem':{
      rootValue(val){
        return val.file.indexOf('vant')===-1?75:37.5;
        //要判断是否是vant文件,是就要使用37.5(因为vant使用的设计标准为375 但是市场现在的主流设置尺寸是750)
      },
      propList:['*']
    }

这样,编译后浏览器渲染的单位就是计算之后的rem; 或者在vscode中安装px转rem的插件,编码阶段就计算为rem

注意事项

js动态设置的px和内联的px不能转出rem

原因: px2rem只会将css中px编译为rem,配合js根据不同的dpr,修改meta的viewport值和html的font-size

解决办法:

1.对于已知的样式,写成动态class形式;或者自定义loader,匹配所有行内样式进行转换

2.未知的样式,可以采用px/htmlSize的形式换算成rem

参考文章: blog.csdn.net/weixin_3985…

blog.csdn.net/pcaxb/artic…

blog.csdn.net/qq_29438877…