从 font-size 探索移动应用的前端开发

  • 2016-12-18
  • 35
  • 0

2016-12-14 在 Dynamsoft 公司月度例会上所做分享的部分内容

讲 font-size 的原因

自公司网站首页改版之后我在慢慢的将一些老页面改成响应式,改版过程中我发现了一些值得完善和改进的地方,例如设计稿的前端实现、对移动端开发的理解以及工作中的沟通交流等问题;刚好最近在博客园上看到一篇博文 – 《移动 web 资源整理》,觉得里面对移动端开发的一些要素总结的还不错,于是我打算以这篇文章中讲到的 font-size 为切入点来整理一下我所理解的移动端开发,以及为什么我要用现在的方式来开发我们公司的网站。

上述文章中有提到:如果 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,该类在不同分辨率下对应的实际宽度如下:

device-width <= 320px  ,font-size:10px               --->  .item 的 width:34px
321px <= device-width <= 375px,font-size:11px        --->  .item 的 width:37.4px
376px <= device-width <= 414px,font-size:12px        --->  .item 的 width:40.8px
415px <= device-width <= 639px,font-size:15px        --->  .item 的 width:51px
640px <= device-width <= 719px,font-size:20px        --->  .item 的 width:68px
720px <= device-width <= 749px,font-size:22.5px      --->  .item 的 width:76.5px
750px <= device-width <= 799px,font-size:23.5px      --->  .item 的 width:79.9px
800px <= device-width         ,font-size:25px        --->  .item 的 width:85px

以上代码看起来好像没啥问题,而且还考虑得很全,响应式设计不就应该是这么干的吗?但是从工作量和复杂度方面来考虑,它有以下几个不足:

  1. .item 类在所有设备下的 width 都是 3.4rem,但在不同分辨率下的实际像素是不一样的,所以在有些分辨率下,width 的界面效果不一定合适,有可能太宽,有可能太窄,比如 iphone4s 就经常会出现错位的情况;这时候就要对 width 进行调整,那么就需要针对 .item 写媒介查询的代码,为该分辨率重新设计一个 rem 值。然而,这里有7种媒介查询的情况,css 又有很多跟尺寸相关的属性,哪个属性在哪个分辨率范围不合适都是不定的,最后会导致要写很多的媒介查询才能适配所有设备;
  2. 以上代码中给出的7个范围下的 font-size 不一定是合适的,这7个范围也不一定合适,实际有可能不需要这么多,所以找出这些个范围,以及每个范围最合适的 font-size 也很麻烦;
  3. 在布局的时候 rem 都得根据某个分辨率 html 的 font-size 去算,这个计算可不见得每次都那么容易,比如 40px / 23.5px,这个 rem 值口算不出来吧!由此可见这无疑给开发人员增加了很多重复计算的工作量;
  4. 设计稿都是以分辨率来标明尺寸的,前端在根据设计稿里各个元素的像素尺寸转换为 rem 时,该以哪个 font-size 为准呢?这需要去写并且一次次调试才能知道。

上面的这几个是我所能想到的不足之处,它的弊端可能会有更多;正是因为有以上提到的这么多不足,所以我觉得该文章给出的这种适配方式并不是特别好,写起来太耗时太麻烦了。如果想在设计师和前端开发人员有限的情况下按时完成工作任务,我觉得就必须找到更简单更有效率的方法来做移动端开发。

下面的例子是我在知乎以及一些前端开发公众号上搜集的,他们的实现方法各不相同,但都很好的满足了各自网站的需求:

拉勾网 – 简单问题简单解决

有些 web app 并一定很复杂,比如拉勾网,看过它在 iphone4,iphone6,ipad 等不同分辨率的移动设备下的样子就会发现,它的页面有一些共同的特点:

  1. 顶部与底部的 bar 不管分辨率怎么变,它的高度和位置都不变;
  2. 中间每条招聘信息不管分辨率怎么变,招聘公司的图标等信息都位于条目的左边,薪资都位于右边,且高度也是固定的;

这种 app 是一种典型的弹性布局:关键元素高宽和位置都不变,只有容器元素在做伸缩变换。对于这类 app,记住一个开发原则就好:文字流式控件弹性图片等比缩放

image

这是一套最基本的适配规则,对于像拉勾网这种简单 web app 来说已经足够了,这套规则也是后面要说的 rem 布局的基础。另外对于拉勾网这种 app 在小屏幕设备上可能需要额外的媒介查询来对布局进行调整。举例来说,因为现在很多设计稿是根据 iphone6 的尺寸来的,而 iphon6 设备宽的逻辑像素是 375px,而 iphone4 的逻辑像素是 320px,所以如果你根据 iphon6 设计稿做出来的东西,在 iphone4 里面可能显示不下。

但如果前端拿到的是根据 iphone4 的设计稿,那就没有问题,比4分辨率大的设备肯定能显示根据4的尺寸做出来的东西。

网易 – 内容丰富、排版相对固定

先来看看网易在不同分辨率下呈现的效果;从不同分辨率的设备呈现的效果来看,随着分辨率的增大,页面的效果会发生明显变化,主要体现在各个元素的宽高与间距。

iphone 6 明显比 iphone 4 的导航栏明显要高。能够达到这种效果的根本原因就是因为网易页面里给这些元素的尺寸都使用了 rem 作为单位。我们在上面有提到,使用 rem 布局以及在 html 上根据不同分辨率设置不同 font-size 的做法有许多弊端,那网易是如何解决的呢?

最根本的原因在于: 网易页面上 html 的 font-size 不是预先通过媒介查询在 css 里定义好的,而是通过 js 计算出来的,所以当分辨率发生变化时,html 的 font-size 就会变,不过这得在你调整分辨率后,刷新页面才能看得到效果

它是根据什么计算的,这就跟设计稿有关了,拿网易来说,它目前版本的设计稿应该是基于 iphone6 来的,所以它的设计稿竖直放时的横向分辨率为 750px,为了计算方便,于是给 html 取一个 100px 的 font-size 为参照,那么 body 元素的宽度就可以设置为 width: 7.5rem; 于是 html 的 font-size = deviceWidth / 7.5。这个 deviceWidth 就是 viewport 设置中的那个 deviceWidth。根据这个计算规则,可计算出网易在不同分辨率的设备中 html 的 font-size 大小如下:

deviceWidth = 320,font-size = 320 / 7.5 = 42.66666px
deviceWidth = 375,font-size = 375 / 7.5 = 50px
deviceWidth = 414,font-size = 414 / 7.5 = 55.2px
deviceWidth = 500,font-size = 500 / 7.5 = 66.66666px

我们可以通过 chrome 的模拟工具查看网易在 320*680,375*680,414*680,500*680 分辨率下 html 的 font-size 是不是与上面计算的一致。

这个 deviceWidth 通过 document.documentElement.clientWidth 就能取到了,所以当页面的 dom ready 后,做的第一件事情就是:

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

这个 7.5 刚才说过是根据设计稿的横向分辨率/100得来的。

总结一下网易的做法:

  1. 先拿设计稿竖着的横向分辨率除以 100 得到 body 元素的宽度:
    如果设计稿基于 iphone6,横向分辨率为 750,body 的 width 为 750 / 100 = 7.5rem
    如果设计稿基于 iphone4/5,横向分辨率为 640,body 的 width 为 640 / 100 = 6.4rem
  2. 布局时,设计图标注的尺寸除以 100 得到 css 中的尺寸,比如:

    设计稿中 header 高度为 90px,那么在 css 中则应该这样设置:.header = 90 / 100 =9.0rem;

  3. 在 dom ready 以后,通过以下代码设置 html 的 font-size:
    document.documentElement.style.fontSize = document.documentElement.clientWidth / 7.5 + 'px';

淘宝 – 内容丰富、改动相对频繁

看看淘宝在不同分辨率下呈现的效果:淘宝的效果跟网易的效果其实是类似的,随着分辨率的变化,页面元素的尺寸和间距都相应变化,这是因为淘宝的尺寸也是使用了 rem 的原因。在介绍它的做法之前,先来了解一点关于 viewport 的知识,通常我们采用如下代码设置 viewport:

&lt;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 的方法如下:

var scale = 1 / devicePixelRatio;
document.querySelector('meta[name="viewport"]').setAttribute('content','initial-scale=' + scale + ', 
maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');

淘宝布局的第二个要点就是: 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

比较网易与淘宝的做法

共同点:

  1. 都能适配所有的手机设备,对于 pad,网易与淘宝都会跳转到 pc 页面,不再使用触屏版的页面
  2. 都需要动态设置 html 的 font-size
  3. 布局时各元素的尺寸值都是根据设计稿标注的尺寸计算出来,由于 html 的 font-size 是动态调整的,所以能够做到不同分辨率下页面布局呈现等比变化
  4. 设计稿都是基于 750 的横向分辨率

不同点:

  1. 淘宝还需要动态设置 viewport 的 scale,网易不用
  2. 最重要的区别就是:网易的做法,rem 值很好计算,淘宝的做法肯定还得用计算器才能算的清楚了。
  3. 淘宝对 font-size 字体没有采用 rem ,依然用的是 px ;而网易的 font-size 的单位是 rem;

前端开发如何与设计协作

前端与设计师的协作应该是比较简单的,最重要的是要规范设计提供给你的产物;

通常对于前端来说,我们需要设计师提供标注尺寸后的设计稿以及各种元素的切图文件,有了这些就可以开始布局了。

考虑到 Retina 显示屏以及这么多移动设备分辨率却不一样的问题,那么设计师应该提供多套设计稿吗?从网易和淘宝的做法来看,应该是不用了,我们可以按照设计稿,先做出一套布局,按照以上方法做适配,由于是等比适配,所以各个设备的视觉效果差异应该会很小,当然也排除不了一些需要媒介查询特殊处理的情况,这肯定避免不了的。

下面这张图是淘宝设计师在知乎上分享的工作流程图:

image

解释一下就是:

  1. 第一步,视觉设计阶段,设计师按宽度 750px(iPhone 6)做设计稿,除图片外所有设计元素用矢量路径来做。设计定稿后在 750px 的设计稿上做标注,输出标注图。同时等比放大 1.5 倍生成宽度 1125px 的设计稿,在 1125px 的稿子里切图。
  2. 第二步,输出两个交付物给开发工程师:一个是程序用到的 @3x 切图资源,另一个是宽度 750px 的设计标注图。
  3. 第三步,开发工程师拿到 750px 标注图和 @3x 切图资源,完成 iPhone 6(375pt)的界面开发。此阶段不能用固定宽度的方式开发界面,得用自动布局(auto layout),方便后续适配到其它尺寸。
  4. 第四步,适配调试阶段,基于 iPhone 6 的界面效果,分别向上向下调试 iPhone 6 plus(414pt) 和 iPhone 5S 及以下(320pt)的界面效果。由此完成大中小三屏适配。

注意第三步,就要使用我们以上介绍的网易跟淘宝的适配方法了。假如公司设计稿不是基于 750 的怎么办,其实很简单,按上图做一些相应替换即可,但是流程和方法还是一样的。

解释一下为什么要在 @3x 的图里切,这是因为现在市面上也有不少像魅蓝 note 这种超高清屏幕,devicePixelRatio 已经达到 3 了,这个切图保证在所有设备都清晰显示。

评论

还没有任何评论,你来说两句吧