移动端高清屏适配方案
移动设备各种规格高清屏的出现,使得PC端常规的px适配方案变得捉襟见肘,在这种情况之下,有针对移动端独立的一套适配方案就显得非常地重要。
第一印象
重度依赖meta标签的viewport
和html标签的font-size
,配合使用dpr\rem\px
。
一些概念
1、Retina
“Retina”是“视网膜”的意思,指将更多的像素点缩至同样大小的屏幕里,显示屏的分辨率极高,超越了肉眼分辨单点像素的极限。 Retina技术一开始由苹果公司应用在iPhone4上,由于该技术带来的细腻屏幕显示体验,其它智能手机厂商纷纷仿效,由此引发了一股从普通屏幕到高清屏幕升级的潮流。如今市面上大部分智能手机都搭载了高清显示屏。
2、物理像素(physical pixel)
物理像素也叫设备像素(device pixel),指显示屏中最小的一个物理部件,是屏幕的实际像素。
3、逻辑像素(logical pixel)
逻辑像素是一个抽象的单位,是计算机显示系统的一个虚拟像素,可以认为是应用程序所使用的计算机坐标系统上的一个点,对应于网页开发中的CSS像素,下图描述了CSS像素(逻辑像素)和设备像素(物理像素)的关系: 所以说,css中的1px并不一定等于设备的1px。
4、设备像素比(device pixel ratio)
设备像素比dpr是描述物理像素和设备独立像素关系的一个比值,计算公式为:
设备像素比 = 物理像素 / 逻辑像素
拿iPhone7为例,三者有如下关系:
也就是说一般情况下,浏览器会以375×667的分辨率来把页面渲染显示在实际上有750×1334分辨率的屏幕上.
5、屏幕密度
屏幕密度是指一个设备表面上存在的像素数量,它通常用PPI(pixels per inch)来表示,即每英寸包含的像素数量,计算公式为:
例如iPhone7的4.7英寸,像素分辨率为1334*750的Retina HD显示屏,计算得出其ppi值为326.
6、viewport
弄明白上面几个概念后,viewport也就不难理解了. viewport字面意思是’视图窗口’,同样是为了解决移动设备高清屏浏览问题而出现的新概念.通过类似下面的基础写法,它允许我们给移动浏览器设定一个虚拟的宽度,从而实现更友好的页面浏览体验:
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
viewport是个很大的课题,这里大概了解即可,ppk大师写了很多相关文章,补充作为深入研究.
7、rem——移动端适配利器
先看看W3C给rem的定义:
‘rem’ — font size of the root element.
也就是说,rem是根据根元素的font-size来设置大小基准,一般的浏览器默认根元素的font-size是16px即 html { font-size:16px; //1rem=16px }
在这基础上,如果我们想给一个P标签设置12px的字体大小那么用rem来写就是
p {
font-size: 0.75rem; //12÷16=0.75(rem)
}
rem的这个特性给我们提供了一个适配的思路,即我们可以人为动态地设置html的font-size的值并以此为参照,让rem和px的转换比例适应不同dpr的屏幕,从而能在css大小布局中得以重用.事实上,手淘和网易正是基于该特性去实现移动web的适配.
iPhone手机和安卓手机
iPhone的dpr有三个,分别是1,2,3.下面这张图来自paintcodeapp,它较为全面地描述了历代iPhone设备上各种参数的关系: 从上图我们可以看出,无论是iPhone3g的320物理宽度还是iPhone4的640物理宽度,css中的320就代表着屏幕的宽度,其它以此类推.
而安卓的dpr值则较为复杂,有2.6的Google Pixel,也有4.0的Samsung Galaxy S7: 非常的多,五花八门,于是乎手淘的flexible干脆不考虑安卓,全部dpr设为1.
更多移动设备的屏幕参数,可以到mydevice上查阅.
设计师与前端
目前设计师通常选择iPhone6作为设计基准,交付给前端的视觉稿是750pxx1334px的尺寸,然后前端开发人员再通过一套适配规则自动适配到其他的尺寸,下面一张图是手淘的协作模式:
关于图片
各种手机不同dpr的现状给开发带来了一定的障碍,比如说我们要设置一张 CSS分辨率 为30×50的图片,对于一个 dpr=2 的手机,为了不让图片看起来模糊,我们需要使用一张 实际分辨率 为60×100的图片;但是对于一个 dpr=1 的手机,我们又没有必要使用一张 实际分辨率 为60×100的图片,因为这样会造成带宽浪费.
所以,为了有效节省带宽,同时保持图片的浏览体验,可以给不同dpr的设备准备不同尺寸的图片,例如:
/1/a.jpg
/1.5/a.jpg
/2/a.jpg
至于如何自动加载哪个版本的图片,Reda Lemeden在Towards A Retina Web一文中提到了一些方案,下面这两种做法值得一提:
1、使用CSS媒体查询
通过css的device-pixel-ratio
属性,我们可以对不同dpr进行media query,从而使高清屏加载高清图片:
.icon {
background-image: url(example.png);
background-size: 200px 300px;
height: 300px;
width: 200px;
}
//如果是dpr大于1.5的情况
@media only screen and (-Webkit-min-device-pixel-ratio: 1.5),
only screen and (-moz-min-device-pixel-ratio: 1.5),
only screen and (-o-min-device-pixel-ratio: 3/2),
only screen and (min-device-pixel-ratio: 1.5) {
.icon {
//启用两倍像素尺寸图片
background-image: url(example@2x.png);
}
}
这种方法的好处在于只需要下载匹配到的图片资源,但是使用场景有很大的局限性——只能用在css控制的背景图片background-image
属性,无法用在img
标签或其它场景,同时不利于大规模项目的开发管理。
2、使用JavaScript
通过js的window.devicePixelRatio
属性同样可以做到设备像素比dpr的检测,目前各个浏览器厂商对其的支持已经,非常好可以放心地使用:
用法:
$(document).ready(function(){
if (window.devicePixelRatio > 1) {
var lowresImages = $('img');
images.each(function(i) {
var lowres = $(this).attr('src');
var highres = lowres.replace(".", "@2x.");
$(this).attr('src', highres);
});
}
});
js的做法适用范围比css做法广,可以用在页面上的所有图片,虽然在非Retina屏幕下不用加载高清图片,但是在Retina屏幕下却需要加载标清和高清两种图片,而且会引起浏览器进行页面重绘(repaints)操作,所以在实际应用场景中还是得权衡好利弊。
手淘的解决方案–flexible.js
谈到移动Web开发,不得不提手淘的flexible,毕竟它经历了时间和实战场合的多重考验,我们现在看到的手淘页面也还在使用着这个方案。 以目前较为流行的750px宽度设计稿为例,手淘的做法就是将750的宽度分为10份,每份是75,整个宽度就是10rem
,并将<html>
的font-size
设置为75px
,这样我们就得到了1rem=75px
的换算关系。
下面是flexible.js中对rem的处理部分:
function refreshRem(){
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10; //分为十份
docEl.style.fontSize = rem + 'px'; //设置font-size
flexible.rem = win.rem = rem;
}
如此一来,页面的元素我们就可以用rem来进行愉快的布局了。
但是这样还没完,flexible.js的源码中还有这样一段非常重要:
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;
}
这段代码对ios设备做了特殊处理,也就是检测到有搭载Retina屏幕的iPhone,会设置不同的dpr,然后对于安卓设备,一律设置dpr=1
,至于为什么要这样做,我有两种猜测:
- 手淘的工程师是骨灰果粉,唯ios马首是瞻:)
- 偷懒,面对类型繁杂的安卓设备,选择放弃治疗:)
这样的做法对安卓高清屏有什么影响? 答:无法通过dpr去进行media query,从而实现字体的适配(具体原因见下面)
为什么布局用的是rem,而字体用的却还是px?
布局对rem转换成px的值的格式要求不高,比如允许图片有.33rem=24.75px的宽度. 但是字体对px格式有要求,比如不希望出现字体有24.75px这样的大小. 现在主流的字体大小都是类似12px、16px等,如果我们用rem做为字体单位,那么转成px的时候,势必会出现奇数或者小数的情况. 因此,为了避免这种情况,我们还是要用px做为字体的单位.但是又该怎么做呢?
手淘的解决方案就是默认写个dpr为1时的字体大小,然后使用css的选择器,根据dpr的不同来设置字体大小:
div {
width: 1rem;
height: 0.4rem;
font-size: 12px; // 默认写上dpr为1的fontSize
}
[data-dpr="2"] div { //dpr为2时
font-size: 24px;
}
[data-dpr="3"] div { //dpr为3时
font-size: 36px;
}
1px像素问题
假设这样一个需求:你需要在任何屏幕上这条边框都是1物理像素. 我们当然不能直接一个border-width:1px
,因为在retina屏下css的1px映射到物理像素就可能会有2px或者3px.解决方案有多种,这里以手淘的flexible为例: * 在dpr = 1
时, 输出是1:1的还原,因此不必顾虑; * 在dpr = 2
时,设置initial-scale=0.5
; * 在dpr = 3
时,设置initial-scale=0.3333333333333333
. 这时我们方可直接的写1px,然后交由浏览器去进行缩放.至此,经典的1px问题得以解决.
总结
最近项目里做一个手机H5,赶紧趁机会恶补了移动Web开发的知识,花了几天总算渐渐理清知识点。移动Web开发是一个很大的课题,本文主要总结了高清屏的适配方案,还有例如meta、字体、事件等方面还需深入了解,一边学习一边做下笔记,好的笔记会继续转成文章发布出来。 最后盗用@南宮瑞揚的一张图作为收尾:
扩展阅读
本文标题:移动端高清屏适配方案
转载请注明出处,欢迎分享