屏幕适配和SASS实现pxTorem

一生装逼成今日,四海无人对夕阳。

##适配设计稿和屏幕尺寸问题

做一个app内置的h5页面时前端经常要做的事

那么屏幕尺寸的像素适配就是不得不思考的问题

比如下面这样的页面

UI通常会出750px宽度的设计稿,即图中这样的样式iphone6的像素

375px 的width X 2倍的dpr

接下来就是前端的事情了

####1、viewport


先设置viewport

viewport不局限于浏览器的可视区域大小,它可能比浏览器的可视区域要大,也可能比浏览器的可视区域要小。一般来讲,移动设备上的viewport都是要大于浏览器可视区域的,这是因为考虑到移动设备的分辨率相对于桌面电脑来说都比较小,所以为了能在移动设备上正常显示那些传统的为桌面浏览器设计的网站,移动设备上的浏览器都会把自己默认的viewport设为980px或1024px。这也是手机上看电脑网页为什么缩小的原因。因此加下面一段代码可以让viewport等于手机屏幕宽度。这时候看电脑的网页,会出现左右滑动,因为页面的元素css宽度超过了viewport。

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

####2、可以使用js先计算font-size


大家都知道可以通过设置根节点的font-size来适配屏幕

那么这时js的计算就可以十分精准了,如下

document.documentElement.style.fontSize = clientWidth / 10 + 'px';

这里除以10是为了方便计算rem计算公式是1 ÷ 根节点的font-size × 需要转换的像素值 = rem值

之后在css里算rem就行了。

这里还有个计算retina屏幕的2倍dpr的问题,这里按下不表,因为安卓的dpr按照1处理

这里如果追求精确,给出手淘的解决方法:使用阿里的名叫lib-flexible的库,而这个库就是用来解决H5页面终端适配的。

lib-flexible是什么?

lib-flexible是一个制作H5适配的开源库,可以点击这里下载相关文件,获取需要的JavaScript和CSS文件。

当然你可以直接使用阿里CDN:

<script src="http://g.tbcdn.cn/mtb/lib-flexible//??flexible_css.js,flexible.js"></script>

将代码中的换成对应的版本号0.3.4。

使用方法

lib-flexible库的使用方法非常的简单,只需要在Web页面的<head></head>中添加对应的flexible_css.js,flexible.js文件:

第一种方法是将文件下载到你的项目中,然后通过相对路径添加:

<script src="build/flexible_css.debug.js"></script> <script src="build/flexible.debug.js"></script>

或者直接加载阿里CDN的文件:

<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script>

另外强烈建议对JS做内联处理,在所有资源加载之前执行这个JS。执行这个JS后,会在<html>元素上增加一个data-dpr属性,以及一个ont-size样式。JS会根据不同的设备添加不同的data-dpr值,比如说2或者3,同时会给html加上对应的font-size的值,比如说75px

如此一来,页面中的元素,都可以通过rem单位来设置。他们会根据html元素的font-size值做相应的计算,从而实现屏幕的适配效果。

除此之外,在引入lib-flexible需要执行的JS之前,可以手动设置meta来控制dpr值,如:

<meta name="flexible" content="initial-dpr=2" />

其中initial-dpr会把dpr强制设置为给定的值。如果手动设置了dpr之后,不管设备是多少的dpr,都会强制认为其dpr是你设置的值。在此不建议手动强制设置dpr,因为在Flexible中,只对iOS设备进行dpr的判断,对于Android系列,始终认为其dpr为1。

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;
}

flexible的实质

flexible实际上就是能过JS来动态改写meta标签,代码类似这样:

var metaEl = doc.createElement('meta');
var scale = isRetina ? 0.5:1;
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
    document.documentElement.firstElementChild.appendChild(metaEl);
} else {
    var wrap = doc.createElement('div');
    wrap.appendChild(metaEl);
    documen.write(wrap.innerHTML);
}

事实上他做了这几样事情:

动态改写标签

给<html>元素添加data-dpr属性,并且动态改写data-dpr的值

给<html>元素添加font-size属性,并且动态改写font-size的值

最后:

border往往也要缩减成0.5px,但是这个px最小就1,设计师不会妥协,怎么办呢

见我之前的文章:CSS实现0.5px border的方法

===

##sass实现pxTorem

既然要适配屏幕,那么rem计算是回避不了的,不过既然除了border都可以用rem那就索性简化计算

简单的js和css的方法:

通过设置fontsize为10px,使得计算简单

document.documentElement.style.fontSize = '10px';

html {font-size: 62.5%;/*10 ÷ 16 × 100% = 62.5%*/}
body {font-size: 1.4rem;/*1.4 × 10px = 14px */}
h1 { font-size: 2.4rem;/*2.4 × 10px = 24px*/}

另一种就是使用sass的function来写一个pxTorem的函数啦

$browser-default-font-size: 16px !default;//变量的值可以根据自己需求定义

而且需要在html根元素中显示的声明font-size:

html {
    font-size: $browser-default-font-size;
}

然后通过@function来实现px转为rem计算:

@function pxTorem($px){//$px为需要转换的字号
    @return $px / $browser-default-font-size * 1rem;
}

使用就简单多了:

//SCSS
html {
    font-size: $browser-default-font-size;
}
.header {
    font-size: pxTorem(12px);
}

//CSS
html {
  font-size: 16px; }

.header {
  font-size: 0.75rem; }

当然结合上面的适配屏幕有实际使用的方法,附demo

index.html:

<!DOCTYPE HTML>
<html id="body">
    
    <head>
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta charset="utf-8">
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, width=device-width">
        <title>demo</title>
        <!-- build:css(.tmp) .css -->
        <link rel="stylesheet" href="index.css"/>
        <!-- endbuild -->
    </head>
    
    <body>
        <div class="content">
            <p>this is test</p>
        </div>
        <script type="text/javascript">
            document.getElementById('body').style.fontSize = document.documentElement.clientWidth / 10+ 'px';
        </script>
    </body>
    
</html>

index.scss:

$uiWidth:750px;//设计稿

@function pxToRem($px){
      @return 10 * $px/$uiWidth * 1rem;
}
div{
    width:pxToRem(200px);
    background-color: #bbccff;
    height: pxToRem(800px);
}
p{
    font-size: pxToRem(32px);
}

实际效果(chrome全部只有1/2像素,但是在iphone6的实际就是对了):

=== ##最后

还有直接输入就能转换的sublime插件的,不过今天看了火影博人传觉得:

有些时候这些自己实现不完美的小东西自己实现一遍真的理解很多。

我也是自己研究,欢迎交流,文章中如果有不对的地方还望在评论指出,多谢!

附参考文章:

Sass基础——Rem与Px的转换

使用Flexible实现手淘H5页面的终端适配

AsteriskYk - 猪场前端搬砖 in categories web  tagged with css  scss