Retina 技术

问题

有没有遇到这样的一个问题,为什么在某些设备上需要用到2倍图。当你的设计师过来问你的时候,你可能回答她:因为这些设备像素高,需要高清的图片才能显示清楚呢。事实上要搞清楚这个问题,我们需要知道的不仅仅是分辨率这么简单。为了弄清楚为什么要用2倍甚至三倍图这个问题,我们首先需要来了解以下这些概念。有时候需要搞清楚底层的逻辑,我们才有跟产品或者设计谈论的资本。

概念

像素

像素(pixel)相信大家都比较熟悉,它是电子设备显示图像的最小单位。注意,它是最小单位,不能再分隔了。我们可以把屏幕理解成一个大的棋盘,这个棋盘上的每个格子就是像素块,很多的格子一起组成了我们看到的大的棋盘。如果你尝试着把一张图片(png,jpeg格式)无限放大,最后你会看到图片上的每一个彩色格子就是代表着一个像素。
Retina 技术

物理/独立像素

物理像素指的是一块固定大小的最小面积,它不会随着设备屏幕的变化而变化,它更有物理性质,例如我们说的300*600像素实际上就是说这块屏幕上最多可以显示的物理像素为横坐标300和纵坐标600个。简单来说,它的标准是统一的。物理像素在设备出场时就已经设置好了数量。而独立像素则不一样,独立像素在不同的屏幕上看起来是不一样的。它标识一个设备上最小的显示单位。它是独立于设备的用于逻辑上衡量像素的单位,因此才称之为独立像素。例如,iPhone 3GS 和 iPhone 4/4s 的尺寸都是 3.5 寸,但 iPhone 3GS 的分辨率是 320x480,IPhone 4/4s 的分辨率是 640x960,这也就是意味着同样长度的屏幕,iPhone 3GS 有 320 个物理像素,iPhone 4/4s 有 640 个物理像素。其实,一开始,是没有任何像素之分的,因为大家的像素大小都是这样,知道后来retina技术的出现,独立像素才从物理像素的概念中分离出来,用来标识可以再次分隔的物理像素。我们的CSS,其实表示的就是独立的逻辑像素,这个我们下面会提到。

位图&矢量图

我们常见的图片分为一种是png或者jpg后缀的图,这种图叫做位图。另外一种的svg后缀的矢量图。位图是由一块一块的点阵像素构成,放大后你可以看到每个小的像素块。就像上面的图片那样。矢量图则不是。矢量图无论你怎么放大,它都会保持清晰的线条和颜色,它本质就是几何图形。既然如此,那为什不选择矢量图作为图片呢?这是因为,矢量图在处理图片真实度和细节上面远远不如位图,位图是对现实世界的精细映射;而矢量图,它则是对现实世界的抽象表达。矢量图常用到图标和指示牌那种标识性的图片中,简介明了的含义,使得它在指示性图标上的应用非常广泛。与矢量图不一样,位图里面的每一个像素像素对应设备上的就是设备上的每一个像素,因此它和css里面的px在设备上的代表有时候是不一样的。这个我们会在下面提到。下面分别是png的位图(左)和一张矢量图(右边),可以看到,在色彩表达和细节上,矢量图完全无法和位图对比。
Retina 技术

PPI

PPI pixel per inch, 即每英寸单位里面的物理像素。分辨率是从整个设备屏幕的角度说明像素大小,来判断设备整体的清晰程度,而PPI则表示在同等物理单位内的像素个数,它同样也能说明显示清晰度。计算一个设备的PPI很简单,把屏幕内的像素除以屏幕物理面积,得到PPI的大小。一般来说,我们也可以认为PPI越高的设备,图像显示清晰度也会越高。
Retina 技术
理论上来讲,要想在单位相同改的面积上展示更多的元素,你要么增加屏幕的宽度,要么添加屏幕的分辨率。同一张图,在不同的分辨率的设备上在大小上会显得变得不同。前者的icon会保持不变,但是后者的icon会变小。

Retina 技术

分辨率

分辨率很好理解,它指的就是一块屏幕内总共像素块数量。比如我们平常听到的640*960像素,指的就是一块屏幕在横轴和纵轴上的像素块数量。通常,对于一块屏幕单位面积内的像素越高,它能显示的面积就越大。但是这样也会有问题随之而来,单位面积内像素越多,在屏幕上图片显示会变小。
现在你知道了,为了增加屏幕的分辨率,我们要么增加屏幕面积,或者增加像素点。还记的我们在window上修改分辨率的时候吗?看起来我们的屏幕可以容纳更多的东西了,通过调整分辨率来控制像素的个数。这个说法其实是有问题的,因为分辨率在设备出厂的时候就已经定义好了。但你调整所谓的分辨率时,电脑只是自动分配给你目标分辨率的有效像素块,剩下的就是计算机通过差值发给你补上了。直到补齐到原本分辨率所包含的像素块数量。可是,如果我们仔细观察会发现实际上我们的图标并没有出现所谓的虚化啊,这是因为软件未每个分辨率都准备不同大小的图标,保证即使我们修改分辨率后桌面的图标也不会被虚化。
Retina 技术

原理

retina技术

2010年苹果手机4发布了,这款手机的屏幕并没有相较于前一代产品更加大的屏幕,但是它却更清晰的展示了屏幕,这是因为它采用了将像素和独立像素分隔的办法,而不仅仅单纯地通过补齐色块来作为显示的方式了,这种方式,我们叫它视网膜(retina)技术。IPhone手机也称为首次将这种技术应用到实际产品中的一款电子设备。
我们看到同一块像素里面的像素是不同的,仔细来说是ihpone 4比左边的3要在单位内拥有跟多的像素。这各种技术,retina技术创造了一种称之为为备独立像素的东西,物理像素现在变成了最小的显示点阵了。而独立像素里面包含了多个设备物理像素点。但是里面却可以增加更多的像素进去。我们看到的ihpone 4就是iphone 3的高宽2倍,所以总得来说是4倍的清晰度。与之的调整分辨率使得屏幕容纳更多的像素不同。
如果你仔细考虑的话,其实retina技术不就是跟上面提到过的缩小像素面积的方式本质是一样的吗?你也这么理解当然是可以的,不过具体到使用上,比如css或者其他开发工具上的话,你就能明显感受到他们之间的差异,这也是这一片博文要回答的疑问。

pixel in css

CSS中的像素正常情况下是和一个设备像素相等的,应该是等于一个物理像素的宽度的css中的px单位,是一个相对单位,在不同的设备上表现是不一样的。css像素是虚拟像素,也就是设备独立像素,1px代表一个虚拟像素,即一个点,元素就是由这些点组成的,我们只能控制虚拟像素。在iphone 3中,一个px对应的是一个像素,而在4中,他的变化依旧是一个物理像素,但是在retina屏幕上因为独立像素是变小了,图片正常是应该缩小显示的,但是我们的css依旧让它保持了之前的尺寸,因此,当我们把一张100*100 的图放到不同的设备,并且强制它有100px的框和高时,我们实际上是把这张图拉开了四倍。下面这张图表示的css对应到实际的物理像素,在大小不变的情况下,右边的retina屏幕多出来4倍的像素块。如果我们把一张图不设置样式的情况下放到传统屏幕和retina屏幕上去的话,这张图会看起来大小不一样,传统的屏幕的会试试大一些,而后者则会变得小。
Retina 技术
css之所以要这么做是因为给他的定义就是要保持在不同设备上的视觉大小一致。下面是对css像素档位的定义:

It is recommended that the reference pixel be the visual angle of one pixel on a device with a pixel density of 96dpi and a distance from the reader of an arm’s length. For a
nominal arm’s length of 28 inches, the visual angle is therefore about 0.0213 degrees.

因为不同的设备像素点是不一样的,为了保证我们的视觉效果不管在任何设备上,css像素对应也需要有逻辑上的显示。到这里你应该明白了,如果是把物理像素面积缩小,css对应的也是会缩小的,而retina则不影响css设计的宽度,也就是物理像素的面积。所以,所让两者都是在单位面积内增加像素的点阵,但造成的结果是截然不同的。

为什么糊了

当我们为web上的图片指定一个固定的px宽和高时,我们是指定了一个设备的独立像素,当图片在高分辨或者是retina的设备上实际是是强行把一个像素拉成了多个物理像素。计算机在像素不足的时候会为你自动补齐确实的颜色,这个写颜色补齐方式是根据周围的颜色来取近似值的。例如周围主要是蓝色,则补足蓝色。红色则补足红色。因此,我们在看web上的图片,感觉会像是糊了一样。这种方法叫做线性插值法。你可以参考这篇文章来观察线性插值法的工作方式。

如果你写过webgl等图形程序,也应该会听过这个名词。给不同的顶点设置不同颜色的时,计算机会按照近似值的插入方式将图像光栅化。

Retina 技术

解决方案

IOS原生系统

在ios系统中,苹果提供给开发者一些命名上的规范,来决定加载那些图片,例如a@2x.png会在设备为2的机器上加载图片。我们在开发的时候一般也忽你采用这规则来进行图片配置。

javascript

浏览器提供给开发者一个可以检测是被设备像素比的api,利用这个api,我们可以知道当前设备的像素是多少,然后根据图片来进行适配。

var dpr = window.devicePixelRatio;
$(".J-replace").each(el => {
	const replceSrc = el.data("src").replace(/([\w\/.-]+@)(\d)(x\.[a-z]+$)/, '$1' + dpr + '$3');
  // 匹配替换到已经准备的图片../a@2x.png
  el.attr("src", replaceSrc);
})

<img class="J-replace" src="../a@1x.png" />

CSS

使用css也可以用判断当前的屏幕设备像素比。这个就跟屏幕宽度的条件是一样的,利用到的也是css 的媒体查询属性。不过这种情况使用于把图片作为背景图片的情况来使用。

/* Exact aspect ratio */
@media (aspect-ratio: 1/1) {
  img {
    background: url(../a.png);
  }
}
 
/* Minimum aspect ratio */
@media (min-aspect-ratio: 2/1) {
  img {
    background: url(../a@2x.png);
  }
}
 
/* Maximum aspect ratio */
@media (max-aspect-ratio: 3/1) {
  img {
   	background: url(../a@3x.png);
  }
}

HTML

不管是使用js或者css,都是需要我们编码来完成的,他们要么是难以维护或者对性能不太友好,要么是使用场景有限制。不过新云的是,我们有一种完美的方式来解决这个问题。得意与浏览器的进化,我们现在可以向原生的IOS一样通过标签上设置熟悉,来指定需要加载那些图片了,使用起来非常便捷。

<img src="../a.png" src-set="../a@2x.png,../a@3x.png" />

使用原生的标签,不仅仅使的我们的代码看起来不再冗余,更重要的是它减少了脚本和样式对html的操作,让浏览器自己给你应用加载需要的图片,对开发者非常友好。

结束

为什么是300

也许会有这样的疑问,如果我把ppi提高,是不是可以做到将图像显示更清晰呢?答案是的,因为如果一个独立像素包含更多的设备像素,图片显示的是越“清晰”。但是给你一台最“清晰”的设备,如果人的眼睛不能分辨的话,它图像质量再好也是无用的对吗?关于人眼契合分辨率在下面这篇文章有说到。

there's a magic number right around 300 pixels per inch, that when you hold something around 10 or 12 inches away from your eyes, is the limit of the human retina to differentiate the pixels.

这是乔布斯在retina发布上说过的一句话,大概意思就是:300ppi是人的眼睛在10 或者 12 英寸(正好是人的眼睛距离手持移动设备的正常距离范围)距离看设备时,识别最小分辨率的极限。过了这个极限范围,再高的ppi人眼就无法分别质量的粗细了。这也是为什么过了这么久,iphone和新起的安卓手机系列的ppi一直保持在这个范围。

参考文档

https://www.kancloud.cn/xiak/quanduan/708595
https://webglfundamentals.org/

上一篇:javascript – SVG渲染到视网膜显示屏上模糊的画布


下一篇:IOS 特定于设备的开发:监测Retina支持