移动端rem的适配
px: 像素,它是一个固定大小的单元,像素的计算是针对屏幕的,一个像素(1px)就是屏幕的一个点,但是因为它是固定大小的,所以不能够自适应;
em: 相对长度单位,它是用来设置文本的字体尺寸,相对于当前对象内的文本的字体尺寸;一般浏览器默认(1em=16px);
em是相对于父级元素的单位,会随父级元素的属性(font-size或其他属性)变化而变化;
rem: (css3新增的相对长度单位),相对于根目录,即HTML元素,所以只要在HTML标签上设置字体大小,文档中的字体大小都会以此为参照标准,一般用于自适应布局;
rem是相对于根目录(HTML)的,所以它会随HTML的元素的属性(font-size)变化而变化
了解了这三个单位的基本概念;
在移动端开发的时候一般为了适应不同屏幕的大小,我们需要使用自适应布局;
1.首先第一步便是文档根元素HTMLfont-size的设置
浏览器一般默认字号为16px,我们可以通过设置<html>的font-size来设置我们的参考值;
看一下他们的转换关系:
| px | rem |
|---|---|
| 12 | 12/16 = 0.75 |
| 14 | 14/16 = 0.875 |
| 16 | 16/16 = 1 |
| ... |
了解了根元素font-size的换算关系,我们就可以为<html>设置font-size,从而自适应布局了
html {
font-size: 100% // 相当于16px
}
div {
width: 3rem // 相当于3*16px = 48px
}
2.在编辑器vscode下载插件px2rem
了解了rem的换算关系后,如果我们每次都要根据换算关系来计算rem的值,也是很烦躁的,所以可以借助插件,类似的插件有很多,我这里用的是px2rem的这个插件,直接在vscode的插件库下载安装就好;
然后配置一下:
图上的root font size即你要配的html的font-size的大小,默认是16px,可根据设计图和项目自行配置,我这里配置的是37.5;
Fixed Digits是对于rem如果除不尽的话,取得小数,默认是六位,我这里取得是小数点后四位; 其它的配置也可自行搜索配置;
这样我们在写css样式的时候就会自动提示rem的对应值了;
这里推荐一篇文章:关于移动端适配:
3.全局js文件,可参照插件lib-flexible
通过这种方式,我们写css的时候局可以直接写px,该文件会在渲染的时候自动给我们转换成对应的rem,我们直接写px就可以了;
文件自行在网上查找;
4.安装插件 lib-flexible
lib-flexuble插件是移动端适配的一种解决方案,是淘宝项目组开发出来的,属于开源项目,可以在各类项目中引用;
lib-flexuble它会自动将html中的font-size设置为屏幕大小的十分之一,即假设说屏幕大小为750px,那么html的font-size为75px,即1rem=75px,那么我们要设置的元素的宽度如果为150px,就可以设置为2rem;
以下是在react项目中的使用:
一般来讲,lib-flexible会和px2rem-loader插件一起使用,目的是将css中的px转换为rem;
4.1 安装lib-flexible
npm install lib-flexible --save-dev
4.2 在项目src目录下的index.js中引入
import 'lib-flexible';
4.3 安装px2rem-loader
关于该插件的配置可查看这篇文章:# lib-flexible适配大屏方案(附移动端适配)
npm install px2rem-loader --save-dev
4.4 分析一下lib-flexible
lib-flexible插件的源码一共100来行,我们一起分析分析吧!
;(function(win, lib) {
var doc = win.document; // 返回文档对象
var docEl = doc.documentElement; // 文档对象的根元素html
var metaEl = doc.querySelector('meta[name="viewport"]'); // 寻找第一个name为viewport的meta标签
var flexibleEl = doc.querySelector('meta[name="flexible"]');// 寻找第一个name为flexible的meta标签
var dpr = 0; // 客户端设备的像素比(与每个CSS像素相对应的物理设备像素的数量)
var scale = 0; // 缩放比例
var tid;
var flexible = lib.flexible || (lib.flexible = {}); // 在初次加载的时候是个空对象,会一次给它设置我们适配的各种值,具体如下
// 这一块主要是计算屏幕的初始比例和缩放比例
if (metaEl) {
console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
} else if (flexibleEl) {
var content = flexibleEl.getAttribute('content');
if (content) {
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
if (initialDpr) {
dpr = parseFloat(initialDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
}
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);// 返回运行当前代码的应用程序的相关信息
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio; // 返回当前显示设备的物理分辨率与css像素分辨率之比。
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
docEl.setAttribute('data-dpr', dpr);
// 如果没有设置视口的meta标签,就根据我们计算的值设置
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}
// 根据屏幕的宽度,设置rem
function refreshRem(){
var width = docEl.getBoundingClientRect().width;
// 这一块会有个小坑,大多数状况是都可以适配的,但是dpr的值一般都是1,当大屏的时候,布局就会全部乱掉,所以如果出现这种状况,可以根据需求修改这块的代码;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
// 监听屏幕大小的变化,随时调整根节点
win.addEventListener('resize', function() {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
// 监听页面显示的事件
win.addEventListener('pageshow', function(e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
// 判断文档对象的加载状态,当文档和所有子资源已完成加载时
// 重置body的字体大小,以防继承自父元素
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener('DOMContentLoaded', function(e) {
doc.body.style.fontSize = 12 * dpr + 'px';
}, false);
}
refreshRem();
flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
// 将rem转换为px
flexible.rem2px = function(d) {
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}
// 将px转换为rem
flexible.px2rem = function(d) {
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
}
})(window, window['lib'] || (window['lib'] = {}));