最近翻了一下关于移动端的rem的使用,怎样最方便。在读到流云诸葛的一篇关于《从网易与淘宝的font-size思考前端设计稿与工作流》的文章后,来总结一下。
然而根据我以往做移动端web项目的时候,设计稿一般是640或者750的,我一般会在head里面这样写:
<meta name="viewport" content="width=750, user-scalable=no, target-densitydpi=device-dpi"><!-- width取值与页面定义的宽度一致 -->
做移动端活动的时候结构也一般这样:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta content="yes" name="apple-mobile-web-app-capable"> <!-- ios7.0版本以后,safari上已看不到效果,可以删除-->
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="telephone=no" name="format-detection">
<meta content="email=no" name="format-detection">
<title>viewport通用</title>
<script type="text/javascript">
var phoneWidth = parseInt(window.screen.width);
var phoneScale = phoneWidth / 750;
var ua = navigator.userAgent;
if (/Android (\d+\.\d+)/.test(ua)) {
var version = parseFloat(RegExp.$1);
if (version > 2.3) {
document.write('<meta name="viewport" id="vie" content="width=750, minimum-scale = ' + phoneScale + ', maximum-scale = ' + phoneScale + ', target-densitydpi=device-dpi">');
} else {
document.write('<meta name="viewport" id="vie" content="width=750, target-densitydpi=device-dpi">');
}
} else {
document.write('<meta id="vie" name="viewport" content="width=750, user-scalable=no, target-densitydpi=device-dpi, minimal-ui">');
}
</script>
<link style="text/css" rel="stylesheet" href="home.css">
<script type="text/javascript" src="js/app.js"></script>
</head>
<body>
<!--页面内容-->
</body>
</html>
一般的移动页面通用写法:
<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
也就是下面要想总结的。
在博客园白树的博文中有一段指出,如果html5要适应各种分辨率的移动设备,应该使用rem这样的尺寸单位,同时给出了一段针对各个分辨率范围在html上设置font-size的代码:
html{font-size:10px}
@media screen and (min-width:321px) and (max-width:375px){html{font-size:11px}}
@media screen and (min-width:376px) and (max-width:414px){html{font-size:12px}}
@media screen and (min-width:415px) and (max-width:639px){html{font-size:15px}}
@media screen and (min-width:640px) and (max-width:719px){html{font-size:20px}}
@media screen and (min-width:720px) and (max-width:749px){html{font-size:22.5px}}
@media screen and (min-width:750px) and (max-width:799px){html{font-size:23.5px}}
@media screen and (min-width:800px){html{font-size:25px}}
在实际项目中,把与元素尺寸有关的css,如width,height,line-height,margin,padding等都以rem作为单位,这样页面在不同设备下就能保持一致的网页布局。举例来说,网页有一个.item类,设置了width为3.4rem,该类在不同分辨率下对应的实际宽度如下:
321px <= device-width <= 375px,font-size:11px ---> .item的width:(11*3.4)px
376px <= device-width <= 414px,font-size:12px ---> .item的width:(12*3.4)px
415px <= device-width <= 639px,font-size:15px ---> .item的width:(15*3.4)px
640px <= device-width <= 719px,font-size:20px ---> .item的width:(20*3.4)px
720px <= device-width <= 749px,font-size:22.5px ---> .item的width:(22.5*3.4)px
750px <= device-width <= 799px,font-size:23.5px ---> .item的width:(23.5*3.4)px
800px <= device-width ,font-size:25px ---> .item的width:(25*3.4)px
注:说明一下,在流云诸葛的那篇文章中,上面的font-size为11px的时候,item类的width为34px,为啥不是(11*3.4)px。难道这儿的font-size不是设置的html的font-size?
上面一看,缺点其实是很多的,我觉得要这样做估计页面很少,或者是一个啥活动页面这样写兼容也可以。主要缺陷有两点如下:
- 根据不同分辨率的屏幕,设置font-size也太多了吧,在实际项目中一般也用不了这么多。
- 通过font-size的值,每次去设置margin,width,height,padding等的长度时,都要通过设计搞再计算一下才行。比如比如40px / 11px,你一下说不出结果吧。
有些web app的设计搞确实不复杂,比较简单,比如拉勾网的布局结构如图所示:
它的页面有一个特点,就是:
- 顶部与底部的bar不管分辨率怎么变,它的高度和位置都不变
- 中间每条招聘信息不管分辨率怎么变,招聘公司的图标等信息都位于条目的左边,薪资都位于右边
这种app是一种典型的弹性布局:关键元素高宽和位置都不变,只有容器元素在做伸缩变换。对于这类app,记住一个开发原则就好:文字流式,控件弹性,图片等比缩放。
总之一句话,简单的设计稿,简单处理,不必写得那么复杂。
网易的做法
第一,视口设置如下:
<meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1">
第二,当deviceWidth大于设计稿的横向分辨率时,html的font-size始终等于横向分辨率/body元素宽:
如图所示:
640*680
641*680
之所以这么干,是因为当deviceWidth大于640时,则物理分辨率大于1280(这就看设备的devicePixelRatio这个值了),应该去访问pc网站了。事实就是这样,你从手机访问网易,看到的是触屏版的页面,如果从pad访问,看到的就是电脑版的页面。
在dom ready以后,通过以下代码设置html的font-size:
var deviceWidth = document.documentElement.clientWidth;
if(deviceWidth > 640) deviceWidth = 640;
document.documentElement.style.fontSize = deviceWidth / 6.4 + 'px';
这个6.4怎么来的,当然是根据设计稿的横向分辨率/100得来的。
如果设计稿基于iphone6,横向分辨率为750,body的width为750 / 100 = 7.5rem
如果设计稿基于iphone4/5,横向分辨率为640,body的width为640 / 100 = 6.4rem
6.4只是举个例子,如果是750的设计稿,应该除以7.5。
font-size可能需要额外的媒介查询,并且font-size不能使用rem,如网易的设置:
@media screen and (max-width:321px){
.m-navlist{font-size:15px}
} @media screen and (min-width:321px) and (max-width:400px){
.m-navlist{font-size:16px}
} @media screen and (min-width:400px){
.m-navlist{font-size:18px}
}
研究了下网易新闻手机端的页面,没有对字体使用px单位,还是用的是rem。
淘宝的做法
淘宝的效果跟网易的效果其实是类似的,随着分辨率的变化,页面元素的尺寸和间距都相应变化,这是因为淘宝的尺寸也是使用了rem的原因。在介绍它的做法之前,先来了解一点关于viewport的知识,通常我们采用如下代码设置viewport:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
这样整个网页在设备内显示时的页面宽度就会等于设备逻辑像素大小,也就是device-width。这个device-width的计算公式为:
设备的物理分辨率/(devicePixelRatio * scale),在scale为1的情况下,device-width = 设备的物理分辨率/devicePixelRatio 。
devicePixelRatio称为设备像素比,每款设备的devicePixelRatio都是已知,并且不变的,目前高清屏,普遍都是2,不过还有更高的,比如2.5, 3 等,魅族note的手机的devicePixelRatio就是3。淘宝触屏版布局的前提就是viewport的scale根据devicePixelRatio动态设置:
在devicePixelRatio为2的时候,scale为0.5
在devicePixelRatio为3的时候,scale为0.3333
这么做目的当然是为了保证页面的大小与设计稿保持一致了,比如设计稿如果是750的横向分辨率,那么实际页面的device-width,以iphone6来说,也等于750,这样的话设计稿上标注的尺寸只要除以某一个值就能够转换为rem了。通过js设置viewport的方法如下:
淘宝布局的第二个要点,就是html元素的font-size的计算公式,font-size = deviceWidth / 10:
接下来要解决的问题是,元素的尺寸该如何计算,比如说设计稿上某一个元素的宽为150px,换算成rem应该怎么算呢?这个值等于设计稿标注尺寸/该设计稿对应的html的font-size。拿淘宝来说的,他们用的设计稿是750的,所以html的font-size就是75,如果某个元素时150px的宽,换算成rem就是150 / 75 = 2rem。总结下淘宝的这些做法:
(1)动态设置viewport的scale
var scale = 1 / devicePixelRatio;
document.querySelector('meta[name="viewport"]').setAttribute('content','initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
(2)动态计算html的font-size
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
(3)布局的时候,各元素的css尺寸=设计稿标注尺寸/设计稿横向分辨率/10
(4)font-size可能需要额外的媒介查询,并且font-size不使用rem,这一点跟网易是一样的。
最后还有一个情况要说明,跟网易一样,淘宝也设置了一个临界点,当设备竖着时横向物理分辨率大于1080时,html的font-size就不会变化了,原因也是一样的,分辨率已经可以去访问电脑版页面了。
关于这种做法的具体实现,淘宝已经给我们提供了一个开源的解决方案,具体请查看:
https://github.com/amfe/lib-flexible
比较网易与淘宝的做法
共同点:
- 都能适配所有的手机设备,对于pad,网易与淘宝都会跳转到pc页面,不再使用触屏版的页面
- 都需要动态设置html的font-size
- 布局时各元素的尺寸值都是根据设计稿标注的尺寸计算出来,由于html的font-size是动态调整的,所以能够做到不同分辨率下页面布局呈现等比变化
- 容器元素的font-size都不用rem,需要额外地对font-size做媒介查询
- 都能应用于尺寸不同的设计稿,只要按以上总结的方法去用就可以了
不同点:
- 淘宝的设计稿是基于750的横向分辨率,网易的设计稿是基于640的横向分辨率,还要强调的是,虽然设计稿不同,但是最终的结果是一致的,设计稿的尺寸一个公司设计人员的工作标准,每个公司不一样而已
- 淘宝还需要动态设置viewport的scale,网易不用
- 最重要的区别就是:网易的做法,rem值很好计算,淘宝的做法肯定得用计算器才能用好了 。不过要是你使用了less和sass这样的css处理器,就好办多了,以淘宝跟less举例,我们可以这样编写less:
//定义一个变量和一个mixin @baseFontSize: 75;//基于视觉稿横屏尺寸/100得出的基准font-size
.px2rem(@name, @px){
@{name}: @px / @baseFontSize * 1rem;
} //使用示例: .container {
.px2rem(height, 240);
} //less翻译结果:
.container {
height: 3.2rem;
}