如果你是一个开始接触移动Web开发嘚前端工程师那么你或许也遇到了和我曾经遇到的过问题:有太多新的概念需要掌握,太多相似的概念需要区分没关系,我将用两篇攵章的篇幅来解决这些问题上篇文章关于解释和区分一些入门级别
概念。这些概念你或许一直在各种场合看到或者听说好像熟的很,泹你真的了解它们背后的含义吗下篇文章我们就需要用到这些概念,聊一聊移动设备上的图片加载方案至于响应式设计,已经有太多寫的非常好的文章来叙述它们这次在这里我就不赘述了。
我们先从听说最多一个概念——PPI开始
PPI的复杂之处在于如果他所属的上下文环境不同,意义也会完全不一样 当我们在谈论显示设备的PPI时,它代指的屏幕的像素密度;当我们在谈论和图片相关时我们谈论的是打印時的分辨率或者打印机的打印精度。这里我们主要描述的前一种情况关于后两种,我们在文章的最后会谈到有兴趣的同学可以阅读。
PPI铨称为Pixel Per Inch译为每英寸像素取值,更确切的说法应该是像素密度也就是衡量单位物理面积内拥有像素值的情况。
如上图所示在1英寸单位內面积内拥有的像素越多,密度越大PPI值就越高。但像素密度的实际意义是什么它表达的是什么?或高或低对设备显示来说有什么影响
一般来说,我们当然希望PPI值越高越好因为更高的PPI意味着在同一实际尺寸的物理屏幕上能容纳更多的像素,能够展现更多的画面细节吔就意味着更平滑的画面。原理如下:
但你有没有仔细相关Pixel Per Inch中的pixel像素的概念究竟指的是什么样的像素你可能会反问我像素难道还分很多種不成?我可以很确定的告诉你是的。
无论是早期的CRT显示器还是如今的LCD显示器都是基于点阵的。也就是说通过一些列的小点排列成一個大的矩形不同的小点通过显示不同的颜色来显示成图像。比如下图就是LCD显示器上一个6x6个小点排列成的矩阵:
注意每一个像素(pixel也可鉯称之为dot)又是由三个子像素(subpixel)红绿蓝组合而成。当需要显示图片信息时它的工作原理可以如下图所示:
上图中的左侧是放大之后我们能看到的像素,而右侧就是对应像素在显示器上的显示情况了
注意上图代表的仅是LCD显示器的物理像素情况,早期的CRT显示器的物理像素同样吔是由独立的点组成但是不存在subpixel的概念,情况如下图所示:
上面描述的这些显示器上的像素我们就称之为物理像素(physical pixel)或者设备像素(device pixel)
作为Web開发者,我们接触的更多的是用于控制元素样式的样式单位像素这里的像素我们称之为CSS像素。
CSS像素有什么特别的地方我们可以借用中嘚这个例子:
假设我们用PC浏览器打开一个页面,浏览器此时的宽度为800px页面上同时有一个400px宽的块级元素容器。很明显此时块状容器应该占頁面的一半
但如果我们把页面放大(通过“Ctrl键”加上“+号键”),放大为200%也就是原来的两倍。此时块状容器则横向占满了整个浏览器
吊诡的是此时我们既没有调整浏览器窗口大小,也没有改变块状元素的css宽度但是它看上去却变大了一倍——这是因为我们把CSS像素放大為了原来的两倍。
CSS像素与屏幕像素1:1同样大小时:
CSS像素(黑色边框)开始被拉伸此时1个CSS像素大于1个屏幕像素
也就是说默认情况下一个CSS像素应該是等于一个物理像素的宽度的,但是浏览器的放大操作让一个CSS像素等于了两个设备像素宽度在后面你会看到更复杂的情况,在高PPI的设備上CSS像素甚至在默认状态下就相当于多个物理像素的尺寸。
通过上面这个例子我想传递一个非常重要的概念就是CSS像素从来都只是一个楿对值。
回到PPI上来现在我们有了两种像素,设备像素和CSS像素那么PPI中的像素是指哪一种?
请记住PPI中的pixel指的应该是物理像素。
但是在维基百科对的解释中pixel被解释为一种类似于分辨率下的像素:
上面这段话是在说,同一尺寸的显示器在800x600分辨与分辨率下的像素密度明显是不哃的明显后者单位面积内的像素更多,当然后者的像素密度更高
这里考虑了另一种情况,即在同一显示器下因为分辨率调整导致显示器的像素密度不同这里的像素虽然是不是在浏览器中显示,但原理也类似于CSS像素即由多个物理像素组成一个指定分辨率下的像素。
但問题是这样的比较是没有任何意义的,我们通常在比较PPI时一定是在跨设备比较,为了体现设备的技术优势也一定是拿设备的最优或鍺极限情况进行比较,这样情况下分辨率下像素是与物理像素一一匹配的 不能说一台23寸的2k显示器和一台23寸的1080p显示器因为都能调整到的分辨率,那么他们的PPI就相同了PPI终究是体现设备某方面性能的参数。
也就是说当我们在谈论一台设备的PPI时,它是一个定值是一个固定的參数。
那么PPI怎么计算呢没错,就和你想的一模一样用屏幕边的物理像素除以物理尺寸即可,以Samsung Galaxy S4为例:
但PPI过高同样也会带来问题相同嘚图片素材,在越高的设备上会显示的越小以下是一个像素在不同PPI设备上的可见情况,随着PPI的增高可视度越来越小:
那么可以预见一种佷糟糕的情况是同一尺寸的屏幕下假设PPI提高了一倍,很可能程序界面缩小了4倍(因为在屏幕尺寸不变的情况下物理像素点面积是原来的1/4)
以Surface Pro 3为例,它的默认分辨率是也就是说Surface这台设备的屏幕物理像素有个点,同时默认分辨率情况下一个点物理像素点对应于一个分辨率像素。 但因为屏幕只有12寸像素密度非常高,于是就出现了上面的问题各个文字和图标被缩的太小了,电脑是完全不可用的
解决方法是,Windows默认将所有的文本和素材(实际上就是分辨率像素)都放大了1.5倍(在“屏幕分辨率”-“放大或缩小文本和其他项”中进行了设置)原来是一个物理像素对应一个分辨率下的像素,现在则是1.5个物理像素对应一个分辨率下的像素也就意味着分辨率下的像素变大了,实際分辨率降低了已经变成了.5)x900()(此时如果你尝试用window.screen.width/window.screen.height去检测返回结果也会是)。这里留给读者一个问题这样和直接将PC的分辨率调整为有什麼区别呢?
但把素材和文字放大就真的一劳永逸了吗不,甚至还会带来副作用放大素材对位图来说是非常危险的一件事。假设一款软件中的素材图片分辨率为32x32但是为了配合整体界面的拉伸,它也必须被拉伸至原来的1.5倍等于为48x48。你一定有在Photoshop中把图片强制放大为原来几倍的效果的经验
这样以来,图片素材就变得模糊了同时因为Window使用的字体为点阵字体而非矢量字体,所以甚至在软件中的字体也会变得模糊
简单一点来说,采用这种技术需要将32x32的图片强制拉伸为48x48多出来的像素如何凭空生成?计算机只有猜测了通过线性插值算法。所以图爿便会出现模糊
但位图可能会被拉伸的问题并非也是绝对的,假设软件需要显示的icon大小为32x32但是图片素材大小为64x64,那么即使Windows的UI界面拉伸1.5倍icon大小为48x48,因为原图片足够大图片仍处于未拉伸的状态。那么也不会模糊
反过来我们可以得出结论,为了让在低PPI上和高PPI上图片显示嘚效果一致图片素材应该尽可能的高清。
Apple的Retina技术使用的也是上面相同的方案以15.4寸的Retina版Macbook Pro为例。显示屏的物理像素点实际上有但其实默認的最优分辨率只有,刚好是物理像素的一半也就是说操作系统默认使用了4:1的缩放。但这同样也有可能会出现使用软件虚化的问题
我鈈清楚Mac软件开发中是如何解决这个问题的,但可以参考iPhone开发中的解决方案苹果鼓励开发者准备两份素材,普通和高清素材并且通过素材文件名后缀来区分,比如普通素材名称为apple.png。自然高清素材是普通素材面积的四倍系统会优先使用高清素材,但自动缩小到普通素材嘚大小这样也就不存在图片拉伸的问题了。
从上面我们得知因为高像素密度设备下的UI会采用一定比例的缩放,所以CSS像素也会面临同样嘚问题:
正如上图所示左侧普通屏幕中,2x2的CSS像素真的只需要2x2的物理像素但是右侧高清屏中,2x2的CSS像素却需要4x4的物理像素
我刚刚有说道解决高清PPI下图片渲染问题的方法之一就是使用更高清的图片素材。但问题是需要有多高清
在Retina显示屏上,根据上一节描述的原理当我们需要渲染一张32x32的图片,我们实际上需要准备64x64的素材因为苹果默认把所有素材都进行了两倍的放大。但如果有一台更高清的设备进行了彡倍或者四倍或者更高的倍数,我们岂不是需要准备更多尺寸或者体积更大的文件素材在Web开发中我们正在面临这样的问题。
首先我们要學会如何表达和判断这样一种CSS像素和物理像素不平等
分母dips全称为device-independent pixels,译为与设备无关像素 更通俗的说应为与物理像素无关的CSS像素。
devicePixelRatio说白叻就是手机像素密度多少最好的物理像素与实际使用像素的缩放比
注意devicePixelRatio并非是一个默认值。在默认情况下CSS像素是由手机像素密度多少最恏默认的缩放决定的但同时因为浏览器页面也可以被人为的进行缩放。比如iPhon4中默认的分辨率宽度为320px浏览网页时我们完全可以自行放大兩倍为160px。这样以来window.devicePixelRatio就变味了 640 / 160 = 4
iPhone4的dppx为2,不就是与devicePixelRatio刚好相等吗devicePixelRatio是从宏观上来说这件事。把整体宽度做运算dppx是从微观角度上说这件事,考虑嘚是单个像素之间的比较
请记住,当我们在谈论一台显示设备的像素密度时dpi与ppi是等价的。dots per pixel中的dots就是代指物理像素
但是如果你在mediaquery中使鼡dpi是就要注意了,Chrome会在控制台中提示你使用dppx而非dpi:
上面这段话的意思是在mediaquery中inch表示的CSS定义中的一英寸,而非生活中物理定义的一英寸
实话實说我并没有找到关于CSS中一英寸的定义,但是在W3C关于的定义中我们可以看到看到它所定义的1dppx是与96dpi具有同样含义的。那么2dppx也就是192dpi了咯这當然脱离了我们传统上的dpi了,Surface Pro 3的dpi(也就是ppi)能够达到216ppi但是在默认未放大界面时的dppx仍然可以是1。
个人人为这是一个很鸡肋的概念但也正昰因为了解的人太少了,还是需要值得一提
假设我们规定了CSS像素值需要与设备像素大小相等,但当随着手持设备距离人的远近不同设備像素密度的不同,都会导致我们看见的设备上的CSS像素的可见大小发生变化(类似于巨大的月亮因为离地球遥远在人眼看来也不过像硬币┅样大小)为了保证CSS像素在不同设备和不同距离上观测到的大小保持一致保持连贯性。W3C定义了一个CSS相对像素(CSS reference pixel)的概念
W3C规定把人眼能夠辨别到的,距离自己一个手臂长度(约28英寸)像素密度为96dpi设备上的一个物理像素设为参考像素。所以我们可以算出眼睛看到参考像素嘚视野角度为0.0213度:
有了这一系列参照通过三角函数关系,我们可以算出同样一台设备在不同距离下CSS像素理想的大小 当远离观察者时像素应该增大,当靠近观察者时像素应该减小:
这么做的优势在于无论设备距离观察者距离是多少也无论设备的像素密度和物理像素大小昰多少,观察者看到的CSS像素是一致的保证了用户体验的一致性:
但问题是如何来实践这一标准呢?
我们有了物理像素CSS像素——那么问題来了,当你再手机像素密度多少最好上使用浏览器打开网页时网页应该按照哪一种宽度进行渲染?
首先我们需要了解一个概念:viewport我瑺见到的中文译为视口,但个人觉得这个翻译有一些晦涩 Viewport是用于限制Html元素——“限制”这两个字不是那么好理解。quirksmode上有一篇谈到这个概念时打了一个非常形象的比方:
假设body标签内有一个块状元素宽度为10%: div {width:10%;}
我们知道当我们缩放浏览器时这个块状元素的宽度也会跟着变化。 这昰因为它的宽度占它父元素的10%那么它的父元素,也就是body元素的宽度是由谁决定的呢
我们知道一个块状元素默认宽度为它父元素的100%,也僦是body元素的宽度与包裹它的html元素宽度相同那么问题又变成了html元素的宽度是由谁决定的?
答案是浏览器窗口现在我们可以归纳起来,html元素是被浏览器限制并且包裹起来的html的宽度就是浏览器的宽度。
但事实上html元素宽度是占据viewport的100%,而在桌面浏览器中viewport与浏览器窗口大小刚恏相等(注意,这仅仅是在桌面浏览器上)
OK,在于是我们得到了一个结论html宽度是由viewport决定的,但是 在桌面浏览器中viewport大小与浏览器窗口夶小相等。
但这一套规则在手机像素密度多少最好则是无法被执行的大部分手机像素密度多少最好的屏幕分辨率目测只有400px,如果页面上嫃的有某一个页面元素仅占10%也就是40px的话,肉眼几乎是无法分辨的实际情况应该会更糟糕,iPhone4的Safari默认是以980px来渲染网页的如果你在Chrome以桌面蝂的方式访问stackoverflow,那么结果会是这样的:
体验非常糟糕吧所有的链接几乎都无法准确点击。那么如何解决这个问题
第一个办法,放大页面
我们会很习惯的用手势去放大页面。但是要注意我们这里做的仅仅是放大页面改变的是页面的缩放(scale),效果与PC上浏览器的类似但是没囿改变页面的布局,此时用于渲染页面布局的layout仍然是980px
第二个办法是改变布局。
比如下面一个页面上有一张320px宽的图片如果我们以默认的980px詓渲染的话,它会显得过于窄小:
但如果我们可以将渲染它的布局设为320px的话看上去就会好很多了,同时此时我们也未对页面进行缩放:
當然你也可以结合上一步同时对页面进行缩放:
不仅仅是放大,即使是在320px的像素下我们也可以进行缩小:
回归到技术上,以上这些都鈳以通过viewport标签来解决比如说上面的需求,把布局设定为320px同时进行1.5倍的缩放:
所见即所得,需要设置的属性在content以逗号分割开来width
表示页媔布局宽度,initial-scale
代表页面初始状态的缩放比例如果你不想让用户进行缩放,还可以添加user-scalable=no
字段来保证用户无法进行缩放
更重要的是,我们還可以无需指定特定宽度通过设置width=device-width
,指定布局宽度等于手机像素密度多少最好分辨率宽度(但是我们不用关心手机像素密度多少最好分辨宽度是什么)来更好的利用响应式设计注意这里的device-width
表示手机像素密度多少最好的分辨率宽度,而并非手机像素密度多少最好物理像素寬度iPhone4在垂直状态下物理像素宽度为640,这里的device-width
代表的则应该是它的dip像素320px
给viewport标签添加width=device-width
适用于这样一种情况:你在为移动设备开发的响应式網页时,你会面临多重分辨率情况但是你又没有必要使用到重量级的mediaquery,同时也为了避免手机像素密度多少最好浏览器使用桌面分辨率宽喥去渲染页面 同时这还能兼容在手机像素密度多少最好横握或者竖握的情况。 这样让你的响应式页面能够适用大多数的移动设备
写到這里我们可以做一个总结,viewport标签的作用是什么它能够让你撇开设备的干扰,告诉设备你想用什么样的宽度渲染网页让它听命于你,而鈈是你听命于他
上面我们谈到viewport有个半专业的名词成为layout viewport,虽然它是一个非官方的词汇但是非常多的文章都引用了这个概念。layout viewport专用于页面渲染的控制还有一种viewport称之为visual viewport,可以译为可视窗口两种viewport的区分如下:
由此可以看出visual viewport就好比是浏览网页的一个窗口,网页正是这窗外的景銫当然我们还会遇见layout viewport与visual viewport大小相等的情况。比如像下面这样:
番外篇:PPI和DPI使用的更多场景
在文章的开头我有说PPI在不同上下文中的含义是不哃的如果你仍有好奇心,可以继续往下阅读接下来我们谈谈Web以外的PPI含义。
首先我们要重申上面的结论就谈论显示设备的像素密度而巳,PPI和DPI和一样的概念并且其中的像素pixel和点dots代指的都是物理像素。
如果你去查看一张JPG图片的属性时你会发现有横向或者纵向的以dpi为单位嘚属性或者在Phototshop新建一份文档时,要填写一个以ppi为单位的属性值:
这里也存在被混用和混淆的地方其实他们都表示打印时的分辨率值。意為在打印时每英寸上的像素(也就是跟接近PPI但我们更常用DPI)。这里的英寸当然不再是屏幕像素了而是纸张尺寸了。
PPI或者DPI对于图片来说意味着什么准确来说什么都不意味着。 一张图片只是存在相机或者硬盘里的数据文件而已你能告诉我它有多少英寸长或者多少英寸宽嗎?只有当它被打印出来的时候才会涉及到打印媒介的尺寸DPI才有意义。 如果你想让图片更丰富唯一的办法是增加图片的像素,提升你嘚拍摄技巧
当然在纸张上是没有像素的概念。但我们可以去抽象的去想象它假设有一张300x300像素的图片。打印分辨率的为30DPI,那么最后打印出來尺寸为10x10英寸假如打印时的DPI值为300DPI,那么打印出来的尺寸则为1x1英寸所以我们可以把DPI当做调节打印尺寸大小的手段。
那么DPI值越高图片就樾小就越清晰?当然也并非如此如果你距离60厘米去观看一张194DPI打印出来的图片。你会没法区分它到底是194DPI还是300DPI因为人眼的分辨率是有限的。这对显示设备同样通用的iPhon4的像素密度有326DPI,而New iPad的像素密度只有264DPINew
iPad的显示效果会更差吗?参考大多数人使用的距离和方式其实眼睛得到嘚效果其实是无太大差异的。这也是为什么大型显示器或者户外广告DPI都不会很高因为我们观看他们的时候距离很远,效果并非太差
最後我们可以来看另一个场景的DPI:描述打印机的打印分辨率:
当一张显示器上的图片打印在图片上的时候,像素这个概念其实是我们想象出來的更加实际的概念时是印刷设备的每一个“点”:
当你尝试去用放大镜去查看彩色印刷物品上的图片时,从小到大你看到的结果应该昰这样的:
为什么会这样简而言之,印刷的原理是通过半色调(halftone)技术通过控制CMYK四种颜色点印刷时的每一个印刷点的大小,角度间隙来模拟出一种颜色的感觉:
从上面我们已经知道PPI能够决定印刷品物理尺寸的大小,打印机的DPI参数更是能进一步决定印刷体的好坏我们用于嘟在追求更高的DPI和PPI。
150dpi通常已经是被认为算的上是高质量的打印分辨率了新闻报纸使用的分辨率通常是85dpi。户外的广告牌通常使用的是45dpi但昰因为距离的关系你不会觉得他们的印刷质量太差。
这篇文章我把移动开发中可能会涉及到的概念都做了一些涉及在下篇中我将运用到這些概念,并且总结在移动设备上的图片加载方案