Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

再谈移动端适配和点5像素的由来 #30

Open
zhiqiang21 opened this issue Oct 6, 2016 · 0 comments
Open

再谈移动端适配和点5像素的由来 #30

zhiqiang21 opened this issue Oct 6, 2016 · 0 comments

Comments

@zhiqiang21
Copy link
Owner

前言

这篇文章的内容如题目一样,主要分为两个部分,

  1. 谈谈业内主流的移动端适配解决方案
  2. 点5像素的由来和实现方法

建议在读这篇文章的时候先读下这篇文章《高清屏概念解析与检测设备像素比的方法_20161005》,因为这些文章涉及的很多概念在这篇文章中都会提到。

1.再谈移动端适配

1.1百分比解决方案的缺点

在我们的项目中大量的使用百分比来解决页面在宽度上的自适应,其实在高度上并没有很好的自适应。所以在我们的前端页面会出现一些比较奇怪的问题。比如下面这样:

还有一个比较明显的问题就是:在任何机型上我们的按钮的高度是不会变化自适应的,所以小屏手机我们的按钮看起来很臃肿。

1.2一些常用单位的概念解析

在谈我为什么在我们的项目中引入rem单位的时候,先来详细的了解几个常用的单位概念。

  1. px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的(也就是说是跟物理设备有关的)。拿高清屏和普通屏来做对比就是普通屏幕的1个像素点就是1个物理像素点,而高清屏的1个像素点4个物理像素点
  2. em相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸。em单位的特点:1. em的值并不是固定的;2. em会继承父级元素的字体大小。
  3. rem相对长度单位。rem是CSS3新增的一个相对单位,这个单位引起了广泛关注。这个单位与em有什么区别呢?区别在于使用rem为元素设定字体大小时,仍然是相对大小,但相对的只是HTML根元素。这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根元素就成比例地调整所有字体大小,又可以避免字体大小逐层复合的连锁反应。

任意浏览器的默认字体高都是16px。所有未经调整的浏览器都符合: 1rem=16px。那么12px=0.75rem,10px=0.625rem。为了简化font-size的换算,需要在css中的html选择器中声明font-size=62.5%,这就使rem值变为 16px*62.5%=10px, 这样12px=1.2rem, 10px=1rem, 也就是说只需要将原来的px数值除以10,然后换上rem作为单位就行了。

注意:当我们在根节点<html>上设置了font-size基准值以后,在文档中有使用rem单位的属性值都是相对于根节点font-size的一个相对值。比如说一些元素的属性如width height margin等。也正是这个原因,现在很多网站的移动端网站都在使用rem单位作为适配工具。

在使用rem的时候比较麻烦的就是pxrem换算的问题。上面的除10的方案是比较简单的。但是根据设置基准值的不同换算方法也不一样。如果我们使用scss来写我们的样式表的话,解决方法就比较简单了,代码如下:

@function px2rem($px){
    @return ($px/10)/2 + rem;  //  相当于$px/20  +rem
}

width:px2rem(100px);   //5rem
height:px2rem(200px);  //10rem

1.3为什么引入 rem 单位

由上面的内容已经知道百分比单位在多屏幕适配上的缺点和rem单位的优点。那么rem单位能为我们的开发带来什么呢?来看一个常见的多列布局,淘宝移动端的商品列表页,如下图:

设计稿(不管是iphone6的二倍稿还是iphone5的二倍稿)中给展示商品的坑位的宽高比是固定的。为了能够使这个列表在不同的屏幕上达到最佳的显示效果,需要保持宽度和高度比一致。如果使用百分比肯定是满足不了这个需求的,因为高度上我们没有办法控制。

iphone6和iphone5的屏幕宽度和高度比不一样,当我们使用百分比做多列布局的时候就会出现下面的这种情况:

所以为了满足商品坑位在不同屏幕上的宽高比一致,淘宝使用的是rem的解决方案,也是目前最好的解决方案。

1.3为什么将计算根元素的font-size值的js放在head标签中

设置根节点font-size值的方法,第一种是使用css的Media queries,示例代码如下:

@media (min-device-width : 375px) and (max-device-width : 667px) and (-webkit-min-device-pixel-ratio : 2){
      html{font-size: 37.5px;}
}

上面的这种方式在我之前做过的项目中使用了一段时间。上面的设置方法有一个很明显的问题font-size是在一个屏幕宽度的区间上有一个基准值。像安卓手机种类的繁多,屏幕大小就更多的情况下,上面的方法很鸡肋。

第二种解决方案,就是使用JavaScript根据当前屏幕的宽度动态计算font-size值,这种方法可以保证屏幕宽度连续变化的时候,font-size基准值也是连续变化的

计算方法可以参考这篇文章《根据iPhone6设计稿动态计算rem值》

那么最后一个问题也来了:为什么将计算rem单位的js放在head标签里面?

一句话总结:在浏览器中文档流是从上往下加载渲染的。为了保证发生不必要的_重绘_或者是_重排_肯定是越早给根节点设置font-size值越好。

1.4什么情况下使用rem来布局和注意的问题

  1. 整体的布局还是使用百分比
  2. 使用rem的最佳场景是,遇到例如多列带有图片的列表,常常需要图片固定宽高比例
  3. 研究了一些网站,比如淘宝,对字体字体一般情况建议使用px
  4. 出现1px像素线的地方,仍旧使用border-width:1px;而不是border-width:.1rem;

2.点5像素的由来

在前言中推荐阅读的那篇文章中已经知道在 高清屏(Retina)中控制显示的最小的物理单元包括4个基本的像素点,而普通屏幕1个点像素就是1个物理像素单元。所以在高清屏(Retina)出来之前,就算我们在css中写 0.5px,对于显示屏幕也是不识别的。但是在高清屏(Retina)中我们可以通过间接的方法实现0.5px的效果。

下面的这张图可能能让我们迅速回忆上篇文章的内容

来看一段简单的示例代码加深理解:

<div class="item1"></div>
<div class="item2"></div>

css代码如下:

.item1{
    width:100px;
    height:50px;
    border-top:1px solid #000;
    float: left;
    margin-top:10px;
}

.item2{
    width:100px;
    height:50px;
    margin-top:10px;
    border-top:.5px solid #de1dfb;
    float: left;
}

chrome中的显示效果如下图:

我把这张图截取下来放到 PS 中放大可以很明显的看到一些问题。就是高清屏实际上是用了两排的物理像素点来显示1px的像素线。且不做特殊处理的话1px0.5px的在Chrome浏览器中显示效果是相同的。也就是说Chrome浏览器不识别0.5px

但是相同的代码我在 safari浏览器中的效果却是下面的效果:

下面的效果是我在PS中做了放大后的效果。

所以,我们可以得出一个结论:对于0.5px不同浏览器对它的识别是存在差异的。

以下这张图是一位网友对一些常用设备是否识别0.5px做的统计:

其实从视觉的感受上来说0.5px像素的显示效果确实是比1px的显示效果要好很多。大多数情况下也更符合设计稿上的1px线的效果。那么我们有没有办法可以让1px在不同的浏览器和设备中显示真正的1像素的效果呢?

答案是肯定定的。

2.1传统的实现方法,也就是我们的项目中正在使用的方法:

伪元素 + css3的缩放巧妙地实现;

基本步骤就是:

  1. 设置目标元素定位参照
  2. 给目标元素添加一个伪元素before或者after,并设置绝对定位
  3. 给伪元素添上1px的边框
  4. 设置伪元素的宽高为目标元素的2倍
  5. 缩小0.5倍(变回目标元素的大小)
  6. 使用border-box把border包进来

先来看一个1像素0.5像素的显示效果,下面的截图是在chrome中:

实现的代码如下:

<div class="item4">测试用的边框</div>
<div class="item5">测试用的边框</div>
.item4, .item5 {
    width: 200px;
    height: 100px;
    position: relative;
}

.item4 {
    border: 1px solid #000;
}

.item5::before {
    content: '';
    position: absolute;
    width: 200%;
    height: 200%;
    border: 1px solid #000;
    transform-origin: 0 0;
    transform: scale(0.5, 0.5);
    box-sizing: border-box;
}

可以很明显的看到缩放前和缩放后的效果。在ps中把上面的截图放大很多倍以后我们可以看到显示细节。**缩放以后确实是使用的最小的物理像素单元来显示边框。**如下图:

注意:按照css3 transformscale的定义,理论上边框可以任意细(1/n px)。但是上图中已经是物理设备能够使用的最小的物理单元了,那么我们继续缩放会有什么现象呢?

使用一下代码

<div class="item6">测试用的边框</div>
.item6::before {
    content: '';
    position: absolute;
    width: 400%;
    height: 400%;
    border: 1px solid #000;
    transform-origin: 0 0;
    transform: scale(0.25, 0.25);
    box-sizing: border-box;
}

chrome中显示效果如下,可以很明显的看到颜色变浅了,但是是否变得更细了我们肉眼无法分辨:

在PS中放大以后的效果:

很明显可以看到线并没有变细,但是线的颜色确实是变浅了。这样的结果也符合我们的预期,就是已经到了物理设备能够显示一块颜色的最小的物理单元了。

2.2js动态设置viewport的方案

一些大厂(所谓的淘宝)的解决方案就是使用js动态获取屏幕的设备像素比,然后动态设置viewport。当然也是我认为目前最好的解决方案。

meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no');

我们知道,一般我们获取到的视觉稿大部分是iphone6的,所以我们看到的尺寸一般是双倍大小的,在使用rem之前,我们一般会自觉的将标注/2,其实这也并无道理,但是当我们配合rem使用时,完全可以按照视觉稿上的尺寸来设置。

  1. 设计给的稿子双倍的原因是iphone6这种屏幕属于高清屏,也即是设备像素比(device pixel ratio)dpr比较大,所以显示的像素较为清晰。
  2. 一般手机的dpr是1,iphone4,iphone5这种高清屏是2,iphone6s plus这种高清屏是3,可以通过js的window.devicePixelRatio获取到当前设备的dpr,所以iphone6给的视觉稿大小是(*2)750×1334了。
  3. 拿到了dpr之后,我们就可以在viewport meta头里,取消让浏览器自动缩放页面,而自己去设置viewport的content例如(这里之所以要设置viewport是因为我们要实现border1px的效果,在scale的影响下,高清屏中就会显示成0.5px的效果)

总结:

由以上两个部分的内容可以知道,不管是在做多终端适配还是实现点5像素的线,都是存在css和js两种解决方案的。两种方案相比来说,我都认为使用JavaScript的解决方案都胜一筹。唯一的缺点就是会在html的head标签中引入一段js代码 。

记得刚入行的时候,业内有一个叫“雅虎军规”的东西,是好多前端做页面优化参考的标准。其中有一条就是要将js文件放在body标签的底部。到现在很多年了,时代在变化,我们的网络带宽也在提升,“雅虎军规”中的一些内容,可能有些已经不适合我们现在应用开发的场景。而且我觉的做技术不应该拘泥于已有的一些规定,而应该按照我们的场景选择适合我们的技术和解决方案。

纵然有瑕疵,有些也是可以通过技术手段来解决的。比如在head标签中引入计算font-size和检测设备独立像素比的js的时候,会担心js的执行阻塞页面的渲染。而我们完全可以通过review的方式确定js代码的执行不会出现阻塞,而影响文档流的加载。

还是那句话:没有十全十美的技术方案,只有适合不合适当前业务场景的技术方案。

参考文章

css3的字体大小单位[rem]到底好在哪?
移动端高清、多屏适配方案
使用Flexible实现手淘H5页面的终端适配
lib-flexible源代码

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant