关于手机屏幕
普通显示屏:旧手机,iPhone3,在屏幕上能看到物理像素点
高清显示屏(视网膜屏、retina屏幕):可以把更多的像素点压缩到一块屏幕内,基本看不到物理像素点了,如iPhone4,
iPhone3和iPhone4的屏幕宽度都是320px。对于物理像素,前者320*280,而后者640*960。后者的像素密度更大,以至于肉眼基本看不到像素点了。
对于普通显示屏,1px对应一个像素点。高清显示屏内1px对应2个像素点了;高清的屏幕,如iPhone6plus,1px对应3个像素点。这种对应关系,有个名词来形容,物理像素比,dpr。
iphone4的大小是320*480,这里说的尺寸是px。媒体查询判断的device-width也是px为单位的
dpr代表每一px要对应多少个物理像素点,如对于2dpr的屏幕,一个1px*1px的div,就需要占用4(2*2)个物理像素点。这样做的好处是px在不同dpr(分辨率)的设备上显示的视觉距离是一致的,所以px也称设备独立像素。换句话说就是:固定px的同一元素,在不同设备上所显示出的大小都是一致的!
高清图片解决方案
图片1px对应一个位图像素,最好的显示效果是一个位图像素显示一个物理像素上。但是不同dpr的手机,单位px对应不同的物理像素。图片显示在2dpr的设备时,图片的每1个位图像素会被拆分成4(2*2)个物理像素来显示,拆分的方式是4个物理像素会显示1个原位图像素的邻近色,这会导致图片显示模糊。如何能让一个位图像素对应一个物理像素呢?
答案就是缩小图片的显示。让1px图片对应多个位图像素即可,对应到屏幕上就可一个位图像素对应一个物理像素了。缩小图片也有以下两种方式:
方式1:视口不缩放,而缩小图片的显示尺寸
对于dpr2的手机使用两倍图(@2x)。如图片的尺寸为40px*60px(位图像素点为40*60),显示到20px*30px的地方上(对应屏幕20*2*30*2的物理像素点),刚好一对一。
方式2:缩放视口,原尺寸显示图片
对于图片按照原尺寸显示(不控制图片的显示尺寸,2dpr下显示2倍图,3dpr下显示3倍图),通过viewport的initial-scale来设置整体缩放,也就是说通过屏幕的dpr来动态设置这个值(对于2dpr则scale:0.5,3dpr则scale:0.333),这是一种viewport的非标准化设置,淘宝的页面就是这样做的。因为scale会作用与整个页面,所以一般要配合rem布局,如3dpr下,font-size设置为3倍,这样页面对应的尺寸就都是3倍了,然后再通过scale缩小回去,这样页面内容就正常了,而且图片也没问题
这个方式总结下来就是:rem整体放大,然后图片原图显示,不控制尺寸,渲染到viewport上就是页面整体都是放大的,接着将viewport缩小回原尺寸。而且这个方案同时解决了1px border问题。
为什么需要布局的屏幕适配?
没有做屏幕适配的页面是仅仅加了一句标准的viewport设置而已(width=device-width,scale=1),这句设置让页面能以比较好的效果(不缩小)显示到移动设备上,但没办法统一不同尺寸设备下的页面布局效果。举个栗子:首页并排显示了两张图片,第二张图片大概只显示了1/5 。如何在不同的设备下都做到这样的效果?回顾以上一句结论:“固定px的同一元素,在不同设备上所显示出的大小都是一致的!”,所以在iphone4上如果实现了这样的效果,但在iphone6上,第二张图片可能就显示了1/2了。为了在不同的设备上显示的效果一致,所以我们要做屏幕适配。
现实中实际上做了一种妥协:不同设备的宽高比例都是不同的,所以在x轴上做到了适配,在y轴上就做不到适配,除非改变页面元素的宽高比例(如正方形变成长方形),但一般不会这么做。所以所说的适配都是指宽度或者说x轴方向、水平方向上的布局适配。
总结以上屏幕适配的要求:1. x轴上布局一致,2.元素的宽高比例不能发生变化。
因为不同设备的宽度不同,所以要实现以上两点,就要求页面内的尺寸不能写死,而是都要跟设备的宽度相关联,也就是说不同宽度的设备下元素显示的大小必定不一致。
怎么做布局的屏幕适配?
理想的情况下是我们根据一张设计稿,做出了一个页面,这个页面在所有的移动端设备下显示,都满足以上的适配要求。
换另一个角度来思考以上这个理想情况:我们的页面是一张宽高比例固定的矢量图(图片中的布局效果是对应设计稿的),等比例缩放这个矢量图放在不同的设备下显示(图片的宽度与设备的宽度刚好一致),这也满足了我们上面说的适配要求。这个过程中有两个问题需要解决:
1. 如何把页面抽象成一张宽高比例固定的矢量图(元素在宽度为n的屏幕上显示,尺寸为k;在宽度为2n的屏幕则尺寸为2k)?
2. 怎么知道在不同设备下,这个图片该缩小或扩大多少,才与设备的宽度刚好一致?
解决以上两个问题,屏幕适配就做好了。
页面内的元素也要求等比例缩放,目前我知道的也就是用rem,所以所有元素的尺寸都使用rem作为单位,然后使用js获取设备的宽度,比上设计稿的宽度,得出一个比例系数,根据这个比例系数来调整font-size,从而改变rem即可。
项目地址:https://github.com/947133297/mobile-demo 中的mbase。思路就是:以rem为单位,在css中记录元素尺寸与屏幕宽度的关系。假设以宽度为300px的手机进行开发,上面元素的尺寸为200px,则css中写元素的尺寸为2/3rem,这个2/3相当于就是元素与屏幕的尺寸比例。运行时通过js获取屏幕的宽度,然后直接把这个尺寸设置为html 的font-size值。当显示到300px的手机上时,元素的尺寸 = 2/3*300 刚好200px。当显示到了600px的手机上时,元素尺寸 = 2/3 * 600 = 400。实现了等比例放大了,有布局适配的效果了。
总结这种方式就是:元素的尺寸(或者距离)与当前开发机的屏幕宽度的比例关系(通过scss来辅助计算),要记录到css中,然后通过js获取宽度设置html标签的font-size上,即实现等比例缩放页面。
对于字体、1px border 额外处理,其他与布局搭配的还有:flex伸缩容器、流式布局(百分比布局),bootstrap栅格系统响应式布局、媒体查询等
1px border问题
设计师想要的1px,对于我们来说是1物理像素点的意思。在retain(2dpr)设备下,我们直接写1px,会对应y方向2个物理像素点(x方向不讨论),但设计师想要的是1物理像素点,所以和以上高清图片解决方案一样,我们需要把元素缩小,进行y方向的0.5倍缩小(2dpr下),解决问题。
可以通过css来缩小,其他通过设置viewport的scale属性为0.5也可以。这里对scale的理解,可以类比为平时用户用两个手指来缩放页面,缩放的时候元素所占据的物理像素点也会同时减少或增加。修改viewport的scale属性,会作用到页面的所有元素上。
首先测试如下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width" />
<style>
#main{
height: 20px;
border-bottom: 1px solid #000;
}
</style>
</head>
<body>
<div id="main"></div>
</body>
</html>
配置外部访问。webstorm这个配置真是坑爹的。如果这里端口的位数是5位,这个配置就无效,一定要改成4位才行
以上配置过后,就可以通过ip地址来访问页面了。查看无线网卡的配置:
对外的地址是192.168.191.1 。在QQ浏览器上访问,发现效果如下:
明显比 1px 要粗。解决办法使用媒体查询,修改后的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width" />
<style>
#main{
height: 20px;
}
.border-1px:after{
content:" ";
display: inline-block;
width: 100%;
border-bottom: 1px solid #000;
}
@media (-webkit-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5){
.border-1px:after{
transform:scaleY(0.7)
}
}
@media (-webkit-min-device-pixel-ratio: 2.0),(min-device-pixel-ratio: 2.0){
.border-1px:after{
transform:scaleY(0.5)
}
}
@media (-webkit-min-device-pixel-ratio: 3.0),(min-device-pixel-ratio: 3.0){
.border-1px:after{
transform:scaleY(0.3)
}
}
</style>
</head>
<body>
<div id="main" class="border-1px"></div>
</body>
</html>
重新访问,正常了: