真的,移动端尺寸自适应与dpr无关

作移动端自适应时可能不少人都对自适应和dpr之间的关系产生疑问,也有一些人会疑虑好比个人自适应方案没有加dpr会不会出问题,针对这些疑问我说一下个人看法。css

1. 什么是尺寸自适应

首先标题说的自适应,可能自适应在不一样人眼里理解不一样,特别与响应式的关系,在这里说一下我所理解的自适应,和其与响应式的区别。先说响应式设计,响应式设计表示在不一样的屏幕尺寸下,都有良好的布局和内容表现,简单一点的说,就是一个页面能够适配多种不一样尺寸的屏幕,并且看上去仍是设计良好的。为了实现这个目的,可能会利用js或者css去动态改变布局的尺寸,在这个过程当中会伴随元素尺寸的改变,布局的改变,甚至会把元素隐藏,好比在pc端显示的页面转到移动端就会这样。而自适应每每考虑的是另外一个方面,就是但愿页面的设计与设计稿的设计比例一致,这个也是作自适应的目的,在这个过程当中针对不一样的屏幕宽度元素的尺寸也会改变,可是通常不会有布局改变,和元素的隐藏,由于设计稿就这样,咱们得按设计师妹子的尺寸来写页面。因此按照我以上的说法,那些按照css媒体查询写的自适应严格来讲不叫自适应,由于断点之间会形成比例偏差,而让偏差少一点就得多插值。很明显使用css媒体查询并非作自适应的好方法,咱们须要一种准确的方法来作这个事,这个时候js就出来,下面将会列举坊间流传甚广的淘宝方案和网易方案。html

2. 淘宝方案

点这里能够看到淘宝方案具体的代码[flexible][1]
固然具体的代码是作了不少的边界处理和兼容处理的,可是核心能够浓缩为如下代码

(function () {
    var dpr = window.devicePixelRatio;
    var meta = document.createElement('meta');
    var scale = 1 / dpr;
    meta.setAttribute('name', 'viewport');
    meta.setAttribute('content', 'width=device-width, user-scalable=no, initial-scale=' + scale +
      ', maximum-scale=' + scale + ', minimum-scale=' + scale);
    document.getElementsByTagName('head')[0].appendChild(meta);
    // 动态设置的缩放大小会影响布局视口的尺寸
      function resize() {
      var deviceWidth  = document.documentElement.clientWidth;
      document.documentElement.style.fontSize = (deviceWidth / 10) +'px';
         }
    resize();
    window.onresize = resize;
  })()

这段代码放在浏览器上就能作到自适应了,他的过程是先获取设备的dpr,所谓的dpr就是设备像素比,什么是设备像素比呢,就是单位尺寸内,设备物理像素的个数除以设备独立像素的大小,物理像素就是手机屏幕上一个一个的发光的点,大小是固定的,独立像素也叫作逻辑像素,css设置的像素大小就是逻辑像素,对于dpr等于2的手机屏幕,设置css宽度为1px,其实覆盖的是2个设备物理像素。回到正题,拿到dpr后,经过动态设置meta的viewport值,进行对布局的缩放操做。这里有一个关键,就是设置 width=device-width和initial-scale的大小,在描述二者的做用以前咱们先要理解一个概念就是布局视口,布局视口在以前有一个别名叫作初始包含块,而在比较早的文献中初始包含块也叫作画布。理解画布可能比理解布局视口更简单,若是你按比例绘图,不少时候就要参照你所用画布的大小,好比设计师在750px画了一个200px的正方形,若是你要在一张大小是100cm的纸上画,你可能就要这样计算正方形的宽度了 100cm * 200 / 750,能够看到这个计算中是没有用到dpr,你的笔触跨过多少个纸张分子,多少个原子根本就不影响个人绘图比例。咱们的画画的过程就至关于设置css的过程,css的尺寸依赖的就是布局视口的大小,而网页的布局视口大小在标准模式下能够这样获取 document.documentElement.clientWidth,而两个关键的元素设置 width=device-width,initial-scale = scale,作的事情就是先把布局视口放大dpr倍,而后总体缩放相应倍数以适应设备尺寸。这个也很容易验证在控制台打印布局视口大小就好了
图片描述
这是按照640px设计规范,设计图上标注200px元素大小,能够看出布局视口放大了3倍,而后再总体缩放到设备屏幕大小,因为这里是证实这个过程其实与dpr无关,我如今把scale的大小设置为
0.1 和 0.5前端

var meta = document.createElement('meta');
        var scale = 0.1;
        meta.setAttribute('name', 'viewport');

图片描述

var meta = document.createElement('meta');
        var scale = 0.5;
        meta.setAttribute('name', 'viewport');

图片描述
这里能够看到就算我设置scale不等于 1 / drp 的大小也不妨碍我按设计图的比例画出元素
这里要注意两点,由于我是用chrome模拟的,设置的时候发现几个问题android

  1. scale的值若是小于0.1布局视口也只能放大10倍,也就是布局视口最多放大10倍
  2. 当scale的值大于1时布局视口并不会缩小,并且布局视口再也不匹配设备宽度
  3. 若是你引入了flexible.js进行测试,要注意删除边界条件,由于缩放影响了布局视口大小,相应的边界条件会触发,致使误认为dpr与自适应有关

要作到自适应关键是让元素的尺寸与布局视口绑定关系,在这里虽然布局视口放大了,但并不影响这种绑定关系,这里淘宝方案把布局视口的宽度分割了十等份,每份的大小至关于布局宽度的十分之一,而把每份的大小分配给根元素的字体大小,元素尺寸就能够设置rem单位来与布局视口绑定关系,以200px尺寸为例,他们比例映射是这样的web

200px : 640px => xrem : 10remchrome

这里的10rem就是布局视口宽度,元素尺寸只要维持这个比例关系就好了,与dpr是没有关系的浏览器

x= 10 * 200 / 640 = 3.125remapp

这里的计算可能会费一点时间,也有一些插件能够辅助把px转为rem的
可是方案是死的,人是活的,你只要把淘宝固有的十等分改一下就好了,好比设计稿是640px的
改一下iphone

document.documentElement.style.fontSize = (deviceWidth / 6.4) +'px';

分了6.4等份
200px : 640px => xrem : 6.4rem
x一看就知道是 2remsvg

流程 rem => 根元素字体大小 => 布局视口

那么为何淘宝要引入dpr,把布局放大再缩小呢,其中一点就是这个方案能够很好地解决1px边框的问题,对于高清屏来讲设置1px像素大小,其实横跨的是dpr个设备像素,这样看起来线条不够细,与设计稿就产生出入,而经过布局放大再缩小的方案恰好就弥补了这个问题。可是随之而来也带来一个问题,看上面的截图咱们看到字体大小发生了改变,在scale设置为0.1时基本就看不见了,缘由是通常咱们的字体大小的设置不会使用rem,而是使用px单位,这里的字体大小没有随布局视口的放大而增大,却随页面的总体缩放而缩小了,这里就得要针对不一样的dpr作响应的处理,在淘宝的代码中咱们能够看到

docEl.setAttribute('data-dpr', dpr);

就是经过在根元素上挂载dpr信息,而后设置相应的css属性例如

[data-dpr=2] div{
    font-size: 32px
}
[data-dpr=3] div{
    font-size: 48px
}

特别对于安卓手机,各类神奇的dpr,若是每一个都这样设置将会是灾难
因此淘宝很是聪明

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;

够简单直接,安卓高清屏是不存在的, 可是其实影响也不大,就是安卓屏的1px线条粗一点而已
若是除了要作自适应还要作响应式,那也得像上面设置字体同样一个一个设置,由于css媒体查询也是针对布局视口尺寸的。对于淘宝他们来讲,确定有一套工程化的方案来解决这种技术难题,对于遇到这个坑的伙伴估计得自已想办法了,预处理器是必不可少的。
从前面能够知道淘宝引入dpr并非为了作自适应的,而是为了解决1px问题的,固然也引入了其余难题,既然如此,放弃解决1px问题,不就简单得多,网易方案就是这么简单。

3.网易方案

去除了边界处理和兼容处理,因为没有动态设置meta因此要在head中引入


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


 (function () {
    var dpr = window.devicePixelRatio;
    function resize() {
      var deviceWidth = document.documentElement.clientWidth;
    document.documentElement.style.fontSize = (deviceWidth / 6.4) +'px';
   }
    resize();
    window.onresize = resize;
  })()

网易方案没有引入dpr相关的,这也说明了移动端自适应与dpr是无关的
图片描述
从图片中能够看出和淘宝方案的区别,布局视口没有放大,整个页面也没有缩放,可是并不影响与设计图的比例
200px : 640px => xrem : 6.4rem
x= 2rem
流程 rem => 根元素的大小 => 布局视口

既然自适应与dpr无关那么就能够扩展出不少方案了

4. 其余方案

1.在布局视口等于设备宽度时,直接把根元素字体大小绑定到设备宽度大小上

document.documentElement.style.fontSize = (screen.width/ 6.4) +'px';

这里有关相关的文章 基于screen.width的伪响应式开发

2.直接定死布局视口

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <meta name="viewport" content="width=640, user-scalable=no">//定死为设计稿的尺寸
  <meta name="renderer" content="webkit">
  <title>定死布局视口</title>
  <style>
    html, body {
      margin: 0
    }
    div {
      width: 200px;
      height: 200px;
      background: red;
    }

    body {
      background: blue;
    }
  </style>
  <script>
  </script>
</head>
<body>
  <div id="size200px">元素大小200px</div>
</body>

</html>

图片描述

不用rem单位,不用设置js,可是布局视口定死后,就不能用css媒体查询作响应式了,从这里也能够看出viewport属性的做用,就是让布局视口经过缩放来适配屏幕宽度,width=device.width仅仅是让布局视口初始大小等于设备宽度,后面设置的initial-scale是用来缩放布局视口大小,并且默认是布局视口初始大小等于设备宽度,也就是所谓的理想视口,换个说法就是若是你设置了initial-scale你能够不用设置 width=device.width了,淘宝方案你把width=device.width去掉,并不会影响自适应过程,加上主要是防止一些不按规范的浏览器出现兼容问题。若是还不能理解viewport的做用,那么能够参考svg中的viewport和viewBox的关系,原理是同样的。

3.使用新出单位 vm, vm 就是专门为自适应而出现的,100vm就是布局视口的宽度,很是厉害,你也不用设置js了
200px: 640px => xvm : 100vm
x=200 * 100 / 640 = 31.25vm

流程 vm => 布局视口

看一下兼容性
图片描述

兼容还能够,这里也有相关的资料 分享手淘过年项目中采用到的前端技术

5.总结

移动端尺寸自适应与dpr无关,除了淘宝方案外,其余方案都得处理1px的问题,但也减小针对不一样dpr设备作响应式处理的麻烦,并且其中也没有一种一劳永逸的方案能解决所有问题。而做为新出来的单位vm,是时候该入坑了

参考文章:

1. 张鑫旭 [基于screen.width的伪响应式开发][10]
2. 大漠 [分享手淘过年项目中采用到的前端技术][11]
3. [flexible][12]
4. 张鑫旭 [设备像素比devicePixelRatio简单介绍][13]
相关文章
相关标签/搜索