一、首先,要搞明白移动端适配问题,就要先搞明白像素和视口
像素包含2种像素:物理像素和css像素
物理像素又称设备像素,任何设备的物理像素的数量是固定不变的,单位是pt。所谓的一倍屏、二倍屏(Retina)、三倍屏,指的是设备以多少物理像素来显示一个CSS像素。
CSS像素就是我们写CSS时所用的像素,是一个抽像的单位,在不同的设备或者不同的环境中,css中的1px所代表的设备物理像素是不同的。比如早期的iphone3的分辨率是 320px*480px
,1css像素=1物理像;iphoen4开始分辨率提高成了640px*960px
,但屏幕尺寸没变,意味着同样大小的屏幕上,像素多了一倍,此时1css像素 = 2物理像素.
设备像素比简称为dpr,其定义了物理像素和设备独立像素的对应关系,前提条件是在缩放程度为100%
dpr = 设备像素 / CSS像素
可以通过JS得到: window.devicePixelRatio
以iphone6为例:iphone6的设备宽和高为375pt * 667pt
,即为设备的物理像素,而其设备像素比为2.固css像素为750pt * 1334pt
关于viewpoint的基本概念,可以参考文章
viewpoint严格等于浏览器的窗口;viewpoint不是一个html的概念,不能通过css修改它
视觉视口
就是用户看到的网页的区域,window.innerWidth/Height 返回视觉视口的尺寸
image.png
布局适口
布局视口不是指设备屏幕区域,它是浏览器厂商定的视口,为了解决PC端网站在移动端展示不佳的一个解决方案,通常比设备屏幕宽得多,一般为980px,但也不是唯一,在不同的浏览器中也会有所不同如:在Safari iPhone中布局视窗的宽为980px,在Opera中布局视窗宽为850px。
可以通过document.documentElement.clientWidth /Height
来获取
你有可能实验过,然后说“你说的不对啊”,不要着急,你的页面可能添加了meta viewport,并且 设置了width = device-width,如果那样通过上面的代码所获得的值就不是布局视窗的默认值了。
image.png
理想视口
布局视口明显对用户是不友好的,完全忽略了手机本身的尺寸。所以苹果引入了理想视口的概念,把布局视窗调整到合适的状态,让页面有最好的表面效果。设置了理想视窗用户就不再需要对页面进行缩放,因为浏览器已经帮你把页面调整到最佳的显示状态了。而你要做的就是告诉浏览器,你要他这么做就OK了。用代码实现就是
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
PS: device-width
是设备宽度,initial-scale
是缩放比例, user-scalable
是是否允许用户缩放
你明白了吗?赶紧动动手试试吧。
移动端适配
1、什么是适配?
由于手机型号多,把设计图实现在各个手机上的过程就是适配
2、怎么做?
我这里说4种方案:
固定高度,宽度自适应
固定宽度,viewport缩放
rem做宽度,viewport缩放
使用vw
方案一:固定高度,宽度自适应
是目前使用最多的方法,垂直方向用定值,水平方向用百分比、定值、flex都行。随着屏幕宽度变化,页面也会跟着变化,效果就和PC页面的流式布局差不多
这种方法使用了理想视口:
<meta name="viewport" content="width=device-width,initial-scale=1">
这样设置之后,我们就可以不用管手机屏幕的尺寸进行开发了。
方案二 :固定宽度,viewport缩放
荔枝的代码:
if(/Android (\d+\.\d+)/.test(navigator.userAgent)){ var version = parseFloat(RegExp.$1); if(version>2.3){ var phoneScale = parseInt(window.screen.width)/640; if(/MZ-M571C/.test(navigator.userAgent)){ document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">'); }else if(/M571C/.test(navigator.userAgent)&&/LizhiFM/.test(navigator.userAgent)){ document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">'); }else{ document.write('<meta name="viewport" content="width=640, minimum-scale = '+ phoneScale +', maximum-scale = '+ phoneScale +', target- densitydpi=device-dpi">'); } }else{ document.write('<meta name="viewport" content="width=640, target-densitydpi=device-dpi">'); } }else{ document.write('<meta name="viewport" content="width=640, user-scalable=no, target-densitydpi=device-dpi">'); }
固定布局视口,宽度设置固定的值,总宽度为640px,根据屏幕宽度动态生成viewport。(假设设计稿是640px的)
这种方式布局如荔枝FM的网页宽度始终为640px。缩放比例scale为:var scale = window.screen.width / 640
方案三:rem做宽度,viewport缩放
这也是淘宝使用的方案,根据屏幕宽度设定 rem 值,需要适配的元素都使用 rem 为单位,不需要适配的元素还是使用 px 为单位。
具体使用方法见使用Flexible实现手淘H5页面的终端适配
实际上做了这几件事情:
动态生成 viewport
屏幕宽度设置 rem的大小,即给<html>设置font-size
根据设备像素比(window.devicePixelRatio)给<html>设置data-dpr
设置 viewport 缩放 和 data-dpr
这两个设置其实是干的一件事,就是适配高密度屏幕手机的px单位。
根据设置的dpr设置font-size
document.documentElement.style.fontSize = 50 * dpr;// dpr 为设置的设备像素比。(注意不是设备自身的设备像素比,而是认为设置的dpr)
当设计以iphone6为标准,出750px的设计稿时,此时dpr=2,故1rem 等于100px,将图上的尺寸转换为rem非常方便,除以100就行。代码如下:
var scale = 1.0;var dpr = 1;var isAndroid = window.navigator.appVersion.match(/android/gi);var isIPhone = window.navigator.appVersion.match(/iphone/gi);var devicePixelRatio = window.devicePixelRatio;// 此处只简单对ios做了伸缩处理,安卓没有做伸缩处理,统一dpr = 1if ( isIPhone ) { scale /= devicePixelRatio; dpr *= devicePixelRatio; }var viewport = document.getElementById('viewport');var content = 'initial-scale=' + scale + ', maximum-scale=' + scale + ',minimum-scale=' + scale + ', width=device-width, user-scalable=no'; viewport.setAttribute( 'content', content );document.documentElement.style.fontSize = 50 * dpr + 'px';document.documentElement.setAttribute('data-dpr', dpr);
对于该方案,假设肉眼看到的宽度(视觉宽度):visualWidth,令dpr=1时,其1rem对应的宽度为50.
dpr = 1 时, 1rem = 50px, initial-scale=1, 缩放为1。
visualWidth = 50 * 1 = 50;dpr = 2 时, 1rem = 100px, initial-scale=0.5, 缩放为0.5。
visualWidth = 100 * 0.5 = 50;dpr = 3 时, 1rem = 150px, initial-scale=0.3333, 缩放为0.3333。
visualWidth = 150 * 0.3333 = 50
也许你会有疑问,这种先设置成倍数尺寸,又缩放是不是多此一举,反正我当时看的时候就比较懵逼;
继续研究了下发现这是为了解决 retina下,图片高清问题
我先具体描述下这个问题:
理论上,1个位图像素对应于1个物理像素,图片才能得到完美清晰的展示。
在普通屏幕下是没有问题的,但是在retina屏幕下就会出现位图像素点不够,从而导致图片模糊的情况。
用一张图来表示:
image.jpeg
如上图:对于dpr=2的retina屏幕而言,1个位图像素对应于4个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊(注意上述的几个颜色值)。
所以,对于图片高清问题,比较好的方案就是两倍图片(@2x)。
如:200×300(css pixel)img标签,就需要提供400×600的图片。
如此一来,位图像素点个数就是原来的4倍,在retina屏幕下,位图像素点个数就可以跟物理像素点个数形成 1 : 1的比例,图片自然就清晰了(这也解释了之前留下的一个问题,为啥视觉稿的画布大小要×2?)。
所以这个问题的解决方案就是:两倍图片(@2x),然后图片容器缩小50%。
方案4:使用vw
目前,vw已经得到了众多浏览器的支持,所以可以直接考虑将vw单位运用于适配布局中,这也是我们项目目前在使用的方案
原理:假设设计稿为750px,那么100vw = 750px, 1vw = 7.5px,那么就可以根据px直接转换成vw了,为了计算方便,可以使用PostCss的插件postcss-px-to-viewport,这样可以直接在代码里写px,比如:
div{ width: 30px; }
编译之后就是我们需要的带vw的代码
div{ width: 4vw; }
使用的时候,可以对该插件进行参数配置:
{ viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750 viewportHeight: 667, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置 unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除) viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用 vw selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名 minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值 mediaQuery: false // 允许在媒体查询中转换`px`著作权归作者所有。}
在不想把px转换成vw的时候,在元素中添加类名 .ignore 或者 .hairlines (.hairlines 一般用于设置 0.5px border)
Retina下1px的问题
可以使用postCss插件postcss-write-svg
@svg 1px-border{ height: 2px; @rect{ fill: var(—-color, black); width: 100%; height: 50%; } }.example{ border: 1px solid transparent; border-image: svg(1px-border param(—color #00b1ff)) 2 2 stretch; }
编译出来之后
.example{ border: 1px solid transparent; border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") ; }
也可以使用border-image的方式
@svg square{ @rect{ fill: var(--color, black); width: 100%; height: 100%; } }
附注:
名称 | 方法 | 度量 | 说明 | 示例 | 移动端 |
---|---|---|---|---|---|
屏幕尺寸 screen size | screen.width / height | 设备pixels | 用户屏幕的完整大小,不重要 | image.jpeg | |
浏览器尺寸 window size | Window.innerWidth / innerheight | css pixels[当zoom in放大的时候,数值变小] | 包含滚动条尺寸的浏览器完整尺寸 | image.png | image.jpeg |
滚动位移 scrolling offset | window.pageXOffset / pageYOffset | css pixels[当zoom in放大的时候,数值不变] | 页面相对于窗口原点的水平/垂直位移 | image.jpeg | |
视口viewpoint | window.documentElement.clientWidth / cilentHeight | css pixels | 视窗内的css pixels | image.jpeg | image.jpeg |
<html> 尺寸 | window.documentElement.offsetWidth / offsetHeight | css pixels | html元素的尺寸 | image.jpeg | |
Event.pageX / Y[使用较多] | CSS pixels | 从<html>原点到事件触发点的距离 | image.jpeg | ||
Event.clientX / Y | CSS pixels | 从viewport原点(浏览器窗口)到事件触发点的距离 | image.jpeg | ||
Event.screenX / Y | CSS pixels | 从用户显示器窗口原点到事件触发点的距离 | image.jpeg |
作者:Wendy曹
链接:https://www.jianshu.com/p/aa60a9100bf1