首页 > 技术文章 > 移动端屏幕适配解决方案

worldly1013 2016-11-11 01:50 原文

  因原在做移动端适配过程中,一直都是应用淘宝的flexible高清方案解决,也没有认真的去研究过源码。这几天又看到一些普通的适配方案,于是把两者源码放在一起比较,发现高清方案是通过动态的获取了当前设备的dpr值,然后根据dpr值的大小来设定手机的视口的缩放比例,从而实现高清。比如:dpr=2时,设定initial-scale = 0.5 ;  dpr=3时 ,设定initial-scale = 0.333 ;

一、手机端viewport详解

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

1.因原手机端厂商为了使电脑端页面更好的迁移到手机端上来,因此大部分手机默认将viewport设为980px ,因此我们在写手机端页面时,上来通常会加上这样一句<meta>声明,这句话究竟有什么用了,又是什么意思了?

width:控制 viewport 的大小,可以指定的一个值或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。

initial-scale:初始缩放。即页面初始缩放程度。这是一个浮点值,是页面大小的一个乘数。例如,如果你设置初始缩放为“1.0”,那么,web页面在展现的时候就会以target density分辨率的1:1来展现。如果你设置为“2.0”,那么这个页面就会放大为2倍。

maximum-scale:最大缩放。即允许的最大缩放程度。这也是一个浮点值,用以指出页面大小与屏幕大小相比的最大乘数。例如,如果你将这个值设置为“2.0”,那么这个页面与target size相比,最多能放大2倍。

minimum-scale:最小缩放,即允许的最小缩放程度。

二、普通方案

代码如下:

 1 var docEl=document.documentElement;
 2 var win=window;
 3 function refreshRem(){
 4     //获取元素宽度
 5     var width=docEl.getBoundingClientRect().width;
 6     console.log(width);
 7     //以640的设计稿为基准,
 8     //直接用设计稿尺寸除以100即得到相对单位rem的值.
 9     var rem=width/640*100;
10     docEl.style.fontSize=rem+'px';
11 }
12 var temp;
13 //监听浏览器窗口改变大小事件
14 win.addEventListener('resize',function(){
15     clearTimeout(temp);
16     temp=setTimeout(refreshRem,300);
17 },false);
18 //监听浏览网页事件
19 win.addEventListener('pageshow',function(e){
20     if(e.persisted){
21         clearTimeout(temp);
22         temp=setTimeout(refreshRem,300);
23     }
24 },false);
25 //初始化
26 refreshRem();

   该JS文件通过获取设备当前宽度,然后通过设定 rem = width / 640  *  100 ;  从而动态的设定rem值的大小,然后把html根节点的font-size大小设为1rem值所代表的字体大小,然后我们就可以愉快

的使用rem来进行移动端的布局了。当然这是针对640px基本设计稿的(iphone5), 如果你使用的是750px的设计稿(iphone6),改为 rem = width/750 * 100 就好了,即本文代码中蓝色标记部分

  注意:html文件开头时需要加上此句meta声明

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

三、高清方案

代码如下:

  1 (function(win, lib) {
  2     var doc = win.document;
  3     var docEl = doc.documentElement;
  4     var metaEl = doc.querySelector('meta[name="viewport"]');
  5     var flexibleEl = doc.querySelector('meta[name="flexible"]');
  6     var dpr = 0;
  7     var scale = 0;
  8     var tid;
  9     var flexible = lib.flexible || (lib.flexible = {});
 10     
 11     if (metaEl) {
 12         console.warn('将根据已有的meta标签来设置缩放比例');
 13         var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
 14         if (match) {
 15             scale = parseFloat(match[1]);
 16             dpr = parseInt(1 / scale);
 17         }
 18     } else if (flexibleEl) {
 19         var content = flexibleEl.getAttribute('content');
 20         if (content) {
 21             var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
 22             var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
 23             if (initialDpr) {
 24                 dpr = parseFloat(initialDpr[1]);
 25                 scale = parseFloat((1 / dpr).toFixed(2));    
 26             }
 27             if (maximumDpr) {
 28                 dpr = parseFloat(maximumDpr[1]);
 29                 scale = parseFloat((1 / dpr).toFixed(2));    
 30             }
 31         }
 32     }
 33 
 34     if (!dpr && !scale) {
 35         var isAndroid = win.navigator.appVersion.match(/android/gi);
 36         var isIPhone = win.navigator.appVersion.match(/iphone/gi);
 37         var devicePixelRatio = win.devicePixelRatio;
 38         if (isIPhone) {
 39             // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
 40             if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
 41                 dpr = 3;
 42             } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
 43                 dpr = 2;
 44             } else {
 45                 dpr = 1;
 46             }
 47         } else {
 48             // 其他设备下,仍旧使用1倍的方案
 49             dpr = 1;
 50         }
 51         scale = 1 / dpr;
 52     }
 53 
 54     docEl.setAttribute('data-dpr', dpr);
 55     if (!metaEl) {
 56         metaEl = doc.createElement('meta');
 57         metaEl.setAttribute('name', 'viewport');
 58         metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
 59         if (docEl.firstElementChild) {
 60             docEl.firstElementChild.appendChild(metaEl);
 61         } else {
 62             var wrap = doc.createElement('div');
 63             wrap.appendChild(metaEl);
 64             doc.write(wrap.innerHTML);
 65         }
 66     }
 67 
 68     function refreshRem(){
 69         var width = docEl.getBoundingClientRect().width;
 70         if (width / dpr > 540) {
 71             width = 540 * dpr;
 72         }
 73         var rem = width / 640 * 100;
 74         docEl.style.fontSize = rem + 'px';
 75         flexible.rem = win.rem = rem;
 76     }
 77 
 78     win.addEventListener('resize', function() {
 79         clearTimeout(tid);
 80         tid = setTimeout(refreshRem, 300);
 81     }, false);
 82     win.addEventListener('pageshow', function(e) {
 83         if (e.persisted) {
 84             clearTimeout(tid);
 85             tid = setTimeout(refreshRem, 300);
 86         }
 87     }, false);
 88 
 89     if (doc.readyState === 'complete') {
 90         doc.body.style.fontSize = 12 * dpr + 'px';
 91     } else {
 92         doc.addEventListener('DOMContentLoaded', function(e) {
 93             doc.body.style.fontSize = 12 * dpr + 'px';
 94         }, false);
 95     }
 96     
 97 
 98     refreshRem();
 99 
100     flexible.dpr = win.dpr = dpr;
101     flexible.refreshRem = refreshRem;
102     flexible.rem2px = function(d) {
103         var val = parseFloat(d) * this.rem;
104         if (typeof d === 'string' && d.match(/rem$/)) {
105             val += 'px';
106         }
107         return val;
108     }
109     flexible.px2rem = function(d) {
110         var val = parseFloat(d) / this.rem;
111         if (typeof d === 'string' && d.match(/px$/)) {
112             val += 'rem';
113         }
114         return val;
115     }
116 
117 })(window, window['lib'] || (window['lib'] = {}));

 

  该JS文件做了上述文件的工作并根据设备当前的dpr值动态的缩放了手机的视口,即dpr=2时,设定initial-scale = 0.5 ;  dpr=3时 ,设定initial-scale = 0.333 ;从而有效的实现了高清方案。其适配

方法与上述一样,你也可以根据你设计师妹妹给你的设计稿情况动态的设定rem的大小,怎么方便怎么来,权利完全在你手上,你可是能够改变世界的人哦!!!

  注意:此法不需要在html文件开头在写 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1,minimum-scale=1,maximum-scale=1" /> 这个声明,不然它

会按照你写的meta声明来对视口进行管理,从而不会再自动对视口进行缩放(代码请看源码蓝色标记)。

  

此法优点:

1.能够有效解决边框1px问题

2.能够很好的使图片达到适配

 

四、rem单位转换工具

1、使用sass预处理器

@function px2em($px, $base-font-size: 16px) {
  @if (unitless($px)) {
    @warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you";
    @return px2em($px + 0px); // That may fail.
  } @else if (unit($px) == em) {
    @return $px;
  }
  @return ($px / $base-font-size) * 1em;
}

 

2、使用CSSREM插件

一个CSS的px值转rem值的Sublime Text 3自动完成插件

效果如下:

 

下载地址:CSSREM插件下载地址

使用方法详见MD文件,默认设置为1rem = 40px ;

 

 

 

 

 

 

推荐阅读