前言
在移动端虽然整体来说大部分浏览器内核都是 webkit,而且大部分都支持 css3 的所有语法。但是,由于手机屏幕尺寸不一样,分辨率不一样,或者你需要考虑横竖屏的问题,或者考虑到各式各样的移动端兼容性问题。这时候你也就不得不解决在不同手机上,不同情况下的展示效果,所以就需要一个开箱即用并且行之有效的移动端适配方案。
响应式设计 - 像素
设备像素
设备像素(device pixels)也叫物理像素,指的是显示器上的真实像素,每个像素的大小是屏幕固有的属性,屏幕出厂以后就不会再改变。
CSS 像素
在 CSS 中使用的 px 都是指 css 像素,比如 width: 128px。css 像素的大小是很容易变化的,当我们缩放页面的时候,元素的 css 像素数量不会改变,改变的只是每个 css 像素的大小。也就是说 width: 128px 的元素在缩放200% 以后,宽度依然是 128 个 css 像素,只不过每个 css 像素的宽度和高度变为原来的两倍。如果原本元素宽度为 128 个设备独立像素,那么缩放 200% 以后元素宽度为 256 个设备独立像素。
css 像素与设备像素的关系
window.devicePixelRatio 设备像素比,devicePixelRatio = (在相同长度的直线上)设备像素的数量 / CSS 像素的数量。这个比例也等价于 CSS 像素边长/设备像素边长。如 devicePixelRatio = 2,表示在相同长度的直线上,设备像素的数量是 CSS 像素数量的 2 倍,因此 CSS 像素的边长是设备像素的 2 倍。缩放会导致 CSS 像素边长的改变,从而导致 window.devicePixelRatio 的改变!
利用 meta 标签对 viewport 进行控制
该 meta 标签的作用是让当前 viewport 的宽度等于设备的宽度,同时不允许用户手动缩放。如果你不这样的设定的话,那就会使用那个比屏幕宽的默认 viewport(layout viewport),也就是说会出现横向滚动条。
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
使用动态设置html font-size的方式 来适配
rem是相对于HTML的根元素,em相对于自身元素的字体大小。VW,VH 屏幕宽度高度的高分比
//按照宽度375图算, 1rem = 1px;
(function (win, doc) {
function changeSize() {
doc.documentElement.style.fontSize = doc.documentElement.clientWidth / 375 + 'px';
}
changeSize();
win.addEventListener('resize', changeSize, false);
})(window, document);
可以看到,设置了font-size这个属性,然后在其它地方就可以用rem这个单位了,
方案选择
1、使用 ss 的媒体查询 @media
基于 css 的媒体查询属性 @media 分别为不同屏幕尺寸的移动设备编写不同尺寸的 css 属性,示例如下所示。虽然此方法能在一定程度上解决移动设备适配的问题,但我们也可以看出其存在以下问题,所以其已几乎被历史潮流淘汰。页面上所有的元素都得在不同的 @media 中定义一遍不同的尺寸,这个代价有点高;
结合使用 rem 单位
rem(font size of the root element)是指相对于根元素的字体大小的单位,如果我们设置 html 的 font-size 为 16px,则如果需要设置元素字体大小为 16px,则写为 1rem。但是其还是必须得借助 @media 属性来为不同大小的设备设置不同的 font-size,相对上一种方案,可以减少重复编写相同属性的代价,简单示例如下所示。
我们也能看到该方案存在以下问题:
- 不同的尺寸需要写多个 @media;
- 所有涉及到使用 rem 的地方,全部都需要调用方法 calc() ,这个也挺麻烦的;
2、viewport 适配方案
由于 viewport 单位得到众多浏览器的兼容,所以目前基于 viewport 的移动端适配方案被各大厂团队所采用。
vw 作为布局单位,从底层根本上解决了不同尺寸屏幕的适配问题,因为每个屏幕的百分比是固定的、可预测、可控制的。 viewport 相关概念如下:
.logo {
font-size: 10vw; // 1vw = 750px * 1% = 7.5px
}
px 自动转换为 vw
设计师一般给宽度大小为 375px 或 750px 的视觉稿,我们采用 vw 方案的话,需要将对应的元素大小单位 px 转换为 vw 单位,这是一项影响开发效率(需要手动计算将 px 转换为 vw)且不利于后续代码维护(css 代码中一堆 vw 单位,不如 px 看的直观)的事情;好在社区提供了 postcss-px-to-viewport 插件,来将 px 自动转换为 vw,相关配置步骤如下:
npm install postcss-px-to-viewport --save-dev
官网是使用 glup 进行配置,但是我们项目模版中是使用 webpack 进行 postcss 插件以及相关样式插件的配置,所以我们就使用 webpack 进行配置使用,不需要额外引入 gulp 编译;webpack 相关配置如下,且每个属性表示的意义进行了备注:
module.exports = {
plugins: {
// ...
'postcss-px-to-viewport': {
// options
unitToConvert: 'px', // 需要转换的单位,默认为"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, // 横屏时使用的视窗宽度
},
},
};
标注不需要转换的属性
在项目中,如果设计师要求某一场景不做自适配,需为固定的宽高或大小,这时我们就需要利用 postcss-px-to-viewport 插件的 Ignoring 特性,对不需要转换的 css 属性进行标注,示例如下所示:
/* px-to-viewport-ignore-next / —> 下一行不进行转换.
/ px-to-viewport-ignore */ —> 当前行不进行转换
/* example input: */
.class {
/* px-to-viewport-ignore-next */
width: 10px;
padding: 10px;
height: 10px; /* px-to-viewport-ignore */
}
/* example output: */
.class {
width: 10px;
padding: 3.125vw;
height: 10px;
}
3、使用动态设置html font-size的方式 来适配
rem是相对于HTML的根元素,em相对于自身元素的字体大小。VW,VH 屏幕宽度高度的高分比
//按照宽度375图算, 1rem = 1px;
(function (win, doc) {
function changeSize() {
doc.documentElement.style.fontSize = doc.documentElement.clientWidth / 375 + 'px';
}
changeSize();
win.addEventListener('resize', changeSize, false);
})(window, document);