响应式之像素和viewport
引言
按照pc尺寸做好的网页,在手机端打开,看起来像是pc的缩小版,东西都在只是字太小都看不清了,有什么办法放大呢?
于是去google一下,发现,贴了这么一行代码就轻松解决了:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0"/>
为什么这行代码能让字看起来和电脑差不多呢?为了搞懂这行代码的意思,又去google,但是立马被一大堆的概念给弄糊涂了。px,pt,dpi,ppi,dip,dpr…… (其实不一定都要懂,懂你需要的就不会糊涂。)
如果你不是前端,也许对上面的代码不感兴趣,那么你很可能对下面的几个奇怪的现象感兴趣:
- Q1: 为什么同样大小的字体(比如14px)或者同一个app的icon在不同的移动设备下人眼看起来的大小不一样?
- Q2: 电影是不是分辨率越高,在电脑上看就越清晰?
- Q3:为什么我用手机拍的同样的照片,在retina屏幕下的电脑更清晰?
- Q4: 为什么视觉设计师为移动端设计的稿子是750px大小的?
- Q5: 为什么在macbook pro(retina)里面的photoshop中打开同样尺寸的图片,retina中缩小了几乎一倍?
不要晕,听我慢慢说。
基本概念
照例,先从我自己的角度介绍一些基本概念。
一切都要从像素讲起。“像素”(pixel)只存在于数码设备显示领域,。图片、视屏、html中的dom元素的尺寸、iphone的设备分辨率都是可以用像素来度量的。
来一张官方iphoneSE的显示屏介绍图
像素的种类
像素分为物理像素和设备独立像素:
-
设备像素/物理像素(physical pixel)
和设备独立像素相反,它是“依赖设备的”的像素,表示设备能控制显示的最小单位。
更详细点,如下图所示,一个电子屏是有很多个固定大小的“点”组成的,可以把“点”看成是能发出彩色光的“灯泡”,每个“灯泡”只能发出一种颜色的光。当每个“灯泡”都发白光时,就是一块纯白色的屏幕;同样的,“灯泡”按照一定的规则展示不同的颜色,就可以让电子屏展示一幅图啦。(为了区分,余下的文字中的物理像素都用“灯泡”来表示)
那么人眼能不能看到这些像素点呢?在一些电子设备上,人眼是可以感受到颗粒感的:
但是在像素点小到人眼在正常情况下无法看到的情况下,屏幕是光滑的。retina屏幕就是这样, 图2-iphone6手机配置图说的就是每行有1136个(灯泡)。 -
设备独立像素(DIP - device-independent pixel)
顾名思义,它是独立于设备的用于逻辑上衡量像素的单位。举两个例子:-
css像素
这个前端最熟悉,平常在开发网页时设置字体大小、图片显示大小等等。用来控制元素展示的长宽,根据代码自由定义 -
描述位图尺寸的像素
平时我们在电脑上可以看到的图片信息是这样的:
一张位图图片是由很多个大小相等的纯色的色块构成,在你用“放大镜”放大到够大的时候能很清楚的看得到。在photoshop中创建画布,会首先让你设置画布长多少像素,宽多少像素。这里的像素值等于色块的个数。下图是某张图片在放大后看到的色块。如果一张图片尺寸是2*2,那么它就是由4个色块组成。这个图片像素指的是图片设计尺寸,不一定是真正显示的尺寸,但是显示尺寸等于设计尺寸时图片是是最清晰的。它指导着屏幕里面的“灯泡”应该展示哪种颜色。
-
像素的相关词
-
ppi (Pixels Per Inch)
也叫像素密度,所表示的是每英寸所拥有的像素数量。密度越高,拟真越强。移动设备通常会给出对角线的长度(单位英寸),和长*宽的分辨率,所以根据公式可以算出ppi:
同理,如果只给出了设备长(单位英寸)和长度分辨率(单位像素),也同样可以算出来。
-
dpi (Dots Per Inch)
这是和ppi容易混淆到一起的概念。指打印设备每英寸印刷出来的点有多少个,同一张图片打印出来和在电脑上看到的清晰度就不一定相同。因为不影响我们理解,有一些博客又喜欢混用,作为只跟电子屏打交道的前端开发,认为各大博客上的dpi=ppi就好。 -
dpr (device-pixel-ratio: Number of device pixels per CSS Pixel)
dpr可以用window.devicePixelRatio得到,是设备上物理像素和设备独立像素(device-independent pixels (dips))的比例。
公式表示就是:window.devicePixelRatio = 物理像素 / dips
大家通常指的dpr是个固定值: dpr的值表示在理想布局宽度(ideal viewport)里,使用多少个物理像素来渲染一个设备独立像素。
在ios中,dpr通常是1,2,3;但是在android中,ideal viewport会根据浏览器自己的喜好变化,甚至会是小数。现在andorid注重高分辨率,很多都达到3了。
Viewport
终于讲到上面的meta标签,name是viewport。 那么什么是viewport?
viewport是视口、视窗的意思,Peter-Paul Koch 大神将其分为layout viewport,visual viewport,ideal viewport三种,三种的单位都是相对像素。在这个meta标签中指的是layout viewport。
-
ideal viewport 理想视图
- 它不是一个真实存在的视图,而是开发者期望的视图。在这个视图中,文字和图片不管在何种分辨率下(无论是pc还是移动端)在肉眼看起来都可以保持差不多大小。在响应式设计的年代,确实只要保证这一点就够了。
- 不同设备"理想视图"的宽度有不同的大小,安卓的设备种类比较多,只列出一种参考下。
- 一些设备上,ideal viewport宽度可以用
screen.width
获取;另一些设备上需要screen.width
除以window.devicePixelRatio
。
剩下的两种视图,我看了很多博客都觉得晕晕的。最后发现用<html></html>
来切入很适合用来理解。
在网页设计里,如果用百分比定义块状元素的尺寸,那么元素的尺寸等于其父元素乘以该百分比。一张网页中所有标签的父元素是<html></html>
,那如果html的宽度也用百分比定义,html的父元素又是什么呢?没错,就是“渲染视图”(layout viewport)。
-
visual viewport 视觉视图
- 从好懂的视图讲起,“视觉视图”的宽度就是指你看到的浏览器或者app的webview中可视区域的宽度。他的值很有用,可以用来推算1个css像素在当前页面等价于多少里面。很简单,把页面分成宽度值那么多份就好。
- 举例:比如视觉视图的宽度为6px;那么2px的字就是这么大。
所以说,为了让手机上的字和电脑上大小差不多,我们希望visual viewport的宽度等于ideal viewport。 - visual viewport宽度可以用
window.innerWidth
获取
-
layout viewport 渲染视图
- 是
<html></html>
的父元素,网页内容在其基础上进行渲染,所以叫“渲染视图”。 - 和visual viewport的关系:
- 在pc上,dom树中超出的layout viewport的部分(网页中有元素任性的比渲染视图还宽)不会算入visual viewport。
- 网页缩放倍率为1或者缩小时,layout viewport宽度=visual viewport宽度;。
- 网页放大时,layout viewport宽度>visual viewport宽度(和手机一样,可参考下图)。
- 在移动端比较复杂,不同浏览器/设备有不同的表现。
- 一部分浏览器/设备,dom树中超出的layout viewport的部分不会算入visual viewport。
- 另一部分浏览器/设备,dom树中超出的layout viewport的部分会算入visual viewport,此时visual viewport宽度 > layout viewport(亲测摩托罗拉的chrome浏览器就是这样)。
- 在网页设计中,我们不希望有滚动条,通常会保证没有元素超出
<html></html>
,或者把超出的部分hidden掉,所以暂时可以不用考虑超不超出的问题。
- 综上,为了让在宽度有限的设备上看到layout viewport的所有内容,我们让layout viewport的宽度等于visual viewport,设置页面缩放比例为1即可.
- layout viewport宽度可以用
document.documentElement.clientWidth
获取。
- 在pc上,dom树中超出的layout viewport的部分(网页中有元素任性的比渲染视图还宽)不会算入visual viewport。
- 是
文章开头说的字在手机上变小的情况,是因为手机面世初期,很多网页都是针对pc端设计的.宽度至少在900px以上吧。这样放在手机上因为容器变窄,文字和图片会互相挤压。为了解决这个问题,很多手机浏览器会在默认情况下,设置visual viewport为一个较大的值,比如iphone6和摩托罗拉t40上是980px。这样,pc上的页面基本就可以不变形的展示在手机上,只是字很小。
如今很多网页都是响应式开发,其实已经不需要这个默认值了。
文中开头的代码是常见的移动端开发会加入的代码,但是在pc端是不生效的。最后解释一下meta标签中的每一个属性:
- width: 设置layout viewport的宽度。可设置为为一个正整数(单位px),或字符串"width-device"(等于设备宽度/理想宽度)
- initial-scale: 设置页面的初始缩放值,为一个数字,可以带小数。当initial-scale为1时,不管是否设置width,layout viewport宽度=visual viewport宽度=ideal viewport宽度。iphone6上就是375px。
- minimum-scale: 允许用户的最小缩放值,为一个数字,可以带小数
- maximum-scale: 允许用户的最大缩放值,为一个数字,可以带小数
- user-scalable: 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许
解答问题
-
Q1: 为什么同样大小的字体(比如14px)或者同一个app的icon在不同的移动设备下人眼看起来的大小不一样?
答:其实这个问题等价于一个css像素在两个设备中分别等价于多少英寸。如果网页是在ideal viewport下,可以推理出一个公式:每个物理像素等于1/PPI()英寸),在ideal viewport下,DPR表示1个css像素由几个物理像素组成。因此,得到公式:
(1/PPI)* DPR = DPR/PPI
举个例子:iphone6-PPI为326,DPR为2,DPR/PPI约等于0.0061;iphone6S-PPI为401,DPR为3,DPR/PPI约等于0.0075;所以iphone6s上的app和字比iphone6大哦。
通常,设备显示屏越大,一个css像素代表的物理尺寸需要大一点。因为人眼会离大屏幕的比较远,离小屏幕的比较近。虽然物理尺寸不一样,但是人眼感觉起来是差不多大的。 -
Q2: 电影是不是分辨率越高,在电脑上看就越清晰?
答:不是。每个设备拥有的“灯泡”(设备像素)是固定的。如果电影的分辨率大于设备拥有的“灯泡”,就代表着要用1个“灯泡”来显示多个图像像素。但是一个“灯泡”只能发出一种光,最好是让一个“灯泡”显示一个图像像素。所以以后下电影下适合的分辨率就好啦,否则也是浪费掉了。 -
Q3:为什么我用手机拍的同样的照片,在retina屏幕下的电脑更清晰?
答:PPI越高,画面越精细。当图片的显示大小小于它实际的分辨率(数码设备发展很快,随随便便一张图就可以很大的分辨率,所以一般都是小于的),4 * 4的照片一般设备只能用2 * 2的“灯泡”来渲染,而retina的设备可以用4*4的来渲染,所以就更清晰啦。同理也可以应用与上面电影的问题,retina可以播放更大分辨率的电影。 -
Q4: 为什么视觉设计师为移动端设计的稿子是750px大小的?
答:现在移动端的页面会基于宽度为750px的画布进行设计,是根据iphone6的设备分辨率来设计的。虽然iphone6上网页设计出来只有375px大小,但是为了充分利用每一个“灯泡”,画面更精细,需要用750px的画布来设计。 -
Q5: 为什么在macbook pro(retina)里面的photoshop中打开同样尺寸的图片,retina中缩小了几乎一倍?
答:补充一下,这里的场景是图片都不放大的情况下。
在不放大的情况下,默认photoshop中一个“灯泡”渲染一个图片像素。因为retina屏幕的像素密度更大,在dpr为2的retina屏幕下面,图片就缩小了一倍。看到论坛上的一些解决方案是放大一倍来设计。