在日常开发中我们总是会遇到各种不一样的分辨率屏幕,为此我们需要使用各种方案去对页面进行前端适配,下面是我对目前所存在的几种方案的展示及总结。
基本单位
在了解我们常用的适配方案之前,首先我们得充分了解我们日常所使用的CSS基本单位,这里我们把日常所用的CSS基本单位分为绝对单位和相对单位,下面是对这些单位的介绍。
绝对单位
-
px(pixel):像素,是屏幕上显示数据的最基本的点,表示相对大小。不同分辨率下相同长度的px元素显示会不一样,比如同样是14px大小的字,在1366✖768显示屏下会显示的小,在1024×768尺寸的显示器下会相对大点。也称为物理像素(设备像素),是分辨率的尺寸单位。
-
pt (point):磅,是一个物理长度单位,指的是 72 分之一英寸。pt=1/72(英寸) 。
相对单位
- em:相对父元素的font-size计算值的倍数 。
- rem :相对长度单位,相对于根元素font-size计算值的倍数 。
- vw(viewport width) :视窗宽度,1vw = 视窗宽度的1% 。
- vh(viewport height) :视窗高度,1vh = 视窗高度的1% 。
- %:根据父元素的指定对应百分比
媒体查询适配方案
媒体查询是我们常用的一个适配方案,它提供给开发者针对不同设备需求进行定制化开发的一个接口。可以让我们根据设备的类型(比如:屏幕设备、打印机设备)或者特定的特性(比如屏幕的宽度)来修改页面。
/* 默认样式 */
body {
background-color: white;
color: black;
}
/* 在宽度小于 1000px 时应用的样式 */
@media screen and (max-width: 999px) {
body {
background-color: #f8f8f8;
color: white;
}
}
/* 在宽度小于 500px 时应用的样式 */
@media screen and (max-width: 499px) {
body {
background-color: #f5f5f5;
color: white;
}
}
/* @media 设置多个媒体特性需要加逻辑运算符来使用 */
@media (min-width: 500px) and (max-width: 800px) {
body {
background-color: orange;
}
}
通过媒体查询,我们可以非常方便的给不同分辨率的设备编写不同的样式来实现响应式的布局。但是媒体查询的缺点也很明显,如果在浏览器大小改变时,需要改变的样式太多,那么多套样式代码会很繁琐,且也无法保证完全兼容所有的屏幕和无法支持 echarts 图表中的参数进行适配。
rem的适配方案
rem是指相对于根元素html的字体大小的单位。如果html的font-size是16px,那么1rem = 16px。所以基于rem的适配方案,是根据获取到的设备宽度,设置html的font-size,这个值一般设置为设计稿宽度的1/10。下面是目前使用rem适配方案的实现方式。
1、安装依赖
npm install postcss-pxtorem autoprefixer amfe-flexible --save-dev
postcss-pxtorem 是PostCSS的插件,用于将像素单元生成rem单位
autoprefixer 浏览器前缀处理插件
amfe-flexible 可伸缩布局方案 替代了原先的lib-flexible 选用了当前众多浏览器兼容的
1rem = viewWidth / 10
2、项目根目录创建 postcss.config.js 文件
module.exports = {
plugins: {
autoprefixer: {
overrideBrowserslist: [
"Android 4.1",
"iOS 7.1",
"Chrome > 31",
"ff > 31",
"ie >= 8",
"last 10 versions", // 所有主流浏览器最近10版本用
],
grid: true,
},
"postcss-pxtorem": {
rootValue: 192, // 设计稿宽度的1/ 10
propList: ["*", "!border"], // 除 border 外所有px 转 rem
selectorBlackList: [".el-"], // 过滤掉.el-开头的class
},
},
};
3、main.ts/js 文件中导入依赖
import "amfe-flexible/index.js";
这种方式的适配方案有点在于布局的自适应代码量少,适配简单。但缺点也比较明显比如不适用于所有浏览器,然后使用iframe引用也会出现问题,还有rem自身的因素,比如在处理小数的时候会有误差,对小屏幕的时候就会比较不友好。
基于vw、vh的适配方案
vw和vh是根据视窗宽高的百分比来控制样式,我们可以通过控制视窗宽度来进行控制。下面是目前使用rem适配方案的实现方式。
1、安装依赖
npm install postcss-px-to-viewport --save-dev
2、在vite.config.ts中
import pxtovw from "postcss-px-to-viewport"
export default defineConfig({
css: {
postcss: {
plugins: [
autoprefixer({
overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8']
}),
pxtovw({
unitToConvert: 'px', // 要转化的单位
viewportWidth: 750, //100vw=750px,UI设计稿的宽度
unitPrecision: 6, // 转换后的精度,即小数点位数
propList: ['*'], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
viewportUnit: 'vw', // 指定需要转换成的视窗单位,默认vw
fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位,默认vw
selectorBlackList: ['ignore-'], // 指定不转换为视窗单位的类名,
minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
replace: true, // 是否转换后直接更换属性值
landscape: false, // 是否处理横屏情况
})
],
},
},
})
基于vw和vh的适配方案从优点上来说它可以动态计算图表的宽高,字体等,灵活性较高,当屏幕比例跟 ui 稿不一致时,不会出现两边留白情况。但它的缺点也是出现在使用图表的问题上,每个图表都需要单独做字体、间距、位移的适配,比较麻烦,当然如果不适用相关图表的话他是一个不错的解决方案。
基于scale的适配方案
在CSS3中,我们可以使用transform属性的scale()方法来实现元素的缩放效果。所以基于transform我们可以进行适配。下面是目前VUE3中实现这种方式的实现方案。
<template>
<div class="scale-box">
<slot></slot>
</div>
</template>
<script setup>
import { ref, onMounted, defineProps } from 'vue'
const width = ref(null), //设计宽度
height = ref(null), //设计高度
scale = ref(null) //缩放比例
const props = defineProps({
width: {
type: String,
default: '1920px',
},
height: {
type: String,
default: '1080px',
},
})
//初始化
const init = () => {
setScale()
window.addEventListener('resize', setScale)
}
//缩放比例
const setScale = () => {
let ww = window.innerWidth / props.width
let wh = window.innerHeight / props.height
scale.value = ww < wh ? ww : wh
}
init()
</script>
<style scoped>
.scale-box {
width: v-bind('props.width');
height: v-bind('props.height');
transform: scale(v-bind(scale)) translate(-50%, -50%);
transform-origin: 0 0;
position: absolute;
left: 50%;
top: 50%;
transition: 0.3s;
}
</style>
scale()这种适配方案优点在于代码量少,适配简单,一次处理后不需要在各个图表中再去单独适配。但是他的缺点也比较明显,因为它是通过缩放的形式进行适配,如果开发中根据 ui 稿等比缩放,当大屏跟 ui 稿的比例不一样时,会出现周边留白情况,同时因为是缩放的缘故,当缩放比例过大时候,字体会有一点点模糊,事件热区会偏移。
scale这种适配方式目前也有已经封装好的npm包,下面是对应使用方式。
1、安装依赖
npm install vue3-scale-box --save-dev
2、使用
vue文件中
<template>
<scale-box
:width="1920"
:height="1080"
bgc="transparent"
:delay="100"
:isFlat="true"
>
<router-view />
</scale-box>
</template>
<script setup>
import { onMounted, onBeforeUnmount } from "vue";
import ScaleBox from "vue3-scale-box";
import { disableScale } from "@/utils/disableScale";
onMounted(() => {
disableScale.addListener();
});
onBeforeUnmount(() => {
disableScale.removeListener();
});
</script>
disableScale.js
//滚轮禁止缩放页面
const disableScale = (function () {
function mousewheel(e) {
e = e || window.event;
if ((e.wheelDelta && event.ctrlKey) || e.detail) {
event.preventDefault();
}
}
function keydown(event) {
if (
(event.ctrlKey === true || event.metaKey === true) &&
(event.keyCode === 61 ||
event.keyCode === 107 ||
event.keyCode === 173 ||
event.keyCode === 109 ||
event.keyCode === 187 ||
event.keyCode === 189 ||
event.keyCode === 48)
) {
event.preventDefault();
}
}
const addListener = function () {
document.addEventListener("mousewheel", mousewheel, {
capture: false,
passive: false,
});
document.addEventListener("keydown", keydown, false);
};
const removeListener = function () {
document.removeEventListener("mousewheel", mousewheel, false);
document.removeEventListener("keydown", keydown, false);
};
return {
addListener,
removeListener,
};
})();
export { disableScale };
当然还有其他的适配方法例如v-scale-screen、flex弹性布局等,当然任何一种方案都有对应的优缺点,我们可以根据项目中的情况,分别配置对应的实现方式。