移动端高清适配方案(解决图片模糊问题、1px细线问题)
干货储备:
物理像素、设备独立像素和设备像素比
在CSS中我们一般使用px作为单位,需要注意的是,CSS样式里面的px和物理像素并不是相等的。CSS中的像素只是一个抽象的单位,在不同的设备或不同的环境中,CSS中1px所代表的物理像素是不同的。在PC端,CSS的1px一般对应着电脑屏幕的1个物理像素,但在移动端,CSS的1px等于几个物理像素是和屏幕像素密度有关的。
物理像素(physical pixel)
物理像素又被称为设备像素、设备物理像素、它是显示器(电脑、手机屏幕)最小的物理显示单位,每个物理像素由颜色值和亮度值组成。所谓的一倍屏、二倍屏(Retina)、三倍屏,指的是设备以多少物理像素来显示一个CSS像素,也就是说,多倍屏以更多更精细的物理像素点来显示一个CSS像素点,在普通屏幕下1个CSS像素对应1个物理像素,而在Retina屏幕下,1个CSS像素对应的却是4个物理像素。
设备独立像素(device-independent pixel)
设备独立像素又被称为CSS像素,是我们写CSS时所用的像素,它是一个抽象的单位,主要使用在浏览器上,用来精确度量Web页面上的内容。
设备像素比(device pixel ratio)
设备像素比简称dpr,定义了物理像素和设备独立像素的对应关系:设备像素比 = 物理像素/设备独立像素。
CSS的1px等于几个物理像素,除了和屏幕像素密度dpr有关,还和用户缩放有关系。例如,当用户把页面放大一倍,那么CSS中1px所代表的物理像素也会减少一倍。
viewport
viewport就是设备上用来显示网页的那一块区域,但viewport又不局限于浏览器可视区域的大小,它可能比浏览器的可视区域要大,也可能比浏览器的可视区域要小。在默认情况下,一般来讲,移动设备上的viewport都是要大于浏览器可视区域的,这是因为考虑到移动设备的分辨率相对于桌面电脑来说都比较小,所以为了能在移动设备上正常显示那些传统的为桌面浏览器设计的网站,移动设备上的浏览器都会把自己默认的viewport设为980px或1024px(也可能是其他值,这个是由设备自己决定的),但带来的后果就是浏览器会出现横向滚动条,因为浏览器可视区域的宽度是比这个默认的viewport的宽度要小的。
明确三种不同的 viewport 视口:
visual viewport 可见视口,指屏幕宽度
layout viewport 布局视口,指DOM宽度
ideal viewport 理想视口,使布局视口就是可见视口即为理想视口
获取屏幕宽度(visual viewport)的尺寸:
window.innerWidth/Height
获取DOM宽度(layout viewport)的尺寸:
document.documentElement.clientWidth/Height
设置理想视口 ideal viewport:
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
该meta标签的作用是让layout viewport的宽度等于visual viewport的宽度,同时不允许用户手动缩放,从而达到理想视口。
meta[name="viewport"]里各参数的含义为:
width:设置 layout viewport 的宽度,为一个正整数,或字符串"width-device";
initial-scale:设置页面的初始缩放值,为一个数字,可以带小数;
minimum-scale:允许用户的最小缩放值,为一个数字,可以带小数;
maximum-scale:允许用户的最大缩放值,为一个数字,可以带小数;
height:设置layout viewport的高度,这个属性对我们并不重要,很少使用;
user-scalable:是否允许用户进行缩放,值为 "no" 或 "yes";
rem适配方案
适配是为了使页面在不同手机设备上,相对保持统一的效果。移动端自适应方案很多,有百分比布局,弹性和模型布局等,但是最好用的要数 rem 布局了。
rem是相对于根元素的字体大小的单位,我们可以根据设备宽度动态设置根元素的 font-size,使得以 rem 为单位的元素在不同终端上以相对一致的视觉效果呈现。
如下是3种根据屏幕宽度设置rem基准值的方法:(注:为了换算方便,以下三种方法都用 1:100的比例,即 1rem = 100px)
用JS设置rem基准值:
用密集的媒体查询设置font-size:
用单位vw设置font-size:
1vw等于屏幕可视区宽度的可是区域的百分之一;
注:兼容性不是很好;
移动端开发中,由于屏幕分辨率导致的两个经典问题:图片模糊问题和1px细线问题。(注:以下多倍屏均只叙述2倍Retina屏,其它屏幕同理。)
>>>> 图片模糊问题
一个位图像素是栅格图像(如:png,jpg,gif等)最小的数据单元。每一个位图像素都包含着一些自身的显示信息(如:显示位置,颜色值,透明度等)。理论上,1个位图像素对应于1个物理像素,图片才能得到完美清晰的展示。对于dpr=2的Retina屏幕而言,1个位图像素对应于4个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,导致图片看起来比较模糊,如下图:
对于图片模糊问题,比较好的方案就是用多倍图片(@2x)。如:一个200 x 300(CSS pixel)的img标签,对于dpr=2的屏幕,用 400x600 的图片,如此一来,位图像素点格式就是原来的4倍,在Retina屏幕下,位图像素点个数就可以跟物理像素点个数形成 1:1 的比例,图片自然就清晰了。
如果普通屏幕下,也用了两倍图片,会怎样呢?
在普通屏幕下,200 x 300(CSS pixel) img 标签,所对应的物理像素个数就是 200 x 300个,而两倍图片的位图像素个数是 200 x 300 x 4 个,所以就出现一个物理像素点对应 4 个位图像素点,但它的取色也只能通过一定的算法取某一个位图像素点上的色值,这个过程叫做 downsampling,肉眼看上去虽然图片不会模糊,但是会觉得图片缺少一些锐利度,或者是有点色差,如下图:
所以最好的解决方法是:不同的 dpr 下,加载不同的尺寸的图片。不管是通过CSS媒体查询,还是通过JS条件判断都是可以的。
>>>> 1px 细线问题
CSS像素为1px宽的直线,对应的物理像素是不同的,可能是 2px 或者 3px,而设计师想要的 1px 宽的直线,其实就是 1 物理像素宽,如下图:
对于CSS而言,可以认为是 border:0.5px;这是多倍屏下能显示的最小单位。然而,并不是所有手机浏览器都能识别border:0.5px,有的系统里,0.5px会被当成 0 px 处理,那么如何实现这 0.5px呢?网上有很多解决方法,如 border-image 图片、background-image 渐变、box-shadow 等。
推荐两种比较好用的方法:
①.伪元素 + transform
构建1个伪元素,border为1px,再以transform缩放到 50%
②.用JS计算rem基准值和viewport缩放值
这个"用JS计算rem基准值和viewport值"的方案可以解决1px细线问题;以2倍Retina屏做比较,如下表格,其他多倍屏同理:
用JS根据屏幕尺寸和dpr精确地设置不同屏幕所应有的rem基准值和initial-scale缩放值,这个JS方案已经在完美解决了1px细线问题,我们不需要再做任何事情,至于图片模糊问题,只要根据data-dpr的值动态加载不同尺寸的图就可以了。