vite+vue3的h5适配方案
- 安装lib-flexible和postcss-px2rem
npm i lib-flexible -D
pnpm install postcss-px2rem -D
- 在main.ts文件中引入lib-flexible
- 在vite.config.js配置文件中配置postcss-px2rem
js
import px2rem from 'postcss-px2rem'
css: {
postcss: {
plugins: [
px2rem({
remUnit: 37.5 // 1rem === 37.5px
})
]
}
}
postcss-px2rem介绍
This is a postcss plugin of px2rem.
postcss
是一个用javascript工具和插件转换css代码的工具。
lib-flexible计算根结点html的fontSize的源码
js
;(function(win, lib) {
var doc = win.document;
var docEl = doc.documentElement;
var metaEl = doc.querySelector('meta[name="viewport"]');
var flexibleEl = doc.querySelector('meta[name="flexible"]');
var dpr = 0;
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;
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);
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);
}
}
function refreshRem() {
var width = docEl.getBoundingClientRect().width; //获取布局视口的宽度
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10; // 将布局视口分成10等分
docEl.style.fontSize = rem + "px"; //将根节点html上的fontSize设置为布局视口的10分之一
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);
}
基础知识
像素
- 设备的逻辑像素(屏幕分辨率)
- 设备的物理像素
- 设备像素比 = 设备的物理像素宽度 / 设备的逻辑像素宽度, 通过
window.devicePixelRatio
获取
视口
-
可视视口(
visual viewport
):视觉视口是屏幕上实际可见的内容 -
布局视口(
layout viewport
):布局视图覆盖页面上的所有元素一般移动设备的浏览器都默认设置了一个布局视口,用于解决早期的PC端页面在手机上显示的问题。IOS、Android基本都是将这个视口分辨率设置为980px,所以PC上的网页大多能在手机上呈现。
-
理想视口(
ideal viewport
) 移动端默认按照980px的宽度布局; 如果所有的网页都按照980px在移动端布局,那么最终页面都会被缩放显示。 事实上这种方式不利于我们进行移动开发局部,我们希望的是设置100px,那么显示的就是100px; 如何做到这一点呢,通过设置理想视口(ideal viewport
)。
移动web包含两个视口,即布局视口和视觉视口。布局视口覆盖页面上的所有元素,视觉视口是屏幕上实际可见的内容。当用户捏缩页面时,视觉视口会缩小,但布局视口不变。屏幕键盘(OSK)等用户界面功能可以缩小视觉视口,而不会影响布局视口。 参考:blog.csdn.net/m0_51636525...
window.screen.width
:返回的是屏幕宽度window.innerWidth
:返回窗口的布局视口宽度window.visualViewport.width
:返回窗口的可视视口宽度document.documentElement.clientWidth
和document.documentElement.getBoundingClientRect().width
:获取的是布局视口宽度
viewport元标签
- viewport的width设置的是布局视口的宽度。
- 移动设备,大部分设备的默认布局视口宽度是980px
- width=device-width 设置布局视口和可视视口大小相同
- viewport的initial-scale属性:设置页面首次加载时显示的缩放倍数。
案例展示
-
案例1: 下图是未设置viewport元标签时的默认展示: 此时,布局视口的宽度默认为980px,缩放比例是0.37。所以,默认情况下会缩小页面,展示布局视口下的所有内容。当内部元素div的宽度大于布局视口的宽度, 此时的多余的div部分没有展示。
-
案例2: 以下是设置viewport的width=device-width和initial-scale=1.0对应的展示图:
js
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<div style="width: 1000px;background-color: red;overflow:auto;">浏览器默认fontSize大小</div>
rem
rem(font size of the root elememt)是CSS3新增的一个相对单位,是指相对于根元素的字体大小的单位。
uniapp计算根结点html的fontSize的圆满
node_modules/@dcloudio/uni-h5/dist/uni-h5.es.js
js
function getWindowWidth$1() {
const screenFix = /^Apple/.test(navigator.vendor) && typeof window.orientation === "number";
const landscape = screenFix && Math.abs(window.orientation) === 90;
var screenWidth = screenFix ? Math[landscape ? "max" : "min"](screen.width, screen.height) : screen.width;
var windowWidth = Math.min(
window.innerWidth,
document.documentElement.clientWidth,
screenWidth
) || screenWidth;
return windowWidth;
}
function useRem() {
const config = __uniConfig.globalStyle || {};
const maxWidth2 = checkValue$1(config.rpxCalcMaxDeviceWidth, 960);
const baseWidth2 = checkValue$1(config.rpxCalcBaseDeviceWidth, 375);
function updateRem() {
let width = getWindowWidth$1();
width = width <= maxWidth2 ? width : baseWidth2;
document.documentElement.style.fontSize = width / 23.4375 + "px";
}
updateRem();
document.addEventListener("DOMContentLoaded", updateRem);
window.addEventListener("load", updateRem);
window.addEventListener("resize", updateRem);
}
代码中 23.4375 是从 iphone6 的屏宽除以默认字号计算得来:375 / 16
。也就是说以 iphone6 的屏幕宽度,可以放下 23.4375 个默认文字。