侧边栏

理解分辨率

发布于 | 分类于 前端/CSS

在前端、客户端开发UI的工程中,肯定绕不开分辨率这个东西。我们所说的分辨率到底是什么呢?不同的分辨率对编写代码有什么影响呢?本文将解答这些问题

参考

相关概念

英寸

按照习惯,描述屏幕大小时,使用的“寸”这个单位,比如4.7寸的手机屏幕、22寸的电脑屏幕、65寸的电视机屏幕等,实际上指屏幕对角线的长度,单位是“英寸”

1英寸 = 2.54 厘米

物理像素

图像、电子屏幕实际上是由一个个具有特定颜色和位置的小方块组成的,通过控制每个点的颜色,就可以展示不同的内容。

可以把物理像素理解为组成图像或屏幕的最小单位,他是显示设备中一个最微小的物理部件。

显示屏是由一个个物理像素点组成的,通过控制每个像素点的颜色,使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位pt。

分辨率

既然屏幕是由像素组成的,除了英寸之外,也可以用物理像素的数量来描述屏幕的大小,称之为分辨率

分辨率 = 画面水平方向的物理像素个数 * 画面垂直方向的物理像素个数

英寸是一个物理上真实的长度单位,1英寸 = 2.54 厘米,不会变多,也不会变少。但分辨率看起来明显是一个数量,而不是长度,如何用来描述屏幕的大小呢?

当我们使用分辨率来描述屏幕的大小时,并不是指的屏幕的真实物理大小,而是指组成屏幕的像素数量

在生活中,经常能够听到诸如1080P2K4K的概念

  • P 代表的就是屏幕纵向的像素个数,1080P 即纵向有 1080 个像素,平常所说的高清屏其实就是屏幕的物理分辨率达到或超过 1920x1080 的屏幕。
  • K 代表屏幕横向有几个 1000 个像素(也有标准定的是1024个),2K即横向像素大于等于2000个

在电视领域,有一个16:9的尺寸标准,因此对于电视而言,1080P就是 1920*1080的物理分辨率,4K就是4096x2160的物理分辨率(也有一种是3840x2160分辨率的4K)

但并不是所有屏幕都是16:9,细究下去,会发现屏幕分辨率的标准实际上是很“混乱”的。

图片也是由像素组成的,我们常说的图片分辨率指的是图片含有的像素数量,如100*100的图片,表示其有10000个像素点,

需要注意的是,物理尺寸和分辨率在真实大小上并不是等价的。

同样分辨率大小的图片,可以放在不同物理尺寸的的屏幕上进行缩小或者放大展示。在屏幕上按1:1展示一张分辨率为100*100的图片时,水平方向上有100个像素点,竖直方向上有100个像素点。

同一张图片,分辨率越高,意味着组成图片的像素点越多,以相同物理尺寸展示时图片就越清晰。

视频是由图像组成的,每一帧切换一张图片。我们常在视频网站上看见720P、1080P等用来区分视频的清晰度,也可以用上面P、K的定义来理解

比如某些视频网站上,需要开通会员可以观看更高清的视频,当浏览器窗口都是同样的大小,但分辨率不同时,4K的视频明显要比720P的清晰很多。

像素密度与逻辑像素

举个例子,iPhone 3gs和iPhone4,他们的物理尺寸都是 3.5英寸,但他们的分辨率一个是320 * 480 ,一个是 640 * 960。

因此可以推算出,在1英寸的长度中,iPhone4展示的像素数量是iPhone 3gs的2倍,这里就会涉及到一个叫做像素密度的概念。

像素密度ppi = Math.sqrt(x^2 + y^2, 2), x、y分别表示设备的物理像素分辨率,表示每英寸的长度上排列的像素点数量,像素密度越高,代表屏幕显示效果越清晰。

如果是一个320像素宽的元素,在320x480的屏幕上会占满屏幕的宽度,而在640x960的屏幕上只会占一半的宽度。

对于同一张图片,按照1:1在屏幕上展示,在320*480的屏幕上的大小会是640x960屏幕的两倍大小,因为高分辨率的屏幕,页面上展示相同像素的图片时会更小,也就是说,高分辨率的屏幕,可以摆放更多的内容。

然而在某些场景下,高分辨率并不是为了展示更多的内容,而是为了提高展示内容的精细程度!!理解这一点非常重要。

乔布斯在iPhone4上提出了视网膜屏幕Retina Display的概念,在iphone4中,把2x2个像素当做1个像素使用,这样可以让屏幕展示的效果更精致,而元素的大小并不会改变。

视网膜屏幕的主要特征是在正常视距下使肉眼无法分辨屏幕的单独像素。因为机构研究表明:只要手机像素密度>=300ppi之后,就不会再出现颗粒感;这个阈值对于平板是260ppi,对于电脑是200ppi

既然屏幕系统支持了这个功能,我们还必须有一种尺寸单位,用来描述在不同分辨率的屏幕上显示相同的元素大小,这就是设备独立像素,又称设备无关像素(Device Independent Pixels,简称DIP 或 DP,没错,就是安卓常用布局单位dp)或逻辑像素。

现在我们就有两个“像素”了,一个是显示分辨率,可以理解为物理像素;一个是设备独立像素,是逻辑像素。在谈论像素时,一定要考虑到底是哪种像素

他们之间的比例被称为设备像素比,常说的几倍屏几倍屏也是这个意思

设备像素比 (dpr)= 物理像素(dp) / 设备独立像素(dip),在web中,可以通过window.devicePixelRatio获取dpr

css像素

web开发中常用的css像素,也是设备独立像素。换言之,1px的css像素,在1dpr和2dpr的被设备上,占据的物理像素是不一样的

这也是为什么我们写一个宽度为375px的元素,在iPhone6上也可以铺满屏幕——尽管iPhone6的逻辑像素是750*1334。

分辨率适配

要将设计图还原为网页,并呈现在不同分辨率的时候上时,还需要考虑响应式布局的问题。

比如设计师的设计标准是750*1334,对于一个750宽度的元素,设计师实际上期望的不管是在iPhone4、iPhone6还是在某个更大屏的安卓手机上,都这个元素都能铺满屏幕,而不是具体的设备独立像素换算之后的宽度,

否则,在大屏幕的机型上可能没有铺满,在小屏幕的机型上又有滚动条。

为了解决这个问题,开发者一般会根据设备的实际宽度,将标准设计尺寸750按比例缩放之后设置新的像素值。

最常见的做法是使用rem布局,大致原理为

  • 根据浏览器窗口的实际大小,动态计算根节点的font size,这个值会应用到rem单位上
  • 所有需要适配的样式尺寸,通过rem单位来进行设置

可以通过calc通过纯CSS的方式实现rem布局方案

scss
// 假设是750基准的设计图
html {font-size: calc((100vw / 750) * 100);}

@function rem($px) {
    @return 1rem * ($px/100);
}

// 使用方式: rem(40) 40为从设计图量取的尺寸

另外还有使用诸如vw等单位来实现的,原理大同小异,这里就不再展开了

常见问题

移动端1px边框

css像素与物理像素不一致时,就导致了移动端开发中经常会遇见的1px边框问题:设计说这个1px的边框变粗了!!

在dpr为2的iPhone上预览时(现在的大部分手机)

可以很明显地看见1px的边框确实变粗了

当在dpr为1的设备上预览时边框显示正常。

在dpr大于1的屏幕上1px边框显示异常,这是由于css像素是逻辑像素,在dpr为2的设备上渲染时,会使用4个像素来渲染一个逻辑像素。

上面的0.5px展示了一种解决方案:在 WWDC大会上提出的,当写 0.5px的时候,iOS就会显示一个物理像素宽度的 border,而不是一个逻辑像素的 border。

上面的0.5px在iOS老版本、Android上存在兼容方案,目前主流的 伪元素+transfrom:scale 应该是兼容性比较好的方案

css
.scale-1px {
    position: relative;
    border: none;
}

.scale-1px:after {
    content: '';
    position: absolute;
    bottom: 0;
    background: #000;
    width: 100%;
    height: 1px;
    -webkit-transform: scaleY(0.5);
    transform: scaleY(0.5);
    -webkit-transform-origin: 0 0;
    transform-origin: 0 0;
}

canvas保存的图片不清楚

首先需要知道,canvas绘制的是位图(由像素点组成,与对应的是矢量图,可以任意缩放而不失真)

当我们为canvas设置属性宽高时,表示画布由多少个物理像素点组成,而设置css样式宽高时,表示画布占据的逻辑尺寸

如果只设置属性宽度或者只设置css宽度,则canvas属性宽度与其css宽度相等,这种情况下一个CSS像素点表示一个canvas像素点。

html
<canvas width="300" height="150"></canvas>

在这种情况下,我们将canvas转换为300*150的图片,然后再用retina屏幕进行访问,此时会使用多个物理像素来展示一个canvas位图像素,由于cavas单个位图像素点不可再分,只能就近取色,就会导致图片看起来变模糊了。

为了避免出现就近取色的问题,就需要保证1个canvas位图像素等于1个物理像素。一个简单的思路是使用更多的物理像素点来进行绘制canvas图片

html
<canvas width="600" height="300" style="width: 300px; height: 150px"></canvas>

上面这段代码可以理解为canvas画布包含600*300的像素点,但在屏幕上展示的是300x150的尺寸,这时,相当于我们手动控制了canvas画布的dpr为2,即一个CSS像素点实际表示canvas上的4个像素点。

基于上面的分析,如果希望canvas保存的图片不模糊,理想状态是1个canvas像素点等于一个物理像素点,而不是等于一个css像素点

js
let dpr = window.devicePixelRatio
let { width: cssWidth, height: cssHeight } = canvas.getBoundingClientRect();

canvas.width = dpr*cssWidth
canvas.height = dpr*cssHeight

// 如果按照之前设计图上的尺寸,绘制出来的内容会变小,因此此处使用scale扩大绘制比例;
// 或者在每个绘制尺寸的地方手动*dpr也是可以的,不过这种方式就显得比较麻烦
ctx.scale(dpr, dpr);

这样可以保证导出的图片分辨率较高,也就比较清晰了

你要请我喝一杯奶茶?

版权声明:自由转载-非商用-保持署名和原文链接。

本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。