移动端适配

移动端适配

参考文章:

  • https://segmentfault.com/a/1190000008767416
  • https://www.jianshu.com/p/2c33921d5a68

适配的目标

在不同尺寸的手机设备上,页面“相对性的达到合理的展示(自适应)”或者“保持统一效果的等比缩放(看起来差不多)”。

viewport视口

viewport是严格的等于浏览器的窗口。

  • visual viewport可见视口 屏幕宽度

  • layout viewport布局视口 DOM宽度

  • ideal viewport理想适口:使布局视口就是可见视口

  • 设备宽度(visual viewport)与DOM宽度(layout viewport), scale的关系为:
    (visual viewport)= (layout viewport)* scale

  • 获取屏幕宽度(visual viewport)的尺寸:window. innerWidth/Height

  • 获取DOM宽度(layout viewport)的尺寸:document. documentElement. clientWidth/Height

设置理想视口: 把默认的layout viewport的宽度设为移动设备的屏幕宽度,得到理想视口(ideal viewport):


属性 属性值 描述
width 数值 / device-width 视口宽度
height 数值 / device-height 视口高度
initial-scale 0.0 ~ 10.0 设备宽度与视口大小之间的缩放比率
maximum-scale 0.0 ~ 10.0 缩放最大值
minimum-scale 0.0 ~ 10.0 缩放最小值
user-scalable 布尔值 默认yes,为no时用户不能缩放网页

物理像素(physical pixel)

物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。每个像素可以根据操作系统设置自己的颜色和亮度。所谓的一倍屏、二倍屏(Retina)、三倍屏,指的是设备以多少物理像素来显示一个CSS像素,也就是说,多倍屏以更多更精细的物理像素点来显示一个CSS像素点,在普通屏幕下1个CSS像素对应1个物理像素,而在Retina屏幕下,1个CSS像素对应的却是4个物理像素。
移动端适配_第1张图片

CSS像素

CSS像素是一个抽像的单位,主要使用在浏览器上,用来精确度量Web页面上的内容。一般情况之下,CSS像素称为与设备无关的像素(device-independent pixel),简称DIPs。CSS像素就是写CSS时所用的像素。

设备像素比dpr(device pixel ratio)

设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系。它的值可以按下面的公式计算得到:

设备像素比 = 物理像素 / 设备独立像素

在Retina屏的iphone上,devicePixelRatio的值为2,也就是说1个css像素相当于2个物理像素。通常所说的二倍屏(retina)的dpr是2, 三倍屏是3。

  • viewport中的scale和dpr是倒数关系。
  • 获取当前设备的dpr:
    JavaScript: window.devicePixelRatio
    CSS: -webkit-device-pixel-ratio, -webkit-min-device-pixel-ratio, -webkit-max-device-pixel-ratio
  • 不同dpr的设备,可根据此做一些样式适配(这里只针对webkit内核的浏览器和webview)。

设备独立像素dip或dp

dip或dp,(device independent pixels,设备独立像素)与屏幕密度有关。dip可以用来辅助区分视网膜设备还是非视网膜设备。

  • 安卓设备根据屏幕像素密度可分为ldpi、mdpi、hdpi、xhdpi等不同的等级。规定以160dpi为基准,1dp=1px。如果密度是320dpi,则1dp=2px,以此类推。
  • IOS设备:从IPhone4开始为Retina屏
  • CSS像素与设备独立像素之间的关系依赖于当前的缩放等级。

屏幕像素密度PPI(pixel per inch)

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

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

概念关系图

屏幕尺寸、屏幕分辨率-->对角线分辨率/屏幕尺寸-->屏幕像素密度PPI
                                             |
              设备像素比dpr = 物理像素 / 设备独立像素dip(dp)
                                             |
                                       viewport: scale
                                             |
                                          CSS像素px

前端实现相关方式

下面大致列下前端在实现适配上常采用的方式。

viewport

设置理想视口

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

设置理想视口(页面不缩放,禁止用户缩放),使得DOM宽度(layout viewport)与屏幕宽度(visual viewport)一样大,DOM文档主宽度即为屏幕宽度。1个CSS像素(1px)由多少设备像素显示由具体设备而不同。

动态设置视口缩放为1/dpr

对于安卓,所有设备缩放设为1,对于IOS,根据dpr不同,设置其缩放为dpr倒数。设置页面缩放可以使得1个CSS像素(1px)由1个设备像素来显示,从而提高显示精度;因此,设置1/dpr的缩放视口,可以画出1px的边框。

不管页面中有没有设置viewport,若无,则设置,若有,则改写,设置其scale为1/dpr。

(function (doc, win) {
  var docEl = win.document.documentElement;
  var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
  var metaEl = doc.querySelector('meta[name="viewport"]');
  var dpr = 0;
  var scale = 0;

  // 对iOS设备进行dpr的判断,对于Android系列,始终认为其dpr为1
  if (!dpr && !scale) {
    var isAndroid = win.navigator.appVersion.match(/android/gi);
    var isIPhone = win.navigator.appVersion.match(/[iphone|ipad]/gi);
    var devicePixelRatio = win.devicePixelRatio;

    if(isIPhone) {
      dpr = devicePixelRatio;
    } else {
      drp = 1;
    }
    
    scale = 1 / dpr;
  }
   //   设置data-dpr和viewport
  docEl.setAttribute('data-dpr', dpr);
  // 动态改写meta:viewport标签
  if (!metaEl) {
    metaEl = doc.createElement('meta');
    metaEl.setAttribute('name', 'viewport');
    metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
    document.documentElement.firstElementChild.appendChild(metaEl);
  } else {
    metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
  }
})(document, window);

px单位的适配

设置动态缩放视口后,在iPhone6上,缩放为0.5,即CSS像素2px最终显示效果为1px,而在scale=1的设备,CSS像素1px显示效果为1px,那么,为了达到显示效果一致,以px为单位的元素(比如字体大小),其样式应有适配不同dpr的版本,因此,在动态设置viewport: scale的时候,同时在html根元素上加上data-dpr=[dpr]但是这种方式还是不够,如果dpr为2,3之外的其他数值,px就没办法适配到。因此我会选择都用rem为单位进行适配。
样式示例:

.p {
    font-size: 14px;

  [data-dpr="2"] & {
    font-size: 14px * 2;
  }

  [data-dpr="3"] & {
    font-size: 14px * 3;
  }
}

为写样式方便,可以借助sass的mixin写代码片段:

// 适配dpr的字体大小
@mixin font-dpr($font-size){
  font-size: $font-size;

  [data-dpr="2"] & {
      font-size: $font-size * 2;
  }

  [data-dpr="3"] & {
      font-size: $font-size * 3;
  }
}
@mixin px-dpr($property, $px) {
  #{$property}: $px;

  [data-dpr="2"] & {
    #{$property}: $px * 2;
  }

  [data-dpr="3"] & {
    #{$property}: $px * 3;
  }
}

// 使用
@include font-dpr(14px);
@include px-dpr(width, 40px); @include px-dpr(height, 40px);

设置缩放视口与设置理想视口有什么不同

问题:viewport设为理想视口(scale=1),基本已经满足适配,为什么要动态设置viewport缩放?

原因:iPhone6为例,dpr为2,缩放设为0.5,则DOM宽度为750,缩放后显示刚好为屏幕宽度375,而总的CSS像素其实是750,与设备像素一致,这样1px的CSS像素,占用的物理像素也是1;而viewport设置缩放为1的理想视口情况下,DOM宽度为375,显示也刚好是屏幕宽度,然而1px的CSS像素,占用的物理像素是2。这样说来,这样设置可以实现1px的线条在二倍屏的显示。因为:CSS像素与设备像素的关系依赖于屏幕缩放。

验证:设备:iPhone6,

  • 在scale=0.5时,1px边框显示效果;
  • 在scale=1.0时,1px边框显示效果;
  • 在scale=0.5时,2px边框显示效果;

通过对比后发现,在scale=0.5时,1px的线比scale=1.0要细,即** 不同dpr的设备用不同伸缩比,可以实现在多倍屏设备上显示1px设备独立像素的线条 **,这也就解决了1px线条的显示问题。

rem(一个CSS单位)

定义:font size of the root element.

这个单位的定义和em类似,不同的是em是相对于父元素,而rem是相对于根元素。rem定义是根元素的font-size, 以rem为单位,其数值与px的关系,需相对于根元素< html>的font-size计算,比如,设置根元素font-size=16px, 则表示1rem=16px。

根据这个特点,可以根据设备宽度动态设置根元素的font-size,使得以rem为单位的元素在不同终端上以相对一致的视觉效果呈现。

选取一个设备宽度作为基准,设置其根元素大小,其他设备根据此比例计算其根元素大小。比如使得iPhone6根元素font-size=16px。

设 备 设备宽度 根元素font-size/px 宽度/rem
iPhone5 320 js计算所得
iPhone6 375 16 23.4375
i6 Plus 414 js计算所得
- 360 js计算所得
  • 根元素fontSize公式:width/fontSize = baseWidth/baseFontSize

  • 其中,baseWidth, baseFontSize是选为基准的设备宽度及其根元素大小,width, fontSize为所求设备的宽度及其根元素大小

动态设置根元素fontSize

(function (doc, win) {
  var docEl = win.document.documentElement;
  var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
  
    //  设置根元素font-size
    //当设备宽度为375(iPhone6)时,根元素font-size=16px; 
  
  var refreshRem = function () {
    var clientWidth = win.innerWidth
                      || doc.documentElement.clientWidth
                      || doc.body.clientWidth;

    console.log(clientWidth)
    if (!clientWidth) return;
    var fz;
    var width = clientWidth;
    fz = 16 * width / 375;
    docEl.style.fontSize = fz + 'px';
  };

  if (!doc.addEventListener) return;
  win.addEventListener(resizeEvt, refreshRem, false);
  doc.addEventListener('DOMContentLoaded', refreshRem, false);
  refreshRem();

})(document, window);

rem计算(px2rem)

对于需要使用rem来适配不同屏幕的元素,使用rem来作为CSS单位,为了方便,可以借助sass写一个函数计算px转化为rem, 写样式时不必一直手动计算。

/* 
 * 此处 $base-font-size 具体数值根据设计图尺寸而定
 * flexible中设置的标准是【fontSize=16px, when 屏幕宽度=375】,因此,按此标准进行计算,
 * 若设计图为iPhone6(375*667)的二倍稿,则$base-font-size=32px
 *
 */ 
@function px2rem($px, $base-font-size: 32px) {
  @if (unitless($px)) {
    @warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you";
    @return px2rem($px + 0px); // That may fail.
  } @else if (unit($px) == rem) {
    @return $px;
  }
  @return ($px / $base-font-size) * 1rem;
}

// 使用,eg:
font-size: px2rem(18px);

注意

  • 某些Android设备会丢掉 rem 小数部分,那么1rem对应的px少些,在这些安卓设备上显示误差就会较小,当然如果不存在会丢掉小数这个问题,这一说也就不必考虑了。
  • 未设置font-size情况下,1rem的大小具体看浏览器的实现,默认的根元素大小是font-size=16px
  • 目前一般会选取iPhone6作为基准,设计图便要iPhone6的二倍图
  • 当动态缩放视口为1/dpr, 计算所得的根元素fontSize也会跟着缩放,即若理想视口(scale=1), iPhone6根元素fontSize=16px; 若scale=0.5, iPhone6根元素fontSize=32px; 因此设置视口缩放应放于设置根元素fontSize之前。

px转rem的mixin

// 使用sass的混合宏
// 淘宝手淘的方案里,i6(375pt)屏幕宽度为10rem,即font-size=75px, scale=0.5 因设计图为二倍图,$base-font-size=75px
@mixin px2rem($property,$px-values,$baseline-px:16px,$support-for-ie:false){
    //Conver the baseline into rems
    $baseline-rem: $baseline-px / 1rem * 1;
    //Print the first line in pixel values
    @if $support-for-ie {
        #{$property}: $px-values;
    }
    //if there is only one (numeric) value, return the property/value line for it.
    @if type-of($px-values) == "number"{
        #{$property}: $px-values / $baseline-rem;
    }
    @else {
        //Create an empty list that we can dump values into
        $rem-values:();
        @each $value in $px-values{
            // If the value is zero or not a number, return it
            @if $value == 0 or type-of($value) != "number"{
                $rem-values: append($rem-values, $value / $baseline-rem);
            }
        }
        // Return the property and its list of converted values
        #{$property}: $rem-values;
    }
}

适配方案

JS动态修改配合rem适配

原理:rem是相对长度单位,rem方案中的样式设计为相对于根元素font-size计算值的倍数。根据 屏幕宽度 设置html标签的font-size,在布局时使用 rem 单位布局,达到自适应的目的,是 弹性布局 的一种实现方式。

实现过程: 首先获取文档根元素和设备dpr,设置 rem,在html文档加载和解析完成后调整body字体大小; 在页面缩放 / 回退 / 前进的时候, 获取元素的内部宽度 (不包括垂直滚动条,边框和外边距),重新调整 rem 大小。

实现方法: 用 css 处理器或 npm 包将页面 css 样式中的px自动转换成 rem。在整个 flexible 适配方案中,文本使用px作为单位,使用[data-dpr]属性来区分不同dpr下的文本字号。由于手机浏览器对字体显示最小是8px,因此对于小尺寸文字需要采用px为单位,防止通过 rem 转化后出现显示问题。手机淘宝 中的字体使用px为单位,腾讯新闻中的字体使用rem为单位。

(function(win, lib) {
    var doc = win.document; //当前文档对象
    var docEl = doc.documentElement; //文档对象根元素的只读属性
    var metaEl = doc.querySelector('meta[name="viewport"]');
    var flexibleEl = doc.querySelector('meta[name="flexible"]');
    var dpr = 0;
    var scale = 0;
    var tid;
    var flexible = lib.flexible || (lib.flexible = {});
 
    if (metaEl) { 
    //当meta中viewport的标签设置了scale时,将根据scale手动设置dpr
        console.warn('将根据已有的meta标签来设置缩放比例');
        var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
        if (match) {
            scale = parseFloat(match[1]);
            dpr = parseInt(1 / scale);
        }
    } else if (flexibleEl) {   
    //当meta中flexible的标签存在时,据此设置dpr
        var content = flexibleEl.getAttribute('content');
        if (content) {
            var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
            var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
            if (initialDpr) {
                dpr = parseFloat(initialDpr[1]);
                scale = parseFloat((1 / dpr).toFixed(2));    
            }
            if (maximumDpr) {
                dpr = parseFloat(maximumDpr[1]);
                scale = parseFloat((1 / dpr).toFixed(2));    
            }
        }
    }

    if (!dpr && !scale) { 
    //根据js获取到的devicePixelRatio设置dpr及scale,scale是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和3倍方案
            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;
    }

    docEl.setAttribute('data-dpr', dpr);
    //文本字号不建议使用rem,flexible适配方案中,文本使用px作为单位,使用[data-dpr]属性来区分不同dpr下的文本字号
    
    if (!metaEl) {
    //添加meta标签,设置name为viewport,content根据scale设置缩放比(默认、最大、最小缩放比)
        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);
        }
    }

    function refreshRem(){
        //更新rem值
        var width = docEl.getBoundingClientRect().width;
        if (width / dpr > 540) {
            width = 540 * dpr;
        }
        var rem = width / 10; //1rem = viewWidth / 10
        docEl.style.fontSize = rem + 'px';
        flexible.rem = win.rem = rem;
    }
    
    //resize与pageshow延时300ms触发refreshRem(),使用防抖函数,防止事件被高频触发可能引起性能问题
    win.addEventListener('resize', function() {
        clearTimeout(tid);
        tid = setTimeout(refreshRem, 300);
    }, false);
    win.addEventListener('pageshow', function(e) {
        //当一条会话历史纪录被执行的时候触发事件,包括后退/前进按钮,同时会在onload页面触发后初始化页面时触发
        if (e.persisted) {//表示网页是否来自缓存
            clearTimeout(tid);
            tid = setTimeout(refreshRem, 300);
        }
    }, false);

    //在html文档加载和解析完成后设置body元素字体大小
    if (doc.readyState === 'complete') {
        doc.body.style.fontSize = 12 * dpr + 'px';
    } else {
        doc.addEventListener('DOMContentLoaded', function(e) {
            doc.body.style.fontSize = 12 * dpr + 'px';
        }, false);
    } 
    //浏览器有最小字体限制,css在pc上font-size是12px(移动端最小是8px), 也就是css像素是12,其DPR为1,在移动端dpr有可能为2和3,为了保证字体不变小,需要用12*dpr进行换算。
   
    refreshRem();

   //实现rem与px相互转换
    flexible.dpr = win.dpr = dpr;
    flexible.refreshRem = refreshRem;
    flexible.rem2px = function(d) {
        var val = parseFloat(d) * this.rem;
        if (typeof d === 'string' && d.match(/rem$/)) {
            val += 'px';
        }
        return val;
    }
    flexible.px2rem = function(d) {
        var val = parseFloat(d) / this.rem;
        if (typeof d === 'string' && d.match(/px$/)) {
            val += 'rem';
        }
        return val;
    }

})(window, window['lib'] || (window['lib'] = {}));

优势:

  • 兼容性好
  • ios: 6.1系统以上都支持
  • android: 2.1系统以上都支持
  • 大部分主流浏览器都支持
  • 相较于之前的静态布局和百分比方案,页面不会因为伸缩发生变形,自适应效果更佳。

不足:

  • 不是纯css移动适配方案,需要引入js脚本 在头部内嵌一段 js脚本 监听分辨率的变化来动态改变根元素的字体大小,css样式和 js 代码有一定 耦合性,并且必须将改变font-size的代码放在 css 样式之前。

  • 小数像素问题,浏览器渲染最小的单位是像素,元素根据屏幕宽度自适应,通过 rem 计算后可能会出现小数像素,浏览器会对这部分小数四舍五入,按照整数渲染。浏览器在渲染时所做的摄入处理只是应用在元素的尺寸渲染上,其真实占据的空间依旧是原始大小。也就是说如果一个元素尺寸是 0.625px,那么其渲染尺寸应该是 1px,空出的 0.375px 空间由其临近的元素填充;同样道理,如果一个元素尺寸是 0.375px,其渲染尺寸就应该是0,但是其会占据临近元素 0.375px 的空间。会导致:缩放到低于1px的元素时隐时现(解决办法:指定最小转换像素,对于比较小的像素,不转换为 rem 或 vw);两个同样宽度的元素因为各自周围的元素宽度不同,导致两元素相差1px;宽高相同的正方形,长宽不等了;border-radius: 50% 画的圆不圆。

  • Android 浏览器下 line-height 垂直居中偏离的问题。常用的垂直居中方式就是使用line-height,这种方法在Android设备下并不能完全居中。

  • cursor: pointer 元素点击背景变色的问题,对添加了cursor:pointer属性的元素,在移动端点击时,背景会高亮。为元素添加tag-highlight-color:transparent 属性可以隐藏背景高亮。

百分比方案

原理: 使用 百分比% 定义 宽度,高度 用px固定,根据可视区域实时尺寸进行调整,尽可能适应各种分辨率,通常使用max-width/min-width控制尺寸范围过大或者过小。下表是子元素不同属性设置百分比的依据

属性 设置参考
height/width 基于子元素的直接父元素,width相对于父元素的width,height相对于父元素的height
top/bottom 和left/right 相对于直接非static定位的父元素的height/width
padding/margin 不论是垂直方向或者是水平方向,都相对于直接父亲元素的width,与父元素的height无关。
border-radius 相对于自身的宽度

优势:

  • 原理简单,不存在兼容性问题

不足:

  • 如果屏幕尺度跨度太大,相对设计稿过大或者过小的屏幕不能正常显示,在大屏手机或横竖屏切换场景下可能会导致页面元素被拉伸变形,字体大小无法随屏幕大小发生变化。

  • 设置盒模型的不同属性时,其百分比设置的参考元素不唯一,容易使布局问题变得复杂

vh/vw方案

原理: 视口是浏览器中用于呈现网页的区域,移动端的视口通常指的是 布局视口

  • vw : 1vw 等于 视口宽度 的 1%
  • vh : 1vh 等于 视口高度 的 **1% **
  • vmin : 选取 vw 和 vh 中 最小 的那个
  • vmax : 选取 vw 和 vh 中 最大 的那个

使用 css 预处理器把设计稿尺寸转换为 vw 单位,包括 文本,布局高宽,间距 等,使得这些元素能够随视口大小自适应调整。以1080px设计稿为基准,转化的计算表示为

// 以1080px作为设计稿基准
$vw_base: 1080
@function vw($px) {
    @return($px / 1080) * 100vw
}

优势:

  • 纯 css 移动端适配方案,不存在脚本依赖问题
  • 相对于 rem 以根元素字体大小的倍数 定义 元素大小,逻辑清晰简单,视口单位依赖于视口的尺寸 “1vw = 1/100 viewport width”,根据 视口尺寸的百分比 来定义 元素宽度

不足:

  • 存在一些兼容性问题,Android4.4以下不支持

借助media实现rem适配

rem:CSS的长度单位, 根元素字体大小的倍数,只有根元素字体大小有关; html 中的根元素即 html 元素。

大部分浏览器的默认字体大小都是16px,所以1rem = 16px;

rem适配原理:

  • 长度单位都是用 rem 设置
  • 当屏幕尺寸改变时,只需要修改 html 元素的 font-size 即可实现等比适配
  • 在制作页面的时候,只考虑跟设计稿相同的屏幕尺寸即可,其他尺寸屏幕自动适配
    
//对屏幕大小划分了html不同的font-size
@media screen and (min-width: 320px) {html{font-size:50px;}}
@media screen and (min-width: 360px) {html{font-size:56.25px;}}
@media screen and (min-width: 375px) {html{font-size:58.59375px;}}
@media screen and (min-width: 400px) {html{font-size:62.5px;}}
@media screen and (min-width: 414px) {html{font-size:64.6875px;}}
@media screen and (min-width: 440px) {html{font-size:68.75px;}}
@media screen and (min-width: 480px) {html{font-size:75px;}}
@media screen and (min-width: 520px) {html{font-size:81.25px;}}
@media screen and (min-width: 560px) {html{font-size:87.5px;}}
@media screen and (min-width: 600px) {html{font-size:93.75px;}}
@media screen and (min-width: 640px) {html{font-size:100px;}}
@media screen and (min-width: 680px) {html{font-size:106.25px;}}
@media screen and (min-width: 720px) {html{font-size:112.5px;}}
@media screen and (min-width: 760px) {html{font-size:118.75px;}}
@media screen and (min-width: 800px) {html{font-size:125px;}}
@media screen and (min-width: 960px) {html{font-size:150px;}}

小结

  • 适配不同屏幕宽度以及不同dpr,通过动态设置viewport(scale=1/dpr) + 根元素fontSize + rem, 辅助使用vw/vh等来达到适合的显示;
  • 若无需适配可显示1px线条,也可以不动态设置scale,只使用动态设置根元素fontSize + rem + 理想视口;
  • 当视口缩放,计算所得的根元素fontSize也会跟着缩放,即若理想视口(scale=1), iPhone6根元素fontSize=16px; 若scale=0.5, iPhone6根元素fontSize=32px; 因此不必担心rem的计算;
  • !!css单位:以前我认为这样比较好:适配元素rem为单位,正文字体及边距宜用px为单位;现在认为全部用rem即可,包括字体大小,不用px
  • px为单位的元素,需根据dpr有不同的大小,如大小12px, dpr=2则采用24px, 使用sass mixin简化写法;
  • 配合scss函数,简化px2rem转换,且易于维护(若需修改$base-font-size, 无需手动重新计算所有rem单位);
  • px2rem函数的$ base-font-size只跟根元素fontSize的基准【fontSize=16px when 375】,以及设计图的大小有关,按此基准,若设计图为iPhone6二倍稿,则$base-font-size=32px,参数传值直接为设计图标注尺寸;
  • 使用iPhone6(375pt)二倍设计图:宽度750px;
  • 切图使用三倍精度图,以适应三倍屏(这个目前我还没有实际应用过)
  • 设置viewport, data-dpr, font-size的完整js代码见此项目文件flexible.js, 相关的CSS预处理片段见_mixins.scss

你可能感兴趣的:(移动端)