前言
这篇文章的内容如题目一样,主要分为两个部分,
谈谈业内主流的移动端适配解决方案
点5像素的由来和实现方法
建议在读这篇文章的时候先读下这篇文章《高清屏概念解析与检测设备像素比的方法_20161005》,因为这些文章涉及的很多概念在这篇文章中都会提到。
1.再谈移动端适配
1.1百分比解决方案的缺点
在我们的项目中大量的使用百分比来解决页面在宽度上的自适应,其实在高度上并没有很好的自适应。所以在我们的前端页面会出现一些比较奇怪的问题。比如下面这样:
还有一个比较明显的问题就是:在任何机型上我们的按钮的高度是不会变化自适应的,所以小屏手机我们的按钮看起来很臃肿。
1.2一些常用单位的概念解析
在谈我为什么在我们的项目中引入rem
单位的时候,先来详细的了解几个常用的单位概念。
px像素(Pixel)
。相对长度单位。像素px
是相对于显示器屏幕分辨率而言的(也就是说是跟物理设备有关的)。拿高清屏和普通屏来做对比就是普通屏幕的1个像素点就是1个物理像素点,而高清屏的1个像素点是4个物理像素点。em
相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸。em
单位的特点:1. em的值并不是固定的;2. em会继承父级元素的字体大小。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
的时候比较麻烦的就是px
和rem
换算的问题。上面的除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来布局和注意的问题
整体的布局还是使用百分比
使用rem的最佳场景是,遇到例如多列带有图片的列表,常常需要图片固定宽高比例
研究了一些网站,比如淘宝,对字体字体一般情况建议使用px
出现
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
的像素线。且不做特殊处理的话1px
和0.5px
的在Chrome浏览器中显示效果是相同的。也就是说Chrome浏览器不识别0.5px
。
但是相同的代码我在 safari浏览器中的效果却是下面的效果:
下面的效果是我在PS中做了放大后的效果。
所以,我们可以得出一个结论:对于0.5px
不同浏览器对它的识别是存在差异的。
以下这张图是一位网友对一些常用设备是否识别0.5px
做的统计:
其实从视觉的感受上来说0.5px
像素的显示效果确实是比1px
的显示效果要好很多。大多数情况下也更符合设计稿上的1px
线的效果。那么我们有没有办法可以让1px
在不同的浏览器和设备中显示真正的1像素的效果呢?
答案是肯定定的。
2.1传统的实现方法,也就是我们的项目中正在使用的方法:
伪元素 + css3的缩放巧妙地实现;
基本步骤就是:
设置目标元素定位参照
给目标元素添加一个伪元素before或者after,并设置绝对定位
给伪元素添上1px的边框
设置伪元素的宽高为目标元素的2倍
缩小0.5倍(变回目标元素的大小)
使用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 transform
的scale
的定义,理论上边框可以任意细(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使用时,完全可以按照视觉稿上的尺寸来设置。
设计给的稿子双倍的原因是iphone6这种屏幕属于高清屏,也即是设备像素比(device pixel ratio)dpr比较大,所以显示的像素较为清晰。
一般手机的dpr是1,iphone4,iphone5这种高清屏是2,iphone6s plus这种高清屏是3,可以通过js的window.devicePixelRatio获取到当前设备的dpr,所以iphone6给的视觉稿大小是(*2)750×1334了。
拿到了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]到低好在哪里](https://www.zhihu.com/questio...
移动端高清、多屏适配方案
使用Flexible实现手淘H5页面的终端适配
lib-flexible源代码
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。