我在开发前端的时候曾经会有几个疑惑:css
1)拿到的设计搞的宽度是640px或750px的,在页面不一样尺寸屏幕布局的时候怎么换算。html
2)移动端布局是用%、px、rem、伸缩盒 Flexible Box Layout仍是多列Multi-column。前端
3)用px设置了字体大小,可是在ipad上面显示的却很小,字体大小是用rem比较好仍是px+媒体查询比较好。html5
4)媒体查询@media分几个尺寸的范围能覆盖比较多的尺寸状况。git
在分析适配问题以前,要先了解一些基础概念,Retina与HI-DPI、DPI与PPI、DP与PT、viewport等。github
Retina Display是苹果注册的命名方式,其余厂商只能使用HI-DPI或者其余的命名方式,不过意思都是同样的,就是屏幕的PPI很是高。web
![]() |
![]() |
iPhone 3GS(非Retina屏幕) | iPhone 4(Retina屏幕) |
右边的图片明显要比左边的清晰,这是由于PPI要高,何为PPI。编程
1)PPI与DPIsegmentfault
PPI和DPI这两个是密度单位,不是度量单位。api
1. PPI(pixels per inch):图像分辨率 (在图像中,每英寸所包含的像素数目)【1英寸(in)=2.54厘米(cm)】
2. DPI(dots per inch): 打印分辨率 (每英寸所能打印的点数,即打印精度)
PPI的计算方式以下,其中长度像素数和宽度像素数取的是物理像素(Physical Pixel)即设备像素。
PPI = √(长度像素数² + 宽度像素数²) / 屏幕对角线英寸数
这个物理像素不是说屏幕的大小,例如iPhone4的屏幕是320*480(这是DP/PT设备独立像素),但物理像素是640*960,屏幕是3.5寸。本身按下计算机算出来≈326。
在知乎上看到,还有一种比较复杂的PPI计算方法,考虑了观察者的距离,下图是一个极简版本:
上面又出现了几个概念,物理像素、DP、PT又是什么鬼?
2)物理像素(Physical Pixel)
物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。下图摘自《The Ultimate Guide To iPhone Resolutions》,原文是洋文写的,对应的中文能够看这里。
上图中iPhone 3GS的物理元素就是320*480,而4/4S是640*960。其中4/4S的320*480其实就是PT,而render中的2x就是dpr(设备像素比)。
3)DP、PT与SP
DP或PT是测量单位,DP(Density-independent pixels,也会叫DiP)表示独立于设备的像素点,PT(Point)表示点。
DP用在Android上,PT用在Apple上,可是他们本质上是相同的。
SP(Scale-independent pixel)和DP、PT从用途上来说是不一样的,可是工做方式相同。SP表示与比例无关的像素,一般用来定义字体大小。
这三个都是抽象像素,不会由于PPI的变化而变化,在相同物理尺寸和不一样PPI下,他们呈现的高度大小是相同。也就是说更接近你手机看上去的大小,而px则不行。
上图摘自google的《Device metrics》,要浏览google的页面你懂得。表格中Density(密度)的基础单位,在Android上是mdpi,而在IOS中是1x。
4)CSS像素
CSS像素是Web编程的概念,指的是CSS样式代码中使用的逻辑像素。例如width:300px跟font-size:14px,这类css语句中的px。
CSS像素和物理像素(Physical Pixel)是容纳的关系。
例如iPhone4/4S中,1个CSS像素=4个物理像素(也叫设备像素):
作个简单的例子,经过改变viewport,来手动放大屏幕。简单设置一个ul的CSS。
ul{ padding-left:20px; font-size: .5rem; }
1. 页面宽度等于屏幕DP或PT值
2. 页面宽度等于物理像素(Physical Pixel)
从两张图片中能够看到,一样是20px,看起来却有大小差异,上图比下图宽了一倍,与第一张图正好对应起来。
5)设备像素比(device pixel ratio)
设备像素比简称为dpr,其定义了物理像素(Physical Pixel)和DP/DiP(设备独立像素)【IOS中是PT(点)】的对应关系。
设备像素比 = 物理像素(Physical Pixel) / 设备独立像素(DP/DiP或PT)
DP/DiP或PT与屏幕密度有关(屏幕密度需经过PPI计算出来)。
在JavaScript中,能够经过 window.devicePixelRatio 获取到当前设备的dpr。
而在CSS中,能够经过 -webkit-device-pixel-ratio , -webkit-min-device-pixel-ratio 和 -webkit-max-device-pixel-ratio 进行媒体查询。
在桌面浏览器中,viewport就是浏览器窗口的宽度高度。
但移动设备的屏幕比桌面屏幕要小得多,为了要让网页在小尺寸的屏幕上显示正确,就须要对viewport作些处理。
须要把viewport分红两部分:visual viewport和layout viewport。
George Cummins在Stack Overflow上对这两个概念作了分析。大体意思以下:
把layout viewport想像成为一张大图。如今用一个比较小的框,经过它来看这张大图。在框内看到的部分就是visual viewport。框中的度量单位是CSS像素。
能够把这个框靠近一些(放大看局部)或靠远一些(缩小看总体)。也能够改变框的方向,可是大图layout viewport的大小和形状永远不会变。
1)visual viewport
visual viewport是页面当前显示在屏幕上的部分。用户能够经过滚动来改变他所看到的页面的部分,或者经过缩放来改变visual viewport的大小。
结合上面的比喻,再想象一下大图和框的操做。
图片来自于文章《A tale of two viewports》,是用洋文写的,对应的中文能够看这里。
2)layout viewport
layout viewport就是页面原来的大小。
可是咱们用在手机用浏览器打开PC的网页的时候,会看到网页被浏览器自动缩小了,变的过小会致使没法浏览内容。
3)viewport meta标签
为了避免让浏览器自动缩小,引入了viewport元标签。经过这个元标签控制layout viewport的宽度。
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
上面这段HTML常常会在移动端页面看到。
1. width:设置 layout viewport 的宽。
2. initial-scale:初始缩放比例,也便是当页面第一次 load 的时候缩放比例,上面是变成设备的宽度,也就是layout viewport= DP或PT。
3. maximum-scale:容许用户缩放到的最大比例。
4. minimum-scale:容许用户缩放到的最小比例。
5. user-scalable:用户是否能够手动缩放。
![]() |
![]() |
浏览器自动缩放 | 添加了viewport |
注意下图片中的红色框,第二张图片内容超了出来,应该是给文字设置了宽度,而且这个宽度大于layout viewport致使了出来。但其实在Android和IOS中会有不同的表现。
在文章《A tale of two viewports》中指出:
1. 经过 document.documentElement.clientWidth 获取 layout viewport 的宽度
2. 经过 window.innerWidth 获取 visual viewport 的宽度
我用iPhone6和红米 note在本身本地作了个获取这两个值的测试,设置了个宽度,撑开页面。
<div style="width: 700px"> //文字内容 </div>
1. 红米中的UC、微信浏览器、自带的浏览器还有移动端的Chrome,显示两个值都同样,是360px,只是UC和微信浏览器不会出现左右滚动条。
2. iPhone6中的UC浏览器和Safari显示两个值不同,layout viewport 的宽度是375px,而visual viewport 的宽度是708px,不会出现滚动条。
4)ideal viewport
这个viewport的尺寸,我理解至关于DP/DiP(设备独立像素)或PT的尺寸。
在文章《Meta viewport》有详细的说明,若是洋文看起来吃力,能够选择浏览中文版的。
visual viewport宽度 = ideal viewport宽度/ zoom factor
zoom factor其实就是meta中的“initial-scale”。
设置initial-scale后,就开始相对于ideal viewport进行计算产生visual viewport,用visual viewport的宽度值去设置layout viewport的宽度值。
注意,刚刚用iPhone6测试获取到layout viewport与visual viewport不一样,一开始应该是相同的,只是后面渲染的时候style="width: 700px"把visual viewport给撑大了。
若是你拿到的设计稿是640px宽度的,在你用iPhone5布局的时候,其实你能够initial-scale=0.5,将layout viewport放大到640px,接下布局能够经过rem来设置。
可是手机屏幕的尺寸各不相同,那么initial-scale的值也应该是不一样的,这个值是能够经过JavaScript计算出来的,能够参考flexible.js(移动自适应方案)中的代码。
打开手机淘宝页面,能够看到里面内联了这个库的代码,这个flexible.js库就是用来解决H5页面终端适配的,具体的使用方法能够参考这里。
1)rem
rem 就是相对于根元素 <html> 的 font-size 来作计算。flexible.js会给html赋font-size的值。下面的代码是在iPhone5中模拟。
使用方法中并不推荐字体用rem单位,但我以为能够因地制宜,具体状况具体分析,好比要在iPad中适配,这个时候用rem体验会比较好。
2)计算initial-scale
注意下面的Android设备一概dpr=1,我后面作了个简单的测试。
1 var devicePixelRatio = win.devicePixelRatio; 2 if (isIPhone) { 3 // iOS下,对于2和3的屏,用2倍的方案,其他的用1倍方案 4 if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { 5 dpr = 3; 6 } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) { 7 dpr = 2; 8 } else { 9 dpr = 1; 10 } 11 } else { 12 // 其余设备下,仍旧使用1倍的方案 13 dpr = 1; 14 } 15 scale = 1 / dpr;
将第2行修改下代码,经过正则匹配来判断dpr的值。
var isRegularDpr = devicePixelRatio.toString().match(/^[1-9]\d*$/g); if (isRegularDpr) { //...逻辑与上面相同 }
在IOS中表现正常,但是在Android中的UC、Chrome浏览器中会放大,微信浏览器显示正常。顺便说下,个人手机是红米note,物理像素是720px,DP(设备独立像素)是360px。
![]() |
![]() |
![]() |
修改前 | 修改后 | 添加target-densitydpi=device-dpi |
CSS像素是360px | CSS像素是720px | CSS像素是720px |
第二和第三都是720px,宽度都变大了,只是一个是经过“initial-scale=0.5”,另外一个是经过“target-densitydpi=device-dpi”。但字体一张变大一张变小了。
形成两种展示不同的缘由,个人理解是,第一种1个CSS像素=4个物理像素,而第二种是1个CSS像素=1个物理像素。
Android的viewport中属性“target-densitydpi”,这个“DPI”就至关于“PPI”,个人红米note的“PPI”是267。
能够的取值以下:
device-dpi: 使用设备本来的 dpi 做为目标 dp。 不会发生默认缩放。 high-dpi: 使用hdpi 做为目标 dpi。 中等像素密度和低像素密度设备相应缩小。 medium-dpi: 使用mdpi做为目标 dpi。 高像素密度设备相应放大, 像素密度设备相应缩小。 这是默认的target density. low-dpi: 使用mdpi做为目标 dpi。中等像素密度和高像素密度设备相应放大。 <value>: 指定一个具体的dpi 值做为target dpi. 这个值的范围必须在70–400之间。
有一篇文章《Viewport target-densitydpi support》曾经讲到,这个属性将被废弃。
3)viewport元标签
若是在页面中已经设置了元标签,那么就读取元标签中的内容,若是没有设置,就动态添加meta标签到head中。
4)refreshRem函数
此函数就是在设置html中font-size,以及flexible.rem与win.rem的值,flexible.rem会在后面的两个辅助方法refreshRem和px2rem内用到。
docEl.getBoundingClientRect().width是为了得到 visual viewport 的宽度。
这个540实际上是个经验值,参考Issues#12,目前主流手机最大的css像素尺寸,是540(好比devicePixelRatio为2,分辨率是1080x1920的手机)。
width / dpr 是由于前面添加了viewport后,width会根据dpr(设备像素比)改变,而540定义的是一个DP/DiP(设备独立像素)或PT值。
width / 10 主要为了之后能更好的兼容 vh 和 vw。
function refreshRem() { var width = docEl.getBoundingClientRect().width; if (width / dpr > 540) { width = 540 * dpr; } var rem = width / 10; docEl.style.fontSize = rem + 'px'; flexible.rem = win.rem = rem; }
5)pageshow与DOMContentLoaded
pageshow事件相似于 onload 事件,onload 事件在页面第一次加载时触发, onpageshow 事件在每次加载页面时触发,即 onload 事件在页面从浏览器缓存中读取时不触发。
win.addEventListener('pageshow', function(e) { if (e.persisted) { clearTimeout(tid); tid = setTimeout(refreshRem, 300); } }, false);
页面文档彻底加载并解析完毕以后,会触发DOMContentLoaded事件,HTML文档不会等待样式文件,图片文件,子框架页面的加载。
jQuery中的ready方法就是基于这个事件编写的,查看ready源码。
如今我本身来解答一下本身的疑惑:
1)经过修改initial-scale的值,而后配合rem来作换算,能够参考flexible.js中计算方式。
2)这几种布局应该是配合使用,因地制宜,我前面的一篇文章中就记录使用了多列与伸缩盒的使用场景。
3)仍是同样,要结合媒体查询与rem或px,因地制宜。
4)这个范围,目前我还不能肯定,尚未作过详细的实验。
参考资料:
移动端前端开发中须要知道的一些屏幕知识
设计师DPI指南(翻译)
理解flexible.js所需的viewport知识
使用Flexible实现手淘H5页面的终端适配
dp、sp、px傻傻分不清楚
移动端H5页面高清多屏适配方案
A tale of two viewports
关于移动端 rem 布局的一些总结
移动端页面布局及字体大小该如何设置
web app变革之rem
苹果的 Retina 屏幕必定须要突破 326 PPI 吗?仍是跟距离相关?
Retina显示屏
(翻译)第三种viewport-ideal viewport