简介

工欲善其事必先利其器,今天这篇文章主要讲解移动端H5开发必备的一些基础知识以及移动端适配和布局方案。如果已经看过这篇文章了或者已经掌握了移动端基础的话可以看笔者写的

移动端H5网页开发常见问题汇总

移动端开发必备知识-Hybrid App

像素

像素即一个小方块,它具有特定的位置和颜色。图片、电子屏幕(手机、电脑)就是由指定数个具有特定颜色和特定位置的小方块拼接而成。比如我们的电脑屏幕1920 * 1080的电脑屏幕,横向有1920个像素纵向有1080个像素。

像素单位有设备像素、逻辑像素、CSS 像素 3 种。

设备像素、独立像素、设备像素比、 CSS 像素

设备像素

设备像素也叫物理像素,是屏幕上最小的显示单元,即设备上真实的物理单元,在设备生产的时候就已经定好。(iphone6 750px)一般比独立像素大。

独立像素

独立像素(DP 或 Dip)是一种虚拟像素,是逻辑上衡量像素的单位,不缩放的情况下等于 css 像素。(iphone5 320px iphone6 375px)。

设备像素比

设备像素比 dpr 就是设备像素和独立像素的比例。比如iphone6的dpr就是2,iphone6 Plus的dpr就是3。

在 web 中,浏览器为我们提供了 window.devicePixelRatio 来帮助我们获取 dpr。在 css 中,可以使用媒体查询 min-device-pixel-ratio 获取dpr。在 React Native 中,我们也可以使用 PixelRatio.get()来获取 dpr。

@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {}

设备像素与设备独立像素之间的比例是多少,普遍规律是,屏幕的像素密度越高,就需要更多的设备像素来显示一个设备独立像素。

CSS 像素

在 CSS 中使用的 px 都是指 css 像素,比如 width: 128px。css 像素的大小是很容易变化的,当我们缩放页面的时候,元素的 css 像素数量不会改变,改变的只是每个 css 像素的大小。也就是说 width: 128px 的元素在缩放200% 以后,宽度依然是 128 个 css 像素,只不过每个 css 像素的宽度和高度变为原来的两倍。如果原本元素宽度为 128 个设备独立像素,那么缩放 200% 以后元素宽度为 256 个设备独立像素。

css 像素与设备独立像素的关系

Retina 屏

前面说到了像素和设备像素比,这里我们再说说Retina 屏。所谓Retina是一种显示标准,是把更多的像素点压缩至一块屏幕里,从而达到更高的分辨率,并提高屏幕显示的细腻程度。在正常阅读距离下,人眼无法分辨屏幕上的像素颗粒,使得屏幕显示效果更为细腻平滑。从iPhone4开始, 苹果公司为其产品mac、iPhone以及iPad的屏幕配置了Retina屏幕。

增加分辨率的方式不同

相同的设备物理尺寸,CSS像素与物理像素的转换不同

k 和 p

我们经常见到用 K 和 P 这个单位来形容屏幕:

英寸

一般用英寸描述屏幕的物理大小,如电脑显示器的 17、22,手机显示器的 4.8、5.7 等使用的单位都是英寸。需要注意尺寸都是屏幕对角线的长度。1 英寸 = 2.54 厘米。

PPI

PPI(Pixel Per Inch):每英寸包括的像素数。

PPI 可以用于描述屏幕的清晰度以及一张图片的质量。使用 PPI 描述图片时,PPI 越高,图片质量越高,使用 PPI 描述屏幕时,PPI 越高,屏幕越清晰。

计算方式 水平像素点数平方与垂直像素点数平方的和的平方根除以屏幕英寸(屏幕对角线长度)。

DPI

DPI(Dot Per Inch):即每英寸包括的点数。

平时你可能会看到使用 DPI 来描述图片和屏幕,这时的 DPI 应该和 PPI 是等价的,DPI 最常用的是用于描述打印机,表示打印机每英寸可以打印的点数。

所以,打印机的 DPI 越高,打印图像的精细程度就越高,同时这也会消耗更多的墨点和时间。

em

em 相对于元素自身的 font-size,1em就等于该元素font-size的大小。由于font-size具有继承性,所以就算本元素没设置font-size大小,也会继承父元素的font-size,如果父元素也没有,会沿着 DOM 树一直向上查找,直到根元素 html,根元素的默认字体大小为 16px。

rem

有了em的基础,rem就更简单了,rem只与根元素html的font-size大小有关,大小固定不变,1rem 就等于根元素 html 的字体大小。

视口(viewport)

布局视口(layout viewport)

布局视口,在移动端显示网页时,由于移动端的屏幕尺寸比较小,如果网页使用移动端的屏幕尺寸进行布局的话,那么整个页面的布局都会显示错乱。所以移动端浏览器提供了一个 layout viewport 布局视口的概念,使用这个视口来对页面进行布局展示,一般 layout viewport 的大小默认为 980px,这保证 PC 的网页可以在手机浏览器上呈现,但是非常小,用户可以手动对网页进行放大。我们可以通过调用 document.documentElement.clientWidth / clientHeight 来获取布局视口大小。布局视口可理解为你网页的宽度。

视觉视口(visual viewport)

视觉视口,visual viewport 指的是移动设备上我们可见的区域的视口大小,一般为设备独立像素大小。visual viewport 和 layout viewport 的关系,就像是我们通过窗户看外面的风景,视觉视口就是窗户,而外面的风景就是布局视口中的网页内容。我们可以通过调用 window.innerWidth / innerHeight 来获取视觉视口大小。视觉视口可以理解为设备独立像素大小。

理想视口(ideal viewport)

理想视口,由于 layout viewport 一般比 visual viewport 要大,所以想要看到整个页面必须通过拖动和缩放才能实现。所以又提出了 ideal viewport 的概念,ideal viewport 下用户不用缩放和滚动条就能够查看到整个页面,并且页面在不同分辨率下显示的内容大小相同。ideal viewport 其实就是通过修改 layout viewport 的大小,让它等于设备的宽度,这个宽度可以理解为是设备独立像素,因此根据 ideal viewport 设计的页面,在不同分辨率的屏幕下,显示应该相同。我们可以通过调用 screen.width / height 来获取理想视口大小,返回的是设备独立像素。理想视口可理解为布局视口与视觉视口相等。

利用 meta 标签对 viewport 进行控制

移动设备默认的 viewport 是 layout viewport,也就是那个比屏幕要宽的 viewport,默认宽度是980px。所以我们经常使用meta标签对viewport进行控制,一般我们设置width=device-width也就是让布局视口和视觉视口相等,即达到理想视口。

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

viewport的属性

适配方案

移动端手机各种各样,手机屏幕也五花八门,所以做移动端H5开发必须要了解适配方案。也就是我们一套代码能兼容各种类型的手机。移动端适配的方案主要有@media媒体查询方案rem方案vw vh方案,下面我们逐一介绍。

使用 css 的媒体查询 @media

通过媒体查询来适配不同的屏幕,这里不具体介绍媒体查询感兴趣的可以自行学习。

@media *mediatype* and|not|only *(media feature)*  {
    CSS-Code;
}
@media screen and (min-width: 375px) { .box { width : 160px; } }
@media screen and (min-width: 750px) { .box { width : 320px; } }

媒体查询虽然能解决适配问题但是使用媒体查询的缺点也很明显

  1. 页面上所有的元素都得在不同的 @media 中定义一遍不同的尺寸,代码冗余多。
  2. 如果再多一种屏幕尺寸,就得多写一个 @media 查询块。
  3. 媒体查询块的书写顺序也很有讲究,后面的会覆盖前面的,很容易出错。

使用 rem

前面我们已经介绍过rem了,1rem的大小就是html元素font-size的大小。所以使用rem布局的核心就是动态设置根元素html的字体大小。在rem适配方案中比较流行的是淘系的lib-flexibleamfe-flexible方案。

lib-flexibleamfe-flexible这两种移动端适配方案都是基于rem实现的。下面我们具体介绍下使用方式,不过官方文档也说了,由于viewport单位得到众多浏览器的兼容,lib-flexibleamfe-flexible这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方。

lib-flexible

感兴趣的可以看看lib-flexible的源码,这个版本会自动生成viewport meta标签

核心代码:

// 根元素字体的大小是布局视口大小/10。
function refreshRem(){
  var width = docEl.getBoundingClientRect().width;
  if (width / dpr > 540) {
      width = 540 * dpr;
  }
  var rem = width / 10;
  docEl.style.fontSize = rem + 'px';
  flexible.rem = win.rem = rem;
}
// 动态生成 viewport meta标签
if (!metaEl) {
  metaEl = doc.createElement('meta');
  metaEl.setAttribute('name', 'viewport');
  metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
  if (docEl.firstElementChild) {
      docEl.firstElementChild.appendChild(metaEl);
  } else {
      var wrap = doc.createElement('div');
      wrap.appendChild(metaEl);
      doc.write(wrap.innerHTML);
  }
}

注意点:

// 如果是苹果手机,才会根据dpr进行缩放,其余的都不做缩放处理
// 我觉得这里的设计应该主要是解决1px的问题吧,在amfe-flexible方案中已经放弃了
if (!dpr && !scale) {
  var isAndroid = win.navigator.appVersion.match(/android/gi);
  var isIPhone = win.navigator.appVersion.match(/iphone/gi);
  var devicePixelRatio = win.devicePixelRatio;
  if (isIPhone) {
      // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
      if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
          dpr = 3;
      } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
          dpr = 2;
      } else {
          dpr = 1;
      }
  } else {
      // 其他设备下,仍旧使用1倍的方案
      dpr = 1;
  }
  scale = 1 / dpr;
}

lib-flexible的使用方式:

// 安装
npm install lib-flexible --save-dev
// 引用
import 'lib-flexible/flexible.js'

这样就会给我们的index.html页面自动生成viewport meta标签,比如<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">

有了lib-flexible的配置,这样就能适配不同的手机屏幕了。我们只需要记住计算公式某元素css尺寸 = 某元素设计稿尺寸/(设计稿宽度/10)。比如设计师的设计稿是375的,假设某元素在设计稿上的高度是75px,我们就需要在代码里面设置该元素的高度为75/(375/10) = 2rem

如果觉得手动计算特别累,我们可以使用postcss-pxtorem插件自动给我们进行计算。

postcss-pxtorem插件的核心是需要我们设置rootValue,如果我们的设计稿是375的我们只需要设置rootValue为37.5,如果我们的设计稿是750的我们只需要设置rootValue为75。这样我们在css中只需要按设计给元素设置px大小,该插件在打包的时候会自动给我们把rem的值计算好,是不是非常棒呢。

// 这里是postcss-pxtorem插件的默认配置
{
  rootValue: 16,
  unitPrecision: 5,
  propList: ['font', 'font-size', 'line-height', 'letter-spacing'],
  selectorBlackList: [],
  replace: true,
  mediaQuery: false,
  minPixelValue: 0,
  exclude: /node_modules/i
}
// 一般我们只需要修改rootValue即可
{
  rootValue: 37.5,
}

amfe-flexible

感兴趣的可以看看amfe-flexible 的源码。这个版本代码更加简洁并且不会自动生成viewport meta标签

核心代码:

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

使用方式:

// 安装
npm install amfe-flexible --save-dev
// 引用
import 'amfe-flexible/index.js'
// 在index.html页面添加  viewport meta标签
<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">

amfe-flexible方案中没有了dpr没有了自动生成viewport meta标签,没有了页面的缩放,变的更加简洁了。

lib-flexible使用方式类似,有了amfe-flexible的配置,这样就能适配不同的手机屏幕了。我们只需要记住计算公式某元素css尺寸 = 某元素设计稿尺寸/(设计稿宽度/10)。比如设计师的设计稿是375的,假设某元素在设计稿上的高度是75px,我们就需要在代码里面设置该元素的高度为75/(375/10) = 2rem

如果觉得手动计算特别累,我们可以使用postcss-pxtorem插件自动给我们进行计算。

postcss-pxtorem插件的核心是需要我们设置rootValue,如果我们的设计稿是375的我们只需要设置rootValue为37.5,如果我们的设计稿是750的我们只需要设置rootValue为75。这样我们在css中只需要按设计稿给元素设置px大小,该插件在打包的时候会自动给我们把rem的值计算好,是不是非常棒呢。

// 这里是postcss-pxtorem插件的默认配置
{
  rootValue: 16,
  unitPrecision: 5,
  propList: ['font', 'font-size', 'line-height', 'letter-spacing'],
  selectorBlackList: [],
  replace: true,
  mediaQuery: false,
  minPixelValue: 0,
  exclude: /node_modules/i
}
// 一般我们只需要修改rootValue即可
{
  rootValue: 37.5,
}

使用 vw vh

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMIMb2P3-1668602484737)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7a8115dc49f94892964ae1913e4818ec~tplv-k3u1fbpfcp-watermark.image?)]

使用vw和vh的适配方式相较于rem方式更简单,我们只需要设置viewport meta标签后就可以直接使用了。

使用方式:

// 在index.html页面添加  viewport meta标签
<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />

跟上面的lib-flexibleamfe-flexible使用方式类似,这样就能适配不同的手机屏幕了。我们只需要记住计算公式某元素css尺寸 = 某元素设计稿尺寸/(设计稿宽度/100) vw。比如设计师的设计稿是375的,假设某元素在设计稿上的高度是75px,我们就需要在代码里面设置该元素的高度为75/(375/100) = 20vw。这里需要注意的是我们宽高都只能用vw为单位。

如果觉得手动计算特别累,我们可以使用postcss-px-to-viewport插件自动给我们进行计算。

postcss-px-to-viewport插件的核心是需要我们设置viewportWidth也就是设计稿的宽度(这点是跟postcss-pxtorem的区别),如果我们的设计稿是375的我们只需要设置viewportWidth为375,如果我们的设计稿是750的我们只需要设置viewportWidth为750。这样我们在css中只需要按设计稿给元素设置px大小,该插件在打包的时候会自动给我们把vw的值计算好,是不是非常棒呢。

// 这里是postcss-px-to-viewport插件的默认配置
{
  unitToConvert: 'px',
  viewportWidth: 320,
  unitPrecision: 5,
  propList: ['*'],
  viewportUnit: 'vw',
  fontViewportUnit: 'vw',
  selectorBlackList: [],
  minPixelValue: 1,
  mediaQuery: false,
  replace: true,
  exclude: undefined,
  include: undefined,
  landscape: false,
  landscapeUnit: 'vw',
  landscapeWidth: 568
}
// 一般我们只需要改viewportWidth即可
{
  viewportWidth: 375,
}

我们还可以使用postcss-px-to-viewport 插件的 Ignoring 特性,标注不需要转换的属性

.box {
  /* px-to-viewport-ignore-next */
  width: 10px;
  height: 10px;
  padding: 10px; /* px-to-viewport-ignore */
}

布局方案

说完了移动端的适配,接下来我们说说移动端的布局方案,移动端布局方案有很多,笔者推荐大家看看 移动端布局教程这篇文章,这里面介绍了移动端常见的布局方案,笔者就不再赘述了。

在这么多布局中,笔者觉得我们必须要掌握的是flex布局和grid布局,因为在日常H5开发中,这两个布局基本上是离不开的。关于flex布局grid布局的教程有很多,这里我就不班门弄斧了,我推荐大家读阮一峰老师的 Flex 布局教程、Grid 网格布局教程。掌握了这两套心法,我觉得在移动端H5开发布局这方面已经没有什么能难得住你了。

扩展

关于窗口大小的常见API

screen.width 获取屏幕的宽度 跟浏览器无关
screen.height 获取屏幕的高度 跟浏览器无关
screen.availWidth 获取屏幕有效宽度 如果任务栏设置在左右两侧的话,去除任务栏宽度
screen.availHeight 获取屏幕有效高度 去除任务栏高度
window.outerWidth 获取浏览器宽度 包括浏览器所有包括侧边栏、窗口镶边和调正窗口大小的边框。
window.outerHeight 获取浏览器高度 包括浏览器所有包括侧边栏、窗口镶边和调正窗口大小的边框。
window.innerWidth:获取浏览器视觉视口宽度(包括滚动条)。
window.innerHeight:获取浏览器视觉视口高度(包括滚动条)。
document.documentElement.clientWidth:获取浏览器布局视口宽度。不包括滚动条。
document.documentElement.clientHeight:获取浏览器布局视口高度。不包括滚动条。
dom.clientWidth:获取元素的宽度 包括内容和内边距
dom.clientHeight:获取元素的高度 包括内容和内边距
dom.offsetWidth:获取元素的宽度 包括内容、内边距、滚动条、边框。
dom.offsetHeight:获取元素的高度 包括内容、内边距、滚动条、边框。
dom.scrollWidth:获取元素内容实际的宽度 包括内边距。
dom.scrollHeight:获取元素内容实际的高度 包括内边距。
dom.clientTop:获取元素上边框高度
dom.clientLeft:获取元素左边框宽度
dom.offsetTop:获取元素距离页面顶部高度
dom.offsetLeft:获取元素距离页面左边的宽度
dom.scrollTop:获取元素滚动条在垂直方向上滚动的距离
dom.scrollLeft:获取元素滚动条在水平方向上滚动的距离

后记

本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个赞~~

发表回复