- 通过
flexiable.js
实现动态计算根元素html
的fontSize
值,以保证实际设备视口尺寸(与meta[name="viewport"]标签密切相)
永远等于10rem
。 - 通过获取当前设备像素比dpr后,
flexiable.js
动态设置meta[name="viewport"]
标签,根据dpr进行scale值的设置(scale值直接会影响视口的实际尺寸,即设备尺寸/scale ===视口的实际尺寸 )
场景如下:
视觉稿:750*1334(2倍)
适配引入: flexiable.js + px2rem-loader
div尺寸:600px
iphone6/7/8 375*667 [window.devicePixelRatio === 2] =>
dpr === 2
=>
scale === 1/dpr === 1/2 === 0.5
=>
页面插入 <meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
=>
根元素fontSize === (实际设备尺寸/viewport[scale] === 375/0.5 === 750) / 10 === 75px
=>
600px
经过px2rem-loader 处理,600 /75 === 8rem
。其中 75 为按照实际视觉稿的尺寸计算的1rem对应的值,即 750/10
即此时div尺寸为8rem,永远是相对与根元素的尺寸进行等比例缩放,从而实现自适应尺寸布局。 此时8rem实际的尺寸为 875 === 600px 此时设备宽度为375px,但是此时meta[name="viewport"] scale 为 0.5,故实际600px对应的设备宽度仅为300px(600px0.5)。自适应完成✅
iphone6/7/8 Plus 414*736 [window.devicePixelRatio === 3] =>
dpr === 3
=>
scale === 1/dpr === 1/3 === 0.3333333333
=>
页面插入 <meta name="viewport" content="initial-scale=0.3333333333, maximum-scale=0.3333333333, minimum-scale=0.3333333333, user-scalable=no">
=>
根元素fontSize === (实际设备尺寸/viewport[scale] === 414/0.3333333333 === 1242) / 10 === 124.2px
=>
600px
经过px2rem-loader 处理,600 /75 === 8rem
。其中 75 为按照实际视觉稿的尺寸计算的1rem对应的值,即 750/10
即此时div尺寸为8rem,永远是相对与根元素的尺寸进行等比例缩放,从而实现自适应尺寸布局。即此时8rem实际的尺寸为 8*124.2 === 993.6px 此时设备宽度为414px,但是此时meta[name="viewport"] scale 为 0.3333333333,故实际600px对应的设备宽度仅为331.2px(993.6px*0.3333333333)
。自适应完成
✅
综上所述:设计稿的600px iphone6/7/8 375*667 dpr = 2
实际看到的为 300px
iphone6/7/8 Plus 414*736 dpr = 3
实际看到的为 331.2px
实际根据rem与根元素的比例关系进行适配
// [flexiable.js](<https://github.com/posuihushui/flexible.js>)
//创建meta标签,设置初始比、最小比和最大比均为scale值,并将其插入到页面
//设置了缩放比,那么相当于这个屏幕渲染在一个被放大的画布之上。
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);
}
}
// 自定义页面元素的fontsize,方便rem的配置
function refreshRem(){
var docEl = window.document.documentElement;
// 重点:通过getBoundingClientRect()获取的是实际渲染的画布尺寸;(与meta[name="viewport"]标签密切相关)
// 即:如果设置initial-scale为0.5,deviceWidth(iphone6s)为375,则width = 375 / 0.5 = 750
// 即:如果设置initial-scale为0.3,deviceWidth(iphone6s)为375,则width = 375 / 0.3 = 1250
var width = docEl.getBoundingClientRect().width;
// 限制最大fontSize为64px
if (width / dpr > 640) {
width = 640 * dpr;
}
// 永远保证:实际设备视口宽度(与meta[name="viewport"]标签密切相关) === 10rem
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
// 监听[resize|pageshow]触发更新设置根元素fontSize值
window.addEventListener('resize', function() {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
window.addEventListener('pageshow', function(e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
// 通过使用react-app-rewired和customize-cra对默认webpack自定义添加px2rem-loader配置
// 参考源码 [customize-cra/src/customizers/webpack.js](<https://github.com/arackaf/customize-cra/blob/master/src/customizers/webpack.js>)
// 参考配置 [px2rem-loader配置](<https://github.com/Jinjiang/px2rem-loader>)
const { override } = require("customize-cra");
const addPx2remLoader = (options) => (config) => {
// 获取config所有loaders配置
const loaders = config.module.rules.find((rule) =>
Array.isArray(rule.oneOf)
).oneOf;
// 获取针对/\.css$/文件的loader配置
const cssLoader = loaders.find((rule) => {
return rule.test && rule.test.toString() === "/\\.css$/";
});
// 追加px2rem-loader预处理loader
cssLoader.use = [
...cssLoader.use,
{
loader: "px2rem-loader",
// 采用参数传递px2rem-loader相关配置项
options,
},
];
return config;
};
module.exports = override(
addPx2remLoader({
remUnit: 75,
remPrecision: 8,
})
);
下面分别附上flexiable.js和px2rem的源码地址(1、3): GitHub - posuihushui/flexible.js: 手机淘宝flexible.js解读
webpack配置相关:customize-cra/webpack.js at master · arackaf/customize-cra
GitHub - Jinjiang/px2rem-loader: Webpack loader for px2rem css file