文章目录
前置概念
先让我们来搞清楚几个概念,就不长篇大论了,简单直接的说明白
视口概念
- 视觉视口(visual viewport): 代表可视区域的大小,就是浏览器app里显示页面的区域大小(包括滚动条),可用
window.innerWidth
查看; - 布局视口(layout viewport): 开发者根据平台开发时的区域大小(开发画布大小),用
document.documentElement.clientWidth
查看; - 理想视口(ideal viewport):就是当视觉视口等于布局视口的状态;
关于像素
- 物理像素(设备像素):手机像素分辨率中的像素;
- 设备独立像素:就是写代码开发时候的画布像素;
- 设备像素比(dpr):等于物理像素 / 设备独立像素,可通过js来获取
window.devicePixelRatio
;
必要工作:设置理想视口(必须的)
打开F12,当我们用默认的视口去开发移动端,就会发现会有滚动条出现:
这是因为此时默认的布局视口为PC端的视口,当切换到移动端布局模式时,默认的整体画布框度为980px,那么此时布局视口就非常大了,当此时的视觉视口就和手机屏幕一样大,所以会出现滚动条。需要我们手动强制进入理想视口,也就是保证布局视口等于视觉视口,可以通过以下标签设置:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
这是苹果的safari的规范,里面的属性为:
属性 | 解释 |
---|---|
width | 设置 layout viewport 的宽度,为一个正整数,或字符串 “width-device” |
height | 设置页面的初始缩放值,为一个数字,可以带小数 |
initial-scale | 允许用户的最小缩放值,为一个数字,可以带小数 |
minimum-scale | 允许用户的最大缩放值,为一个数字,可以带小数 |
maximum-scale | 设置 layout viewport 的高度,这个属性对我们并不重要,很少使用 |
user-scalable | 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes 代表允许 |
由此可以读出这个meta标签的作用就是把画布的宽度等于设备的宽度,缩放值固定为1,也不允许用户缩放,就是固定死了(很有苹果的风格hhh)。
方案集
@media修改动态rem(不推荐)
通过媒体查询符@media
,来修改不同设备大小下的rem。下面偷来别人写好的哈哈。
@media screen and (min-width: 320px) {html{font-size:50px;}}
@media screen and (min-width: 360px) {html{font-size:56.25px;}}
@media screen and (min-width: 375px) {html{font-size:58.59375px;}}
@media screen and (min-width: 400px) {html{font-size:62.5px;}}
@media screen and (min-width: 414px) {html{font-size:64.6875px;}}
@media screen and (min-width: 440px) {html{font-size:68.75px;}}
@media screen and (min-width: 480px) {html{font-size:75px;}}
@media screen and (min-width: 520px) {html{font-size:81.25px;}}
@media screen and (min-width: 560px) {html{font-size:87.5px;}}
@media screen and (min-width: 600px) {html{font-size:93.75px;}}
@media screen and (min-width: 640px) {html{font-size:100px;}}
@media screen and (min-width: 680px) {html{font-size:106.25px;}}
@media screen and (min-width: 720px) {html{font-size:112.5px;}}
@media screen and (min-width: 760px) {html{font-size:118.75px;}}
@media screen and (min-width: 800px) {html{font-size:125px;}}
@media screen and (min-width: 960px) {html{font-size:150px;}}
这种方式在设置font-size数值的时候,会以一个值为基准,例如100px,然后根据宽度的比值,去算出不同宽度的font-size,就有了上面这么多的情况。这样的好处就是不同屏幕大小的手机屏幕上看到的界面比例都是一样的。
我感觉如果设计稿是宽度为640px-680px之间的还好,刚好1rem=100px,开发时好做换算,但如果不是就很麻烦了。我会pass掉这个方案。
第三方手淘flexible(不推荐)
通过lib-flexible
这个第三方插件实现rem的自适应。
npm install lib-flexible –save
main.js中引入。
import 'lib-flexible';
把meta标签注释掉,flexible会自动生成合适的meta标签,调整布局视口。同时会自动设置html的font-size为屏幕宽度除以10,也就是1rem等于html根节点的font-size。假如设计稿的宽度是750px,此时1rem等于75px。
但有个问题,设计稿给的单位是px,每次写css单位都要自己算出是多少rem,所以不推荐,这玩意也很老了。
JS控制rem(不推荐)
这个方法是从《前端解决移动端适配的五种方法》这篇博客看来的。
var designWidth = 375; // 手动设置设计稿宽度
var remPx = 100; // 设置根元素字体大小100px,可以改成其他的
var scale = window.innerWidth / designWidth; //计算当前屏幕的宽度与设计稿比例
// 根据屏幕宽度 动态计算根元素的 字体大小
document.documentElement.style.fontSize = scale*remPx + 'px';
此时,当设计稿的宽度为375px的时候,1rem=100px
;
这个方法不知道能不能用在一个工程中,我平时就用来快速写demo用吧。不推荐在工程中使用。
postcss-px-to-viewport使用自动转换vh/vw(非常推荐)
先看什么是vh/vw单位?
首先要知道window.innerHeight = 100vh
,宽度同理。那么 1vh就是网页视口高度的百分之一,宽度同理。如下例子:
.div {
width: 10vw;
height: 10vh;
}
在手机上,就会呈现一个高度为屏幕高度十分之一,宽度为屏幕宽度十分之一的矩形盒子。
什么是vmax/vmin单位?
设置vmax时,会对比网页视口高度与宽度,选一个最大的最为百分比标准,例如手机的高比宽大,那么就只以网页视口高度为百分比。
.div {
width: 10vamx;
height: 10vamx;
}
在手机上,就会呈现一个高度为屏幕高度十分之一,宽度为屏幕高度十分之一的正方形盒子。
同理设置vmin时,在手机上,就会呈现一个高度为屏幕宽度十分之一,宽度为屏幕宽度十分之一的正方形盒子。
ok,说一个理想的情况。如果我们以UI设计稿的尺寸作为开发画布的尺寸,然后用户手机屏幕再进行等比放大缩小就好。例如设计稿上的一个盒子的宽度是100px,写css的时候转换成vw单位,这样到其他手机上看是等比的。
但我们每次设置单位的时候都要自己换算太麻烦了,这里推荐一个库postcss-px-to-viewport
,它能帮我们把写的px单位自动转换成vw或者vh单位。
安装:
npm install postcss-px-to-viewport --save-dev
webpack在postcss插件里配置:
postcss: {
plugins: [
require("postcss-px-to-viewport")({
unitToConvert: 'pxs', // 需要转换的单位,默认为"px"
viewportWidth: 750, // 设计稿的视窗宽度
unitPrecision: 5, // 单位转换后保留的精度,应该是小数位
propList: ['*', '!font-size'], // 能转化为 vw 的属性列表
viewportUnit: 'vw', // 希望使用的视窗单位
fontViewportUnit: 'vw', // 字体使用的视窗单位
selectorBlackList: [], // 需要忽略的 CSS 选择器,不会转为视窗单位,使用原有的 px 等单位
minPixelValue: 1, // 设置最小的转换数值,如果为 1 的话,只有大于 1 的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: undefined, // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
include: /\/src\//, // 如果设置了include,那将只有匹配到的文件才会被转换
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1125, // 横屏时使用的视窗宽度
})
]
}
或者:
module.exports = {
plugins: {
// ...
'postcss-px-to-viewport': {
// ...
},
},
};
然后就可以直接以你设定的画布大小去写像素单位了,会自动给你转成vw或者vh:
.box{
width: 20pxs;
}
使用vue的可以看看这个:vue移动端/pc端适配插件:postcss-px-to-viewport
关于加入这个库的时机问题
如果你是项目已经开发很久了才加入这个库的,建议配置里unitToConvert: 'px'
,这样之前都是写的px单位就直接生效了,然后看看实机展示是否ok。如果有大面积的很多问题,可以先设置成其他单位例如pxs,这样之前设置的px就不会转换,然后以后再慢慢去调整。
如果是项目刚搭建的,直接unitToConvert: 'px'
就好,然后如果有不想转换的可以这样写:
.box{
width: 20px; /* px-to-viewport-ignore */
}
内联样式不生效
是的,再内联样式中的单位是不会被转换的,想办法用class去改写吧。
一像素问题
之前看网上大家把这个问题说的很难懂,对新手不是很友好,今天就来简单的讲明白什么是一像素问题。
解释
一般情况下,UI同事做出来的设计稿宽度是iPhone6标准的750px:
然而我们的开发界面有设备像素比,所以同样是IPhone6机型却是375px:
这种情况下,如果我们写盒子的边框为1px时,在电脑上看确实是1px,但是真正用手机去看时,就会因为设备像素比而放大两倍变成2px。
我截个图放大给你们看:
上面的123边框css设置的1px,手机看是2px,对比下面的123边框(是真正1px的边框宽度),很明显不同吧!
有人说那我就主动除以2,设置0.5px,不就行了。哈哈,我来告诉你会有什么问题:
明显比下面正常的1px还细一些吧,而且并不是所有设备的设备像素比都是2的,还有3或其他,都除以2显然不合适。
解决方法-很抱歉
目前我只知道怎么处理设备像素比是2的情况,用transform: scale()
结合伪元素实现:
.border-left,
.border-right,
.border-top,
.border-bottom
{
position: relative;
}
.border-left::after {
position: absolute;
top: 0;
left: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 200%;
height: 200%;
border-left: 1px solid red;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: left top;
transform-origin: left top;
content: '';
}
.border-right::after {
position: absolute;
top: 0;
left: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 200%;
height: 200%;
border-right: 1px solid red;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: left top;
transform-origin: left top;
content: '';
}
.border-top::after {
position: absolute;
top: 0;
left: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 200%;
height: 200%;
border-top: 1px solid red;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: left top;
transform-origin: left top;
content: '';
}
.border-bottom::after {
position: absolute;
top: 0;
left: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 200%;
height: 200%;
border-bottom: 1px solid red;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: left top;
transform-origin: left top;
content: '';
}
需要哪边的边框就给盒子加上对应的class名,很抱歉没发现最好的解决方案。
但是!其实这一像素的问题真的没有这么重要,很多时候除非UI设计师,用户很难看得出来的,没必要去纠结这玩意。