做过移动端适配的小伙伴一定有遇到过这行代码:
但是,很多小伙伴只是感性的认识:噢,我加了这行代码,然后页面的宽度就会跟我的设备宽度一致。然而,这种理解是很片面的。那么,这句话的本质到底是什么呢? 不急,我们先往下面看,这里先留个悬念。 这里,我们先来辨析一下在适配的时候经常会遇到的一些名词、数值单位。 首先,先来看一下物理像素。 以iphone6为例,可知道: 分辨率:1334pt x 750pt 指的是屏幕上垂直有1136个物理像素,水平有750个物理像素。 屏幕尺寸:4.7in 注意英寸是长度单位,不是面积单位。4.7英寸指的是屏幕对角线的长度,1英寸等于2.54cm。 屏幕像素密度:326ppi 指的是每英寸屏幕所拥有的像素数,在显示器中,dpi=ppi。dpi强调的是每英寸多少点。同时,屏幕像素密度=分辨率/屏幕尺寸 接着,我们来看一下其他的单位。 设备独立像素:设备独立像素,不同于设备像素(物理像素),它是虚拟化的。比如说css像素,我们常说的10px其实指的就是它。需要注意的是,物理像素开发者是无法获取的,它是自然存在的一种东西,该是多少就是多少。 设备像素比:缩写简称dpr,也就是我们经常在谷歌控制台移动端调试顶端会看到的一个值。设备像素比 = 设备像素 / css像素(垂直方向或水平方向)。可以通过JS来获取: window.devicePixelRatio 注:以下涉及的像素均为CSS像素。并且默认不考虑缩放。 布局视口 写过css的小伙伴应该知道,我们在 html、 body设置 width:100%;height:100%;的时候,它并不是无效的。我们都知道 100%这种百分数应该是继承父元素而来的。那在这里是继承哪里的呢? 在PC浏览器中,有一个用来约束CSS布局视口的东西,又叫做初始包含块。这也就是所有宽高继承的由来。除去 margin、 padding,布局视口和浏览器可视窗口宽度是一致的,同时也和浏览器本身的宽度一致。 但是在移动端,就大不一样了。 以下的例子是在不加 meta标签的前提下进行演示的。 假如我们现在做一个二八分的左右布局,那么如果在PC端上面的话,显示的效果非常完美,这没什么好说的。 那如果是在手机端呢,这里以iphone6为例子来讲解: 图例如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190308153906317.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ExODA4ODM1NjY2NQ==,size_16,color_FFFFFF,t_70) 代码如下: * { margin: 0; padding: 0; } html, body { height: 100%; width: 100%; } .left { float: left; width: 20%; height: 100%; background: red; } .right { float: right; width: 80%; height: 100%; background: green; }很明显,由于位图像素不够分而产生模糊的情况,解决的办法十分简单,就是使用跟dpr同个倍数大小的图片。比如iphone6,一个200x300的 img标签,原图就要提供400x600的大小。
那么当加载到 img标签中,浏览器会自动对每1px的css像素减半,可以理解为此时还是维持着1:1的css像素:物理像素,不产生模糊。
这个做法其实就是手淘团队在做retina适配的一个重要的原理之一,后面会讲到,这里先放着不说。
反向思考一下,如果普通屏幕,也就是dpr为1的屏幕,也使用了两倍的图片,会发生什么样的情况呢?
很明显,在普通屏幕下,200×300的 img标签,所对应的物理像素个数就是200×300个,而两倍图片的位图像素个数则是200x300x4,于是就出现一个物理像素点对应4个位图像素点,所以它的取色也只能通过一定的算法进行缩减,显示结果就是一张只有原图像素总数四分之一,肉眼看上去虽然图片不会模糊,但是会觉得有点色差。(其实就是模糊的逆向过程)
用图片来表示就是:
这里摘取了网上一篇博文的demo来阐述上面所说的问题。
以上是一张100x100的图片,分别放在了100x100,50x50,25x25的容器中,在retina屏幕下面的显示效果。
通过取色器放大镜可以看出边界像素点的差别:
在图一中,边界像素点就近取色,色值介于红白之间,偏淡,图片看上去会模糊(可以理解为图片拉伸)。
在图二中,图片正常,很清晰。
在图三中,边界像素点就近取色,色值介于红白之间,偏浓,图片看上去有色差。
现今,适配手机端的传统rem布局已经逐步被手淘团队的一套flexible布局代替。
具体的实现方式以及细节这里也不铺开来说,具体参考w3cplus的一篇文章,很容易读懂和理解。
这里我更想分析一下flexible.js做法的意义和原因。
读过文章之后,相信大家应该对整个开发适配的流程比较熟悉了。
假设现在要适配一个iphone6的设备。上面已经说过了iphone6的各个参数,这里不再赘述,需要的自行上移查看。
于是:
设计师给了一个750px宽度的设计稿(注意这里是750px而不是375px)
前端工程师用750px的这个比例开始还原
把宽高是px的转换成rem
字体使用px而不使用rem
flexible.js会自动判断dpr进行整个布局视口的放缩
从flexible.js中可见,在宽高中使用的是rem,这是为了保证在不同宽度尺寸的设备中能够保证布局的等比例缩放。
而为什么字体不使用rem而是采用px呢?
首先,用过rem单位的小伙伴都会发现,使用rem后由于不同的尺寸,换算之后出现各种奇奇怪怪的数值,最为明显的就是更多的小数位,比如 13.755px之类的数值。在浏览器中,各浏览器中对小数点的计算存在偏差,而且有些带小数的 font-size值在特定的浏览器显示并不够清晰。
其次,我们并不希望在小屏幕下面显示跟大屏幕同等量的字体。并且如果使用rem的话,那么由于等比例的存在,在小屏幕下就会存在小屏幕字体更小的情况,不利于我们更好的去阅读,违背了适配的初衷。所以,对于字体的适配,更好的做法就是使用px和媒体查询来进行适配。
所以,也就不难解释为什么要对 font-size进行放大的处理了,如下的sass代码:
@mixin font-dpr($font-size) { font-size: $font-size; [data-dpr=“2”] & { font-size: $font-size *2; } [data-dpr=“3”] & { font-size: $font-size *3; } }
由于retina屏幕下dpr的不同,我们又想显示的字体一样大,于是就给字体再增大dpr的倍数,这样当缩小dpr倍的时候,那么字体也就和设计稿所示的大小一样大了,在不同的手机中显示的大小也是一致的。
从flexible.js的代码中可以知道,flexible布局仅仅只是针对iPhone进行适配,而默认所有的安卓设备都强制性设置dpr为1。
于是,因为这个缘故,很多小伙伴可能就会产生这样的问题:为什么安卓不用retina屏幕,安卓下面是不是就不会有模糊的问题?
其实不然,模糊的本质是因为dpr,而安卓手机不同的设备的dpr也是不尽相同的。也就是说,安卓手机下也存在模糊的情况。只不过它的屏幕不叫retina屏幕,没有这个叫法,所以很多小伙伴都误认为安卓手机没有这个毛病。
那么问题又来了?既然也有模糊的毛病,那么为什么安卓手机不进行适配呢?
问题就在这里了,有兴趣的小伙伴可以去看一下大中华的安卓手机,dpr参数五花八门,从1到4,连1.75、2.75这种奇葩的数字也有,所以个人觉得权衡之下,直接简单“粗暴”把安卓手机全部设置为1,是效率和收益更高的做法。
当然,也有人进行了flexible.js的改进,就是对dpr比较正常的安卓手机进行适配,也就是说只适配dpr为整数的安卓设备。对于那些奇葩的dpr为1.75的设备直接忽略。实现这个并不难,有兴趣的小伙伴们可以试下。
最后,对于响应式和自适应的区别,网上有各种各样的解释。
个人认为,其实没必要把它讲得那么复杂,知乎上有个小伙伴讲我觉得就很白话文:
响应式针对的是不同分辨率设备而进行的适配式设计,以利用@media规则为主要手段,而自适应则忽略@media以比例布局为主,目的是适应不同的浏览器窗口大小。
于是我们会发现,现今大型网站,例如说淘宝网,已经没有做响应式了。什么意思呢?
我们会发现,淘宝网手机端和网页端使用的是两个域名,也就是说,不同的客户端已经不再共用一套dom结构了。而是区分开来做自适应。然后每次用户访问的时候它就根据客户端的类型重定向。
为什么呢?
试想一下淘宝这种大型网站,一个分页下的商品条目特别多,并且每个商品条目的dom结构又十分复杂,而且pc端往往显示的信息是要比手机端更多的。如果不分开做两套,而是直接用响应式的话,那么pc端上显示的很多dom就要在手机端上隐藏,结果这些dom都没有被用到,但是却加载了。在这个流量和速度至上的时代,代码冗余先不说,多加载的这些无用的代码而消耗的流量,从某种意义上来说就已经损失了很多的效益。
以上,就是本文的全部啦。