移动端适配总结

1,385 阅读6分钟

一 、viewport 视口

pc端

1、是浏览器的可视区域,其宽度和浏览器窗口的宽度保持一致

2、 默认「body」的宽度取自「HTML」,而「HTML」的宽度取自「viewport」,「viewport」的宽度正好等于「浏览器」窗口的宽度

3、document.documentElement.clientWidth取到的是「viewport」的宽,window.innerWidth能取到「浏览器」窗口的宽

4、PC端这两者的区别仅仅区分在window.innerWidth包含滚动条的宽

移动端

  • 布局视口(Layout Viewport)、即DOM宽度、document.documentElement.clientWidth
  • 视觉视口(Visual Viewport)、即屏幕宽度、window.innerWidth
  • 理想视口(Ideal Viewport)、使布局视口等于视觉视口

1、移动设备默认的viewport是layout viewport,一般情况下,都比屏幕宽度大,借助mate标签设置理想视口

<meta name="viewport" content="width=device-width,initial-scale=1">

2、window.innerWidth,度量单位是css像素,比如原=1220px,放大200%后=610px;其值会根据缩放的程度而改变,读到的值为当前屏幕上x方向的css像素的值。

3、Layout Viewport,作为html的“上层”元素,用来约束网站中最顶级包含块元素

4、在pc端window.innerWidth和document.documentElement.clientWidth在缩放不同的情况下只是相差一个滚动条的宽度

5、在移动端,window.innerWidth(visual viewport)和document.documentElement.clientWidth(layout viewport)在缩放的情况下值是不同的。原因在于移动端的document.documentElement.clientWidth(layout viewport)总是固定的。

6、width 设置layout viewport的宽度

7、缩放值是相对ideal viewport width进行缩放的

8、设置initial-scale指令实际上做了两件事:

  • 把页面的初始缩放因素设置了一个有意义的值,是相对于ideal viewport进行计算的,产生了visual viewport的宽度。
  • 根据刚刚计算得到的visual viewport的宽度值去设置layout viewport 的宽度值

二、单位

  • 物理像素(设备像素、device pixels、dp)

    • 是显示设备中一个最微小的物理部件,每个像素可以根据操作系统设置自己的颜色和亮度,所谓的一倍屏、二倍屏(Retina)、三倍屏,指的是设备以多少物理像素来显示一个CSS像素。
    • 多倍屏以更多更精细的物理像素点来显示一个CSS像素点,在普通屏幕下1个CSS像素对应1个物理像素,而在Retina屏幕下,1个CSS像素对应的却是4个物理像素。
  • css像素(设备无关像素、device-independent pixel、简称DIPs)

  • 设备独立像素

    • 与屏幕密度有关,dip可以用来辅助区分视网膜设备还是非视网膜设备;
    • 安卓设备根据屏幕像素密度可分为ldpi、mdpi、hdpi、xhdpi等不同的等级,规定以160dpi为基准,1dp=1px;如果密度为320dpi,则1dp=2px,以此类推;
    • IOS设备:从iphone4开始Retina屏;
    • CSS像素与设备独立像素之间的关系依赖于当前的缩放等级
  • 设备像素比(dpr)

    • dpr = 物理像素/设备独立像素,viewport中的scale和dpr是倒数关系
  • 屏幕像素密度(pixel per inch、PPI)

    • 屏幕密度=对角线分辨率/屏幕尺寸

    • 指一个设备表面上存在的像素数量,它通常以每英寸有多少像素来计算,屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小;

三、flexible适配方案

源码

// 首先是一个立即执行函数,执行时传入的参数是window和document
(function flexible (window, document) {
  var docEl = document.documentElement // 返回文档的root元素
  var dpr = window.devicePixelRatio || 1 
  // 获取设备的dpr,即当前设置下物理像素与虚拟像素的比值
 
  // 调整body标签的fontSize,fontSize = (12 * dpr) + 'px'
  // 设置默认字体大小,默认的字体大小继承自body
  function setBodyFontSize () {
    if (document.body) {
      document.body.style.fontSize = (12 * dpr) + 'px'
    } else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize)
    }
  }
  setBodyFontSize();
 
  // set 1rem = viewWidth / 10
  // 设置root元素的fontSize = 其clientWidth / 10 + ‘px’
  function setRemUnit () {
    var rem = docEl.clientWidth / 10
    docEl.style.fontSize = rem + 'px'
  }
 
  setRemUnit()
 
  // 当页面展示或重新设置大小的时候,触发重新
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function (e) {
    if (e.persisted) {
      setRemUnit()
    }
  })
 
  // 检测0.5px的支持,支持则root元素的class中有hairlines
  if (dpr >= 2) {
    var fakeBody = document.createElement('body')
    var testElement = document.createElement('div')
    testElement.style.border = '.5px solid transparent'
    fakeBody.appendChild(testElement)
    docEl.appendChild(fakeBody)
    if (testElement.offsetHeight === 1) {
      docEl.classList.add('hairlines')
    }
    docEl.removeChild(fakeBody)
  }
}(window, document))

存在问题

1、iframe的引用

2、当宽度大于540px时,不能适配,改为pc适配或者修改源码

四、vh适配方案

1、以vw为单位:

  • 目前vw已得到众多浏览器的支持

  • vw是基于viewport视窗的长度单位,viewport视窗是指浏览器的可视区域=window.innerWidth/window.innerHeight

  • 1vw=1% window.innerWidth

2、postcss-px-to-viewport将px转为vw:

  • 视觉稿一般使用750px宽度,100vw=750px,1vw=7.5px,根据设计图上的px值转换为对应的vw值;

  • 使用postcss-px-to-viewport插件将px转为vw,可手动配置

“post-px-to-viewport”:{

    viewportWidth: 750, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750

    viewportHeight: 1334, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置

    unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除)

    viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw

    selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名

    minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值 mediaQuery: false // 允许在媒体查询中转换`px`

}

在不想要把px转换为vw的时候,在对应的元素(html)中添加配置中指定的类名.ignore或.hairlines(.hairlines一般用于设置border-width:0.5px的元素中)

3、哪些地方使用vw单位:

容器适配;文本适配;大于1px的边框、圆角、阴影;内边距、外边距

4、解决1px方案:

1、常见解决方案:媒体查询,在dpr>=2时,使用.5px;border-image;background-image;:before:after重写border,transform scale缩小一半;viewport+rem;

2、PostCSS插件:postcss-write-svg,通过border-image、和background-image两种方式处理,自动生成图片;低端机对border-image支持度不够好,使用background-image。

5、不支持vw单位降级处理:

1、CSS Polyfill:通过相应的Polyfill做相应的处理,使用针对于vw单位的Polyfill viewport-units-buggyfill

第一步:

viewport-units-buggyfill主要有两个JavaScript文件:viewport-units-buggyfill.js和viewport-units-buggyfill.hacks.js

在HTML文件中引入这两个文件

第二步:

在HTML文件中调用viewport-units-buggyfill

window.onload = function () {

window.viewportUnitsBuggyfill.init({

    hacks: window.viewportUnitsBuggyfillHacks

 });

}

第三步:

在你的CSS中,只要使用到了viewport的单位(vw、vh、vmin或vmax )地方,需要在样式中添加content

2、postcss-viewport-units:

给CSS的属性添加content的属性,配合viewport-units-buggyfill库给vw、vh、vmin和vmax做适配的操作,无需关注content的内容,插件会自动帮你处理

3、注意:

content也会引起一定的副作用,比如img和伪元素::before(:before)或::after(:after),在img中content会引起部分浏览器下,图片不会显示,需要全局添加:

img {

content: normal !important;

}

6、vw不足之处:

1、存在少量不支持vw的机型、浏览器;

2、px转为vw单位时存在一定误差。

五、三大适配方案对比

参考

segmentfault.com/a/119000000…

www.w3cplus.com/css/vw-for-…

www.jianshu.com/p/1f1b23f83…