一、基础概念
1. 英寸
一般用英寸描述屏幕的物理大小,如电脑显示器的17、22,手机显示器的4.8、5.7等使用的单位都是英寸。需要注意,上面的尺寸都是屏幕对角线的长度。
英寸和厘米的换算:1英寸 = 2.54 厘米
2. 分辨率
-
2.1 像素
像素是指由图像的小方格组成的,这些小方块都有一个明确的位置和被分配的色彩数值,小方格颜色和位置就决定该图像所呈现出来的样子。(ps上图像放大之后可以看到的像素点)
-
2.2 屏幕分辨率
屏幕分辨率指一个屏幕具体由多少个像素点组成,当然分辨率高不代表屏幕就清晰,屏幕的清晰程度还与尺寸有关。
-
2.3 图像分辨率
我们通常说的图片分辨率其实是指图片含有的像素数,比如一张图片的分辨率为800 x 400。这表示图片分别在垂直和水平上所具有的像素点数为800和400。
同一尺寸的图片,分辨率越高,图片越清晰。
3. 设备独立像素
上面描述的都是物理像素,是设备上真实的物理单元。随着设备分辨率越来越高,同样大小的图片和文字,使用物理像素现实的话将会越来越小
乔布斯首次提出了Retina Display(视网膜屏幕)的概念,不管分辨率多高,他们所展示的界面比例都是基本类似的。
在iPhone4使用
的视网膜屏幕中,把2x2个像素当1个像素使用,这样让屏幕看起来更精致,但是元素的大小却不会改变
如果黑色手机使用了视网膜屏幕的技术,那么显示结果应该是下面的情况,比如列表的宽度为300个像素,那么在一条水平线上,白色手机会用300个物理像素去渲染它,而黑色手机实际上会用600个物理像素去渲染它。
我们必须用一种单位来同时告诉不同分辨率的手机,它们在界面上显示元素的大小是多少,这个单位就是设备独立像素(Device Independent Pixels)简称DIP或DP。上面我们说,列表的宽度为300个像素,实际上我们可以说:列表的宽度为300个设备独立像素。
打开chrome的开发者工具,我们可以模拟各个手机型号的显示情况,每种型号上面会显示一个尺寸,比如iPhone X显示的尺寸是375x812,实际iPhone X的分辨率会比这高很多,这里显示的就是设备独立像素。
- 3.1 设备像素比
设备像素比device pixel ratio简称dpr,即物理像素和设备独立像素的比值。
4. 视口
视口(viewport)代表当前可见的计算机图形区域。在Web浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的UI, 菜单栏等——即指你正在浏览的文档的那一部分。
一般我们所说的视口共包括三种:布局视口、视觉视口和理想视口,它们在屏幕适配中起着非常重要的作用。
-
4.1 布局视口
布局视口(layout viewport):当我们以百分比来指定一个元素的大小时,它的计算值是由这个元素的包含块计算而来的。当这个元素是最顶级的元素时,它就是基于布局视口来计算的。
所以,布局视口是网页布局的基准窗口,在PC浏览器上,布局视口就等于当前浏览器的窗口大小(不包括borders 、margins、滚动条)。
在移动端,布局视口被赋予一个默认值,大部分为980px,这保证PC的网页可以在手机浏览器上呈现,但是非常小,用户可以手动对网页进行放大。
我们可以通过调用document.documentElement.clientWidth / clientHeight来获取布局视口大小。
-
4.2 视觉视口
视觉视口(visual viewport):用户通过屏幕真实看到的区域。
视觉视口默认等于当前浏览器的窗口大小(包括滚动条宽度)。
当用户对浏览器进行缩放时,不会改变布局视口的大小,所以页面布局是不变的,但是缩放会改变视觉视口的大小。
例如:用户将浏览器窗口放大了200%,这时浏览器窗口中的CSS像素会随着视觉视口的放大而放大,这时一个CSS像素会跨越更多的物理像素。
所以,布局视口会限制你的CSS布局而视觉视口决定用户具体能看到什么。
我们可以通过调用window.innerWidth / innerHeight来获取视觉视口大小。
-
4.3 理想视口
布局视口在移动端展示的效果并不是一个理想的效果,所以理想视口(ideal viewport)就诞生了:网站页面在移动端展示的理想大小。
如上图,我们在描述设备独立像素时曾使用过这张图,在浏览器调试移动端时页面上给定的像素大小就是理想视口大小,它的单位正是设备独立像素。
上面在介绍CSS像素时曾经提到页面的缩放系数 = CSS像素 / 设备独立像素,实际上说页面的缩放系数 = 理想视口宽度 / 视觉视口宽度更为准确。
所以,当页面缩放比例为100%时,CSS像素 = 设备独立像素,理想视口 = 视觉视口。
我们可以通过调用screen.width / height来获取理想视口大小 -
4.4 meta viewport
meta元素表示那些不能由其它HTML元相关元素之一表示的任何元数据信息,它可以告诉浏览器如何解析页面。
我们可以借助<meta>元素的viewport来帮助我们设置视口、缩放等,从而让移动端得到更好的展示效果。
<meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">
二、移动端适配方案
1. flexible方案
flexible方案是阿里早期开源的一个移动端适配解决方案,引用flexible后,我们在页面上统一使用rem来布局。
它的核心代码非常简单:
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit();
rem 是相对于html节点的font-size来做计算的。
我们通过设置document.documentElement.style.fontSize就可以统一整个页面的布局标准。
上面的代码中,将html节点的font-size设置为页面clientWidth(布局视口)的1/10,即1rem就等于页面布局视口的1/10,这就意味着我们后面使用的rem都是按照页面比例来计算的。
这时,我们只需要将UI出的图转换为rem即可。
以iPhone6为例:布局视口为375px,则1rem = 37.5px,这时UI给定一个元素的宽为75px(设备独立像素),我们只需要将它设置为75 / 37.5 = 2rem。
当然,每个布局都要计算非常繁琐,我们可以借助PostCSS的px2rem插件来帮助我们完成这个过程。
下面的代码可以保证在页面大小变化时,布局可以自适应,当触发了window的resize和pageShow事件之后自动调整html的fontSize大小。
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
由于viewport单位得到众多浏览器的兼容,上面这种方案现在已经被官方弃用:
lib-flexible这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方案。
2. vw、vh方案
vh、vw方案即将视觉视口宽度 window.innerWidth和视觉视口高度 window.innerHeight 等分为 100 份。
- vw(Viewport's width):1vw等于视觉视口的1%
- vh(Viewport's height) :1vh 为视觉视口高度的1%
- vmin : vw 和 vh 中的较小值
-
vmax : 选取 vw 和 vh 中的较大值
如果视觉视口为375px,那么1vw = 3.75px,这时UI给定一个元素的宽为75px(设备独立像素),我们只需要将它设置为75 / 3.75 = 20vw
这里的比例关系我们也不用自己换算,我们可以使用PostCSS的 postcss-px-to-viewport 插件帮我们完成这个过程。写代码时,我们只需要根据UI给的设计图写px单位即可。
当然,没有一种方案是十全十美的,vw同样有一定的缺陷:
- px转换成vw不一定能完全整除,因此有一定的像素差。
- 比如当容器使用vw,margin采用px时,很容易造成整体宽度超过100vw,从而影响布局效果。当然我们也是可以避免的,例如使用padding代替margin,结合calc()函数使用等等...
以上内容参考:https://juejin.im/post/5cddf289f265da038f77696c
vue-cli 3.0+vw、vh实践
-
创建项目
vue create demo
-
引入postcss-px-to-viewport
npm install postcss-loader postcss-px-to-viewport --save-dev
将下面代码放在package.json里,然后重启服务即可
"postcss": {
"plugins": {
"autoprefixer": {},
"postcss-px-to-viewport": {
"viewportWidth": 750,
"viewportHeight": 1334,
"unitPrecision": 5,
"viewportUnit": "vw",
"selectorBlackList": [
".ignore"
],
"minPixelValue": 1,
"mediaQuery": false
}
}
}