方案一: vw vh
配置vw vh转换函数
src/styles/common.scss
- scss写法
@use "sass:math";
$vw_base: 1920;
$vh_base: 1080;
/* 计算vw */
@function vw($px) {
@return math.div($px, $vw_base) * 100vw;
// @return ($px / $vw_base) * 100vw;
}
/* 计算vh */
@function vh($px) {
@return math.div($px, $vh_base) * 100vh;
// @return ($px / $vh_base) * 100vh;
}
- less写法
// 默认设计稿的宽度
@vw_base: 1920;
// 默认设计稿的高度
@vh_base: 1080;
.px2vw(@name, @px) {
@{name}: (@px / @vw_base) * 100vw;
}
.px2vh(@name, @px) {
@{name}: (@px / @vh_base) * 100vh;
}
.px2font(@px) {
font-size: (@px / @vw_base) * 100vw;
}
vite 配置
在vite.config.js配置中引入全局css文件
export default defineConfig({
css: {
// css预处理器
preprocessorOptions: {
scss: {
additionalData:
'@import "@/assets/styles/common.scss";'
},
},
},
})
使用
scss
.box{
width:vw(500);
}
less
.box{
.px2vw(width, 500);
}
方案二: scale
这里 #app的内容进行缩放, 项目中的vh、vw单位要进行特殊处理(步骤2), 另外#app之外的元素如弹窗也要单独写样式(步骤3)
1. 缩放方法
import { ref, onBeforeUnmount, onMounted } from "vue";
import useSettingsStore from '@/store/modules/settings' // 屏幕设置参数
/**
* 屏幕缩放
*/
export const useScreenScale = () => {
// * 定时函数
const timer = ref();
const settingsStore = useSettingsStore()
onMounted(() => {
handleScreenScale();
window.addEventListener("resize", resize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", resize);
});
/**
* 缩放防抖
*/
const resize = () => {
clearTimeout(timer.value);
timer.value = setTimeout(() => {
handleScreenScale();
}, 200);
};
/**
* 处理屏幕缩放
*/
function handleScreenScale(designWidth = 1920, designHeight = 929) {
const clientHeight = document.documentElement.clientHeight;
const clientWidth = document.documentElement.clientWidth;
const style = document.querySelector('#app').style
let scale = clientWidth < 600
? 1 : (clientWidth / clientHeight) < (designWidth / designHeight)
? (clientWidth / designWidth)
: (clientHeight / designHeight);
scale = Math.abs(1 - scale) > 0.1 ? scale.toFixed(2) : 1;
scale = scale * settingsStore.screenSize; // screenSize几倍屏幕
settingsStore.screenScale = scale; // 存储缩放值
document.documentElement.style.setProperty(`--screenScale`,scale); // 存css变量到html
const height = Math.round(clientHeight / scale);
const width = Math.round(clientWidth / scale);
style.height = `${height}px`;
style.width = `${width}px`;
style.transform = `scale(${scale})`;
style.transformOrigin = `0 0`;
document.body.style.overflow = `hidden`;
}
};
2.写vite插件, 转化vh、vw单位
把所有文件中的vh、vw单位除以缩放比例var(--screenScale), 动态变更尺寸 /plugins/vw-vh-transform.ts
/**
* vite插件, 把vw,vh除以缩放比例
*/
export default function vwVhTransform() {
return {
name: 'vw-vh-transform',
transform(code, id) {
const fileTypes = ['vue', 'js', 'ts', 'jsx', 'tsx', 'css', 'scss', 'less']
// if (id.endsWith('.vue')) {
if (fileTypes.includes(id.split('.').pop())) {
// 匹配类似于 100vh的样式
code = code.replace(/(?<![a-zA-Z])(\d*\.?\d+)vh(?!\s*\/\s*var\(--screenScale\))/g, 'calc($1vh / var(--screenScale))')
code = code.replace(/(?<![a-zA-Z])(\d*\.?\d+)vw(?!\s*\/\s*var\(--screenScale\))/g, 'calc($1vw / var(--screenScale))')
// 匹配类似于 calc(80vh - 110px) 的样式
code = code.replace(/calc\((\d*\.?\d+)vh - (\d*\.?\d+)px\)/g, 'calc($1vh / var(--screenScale) - $2px)')
code = code.replace(/calc\((\d*\.?\d+)vw - (\d*\.?\d+)px\)/g, 'calc($1vw / var(--screenScale) - $2px)')
}
return { code, map: null }
}
}
}
vite.config.ts使用
import vwVhTransform from './plugins/vw-vh-transform'
export default ({ mode, command }) => defineConfig({
plugins: [
vwVhTransform()
]
})
3.app.vue使用
这里把 #app 之外的下拉菜单的样式做动态修改, 用v-bind失效, 所以用 var变量
利用zoom 缩放element-plus的弹窗
tooltip要特殊处理
#app 内的弹窗已经随app整体缩放过就不要缩放了, 设置zoom 为 1
el-drawer 不能设置append-to-body, 否则里面的弹窗位置会不对
<template>
<router-view />
</template>
<script setup>
import { useScreenScale } from '@/utils/scale'
useScreenScale()
</script>
<style lang="scss">
.el-popper[role^=tooltip]:not(.is-pure):not(.el-popover) {
padding: calc(var(--screenScale) * 5px) calc(var(--screenScale) * 11px) !important;
font-size: calc(var(--screenScale) * 12px) !important;
line-height: calc(var(--screenScale) * 20px) !important;
min-width: calc(var(--screenScale) * 10px) !important;
}
.el-popover {
transform: scale(var(--screenScale));
transform-origin: 80% 0%;
}
.el-overlay-dialog {
overflow: hidden !important;
.el-dialog {
transform: scale(var(--screenScale));
width: calc(var(--el-dialog-width) / var(--screenScale)) !important;
max-height: 100%;
.el-dialog__body {
max-height: calc(80vh / var(--screenScale));
overflow-y: auto;
}
}
}
.el-notification,
.el-alert,
.el-loading-mask,
.el-message,
.el-message-box,
.el-drawer,
.el-menu--popup{
zoom: var(--screenScale);
}
.el-popper:not(.el-popover) {
.el-select-dropdown,
.el-dropdown-menu,
.el-popper__arrow,
.el-picker-panel {
zoom: var(--screenScale);
}
}
#app {
.el-popover {
transform: scale(1);
}
.el-dialog {
transform: scale(1);
width: var(--el-dialog-width) !important;
}
.el-notification,
.el-alert,
.el-loading-mask,
.el-message,
.el-message-box,
.el-drawer {
zoom: 1;
}
.el-popper:not(.el-popover) {
.el-select-dropdown,
.el-dropdown-menu,
.el-popper__arrow,
.el-picker-panel,
.el-menu--popup {
zoom: 1;
}
}
}
</style>
方案三: rem
方式一 安装依赖
npm i postcss-pxtorem autoprefixer amfe-flexible --save-dev
postcss-pxtorem 是PostCSS的插件,用于将像素单元生成rem单位
autoprefixer 浏览器前缀处理插件
amfe-flexible 动态设置根元素字体大小, 替代了原先的lib-flexible
选用了当前众多浏览器兼容的viewport
创建 /postcss.config.js 配置文件, 配置完成后,页面内的 px 就会被转换成 rem
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 例如设计稿按照 1920设计 此处就为192
propList: ["*", "!border"], // 除 border 外所有px 转 rem
selectorBlackList: [".el-"], // 过滤掉.el-开头的class,不进行rem转换
},
},
};
main.js 引入依赖 import "amfe-flexible/index.js";
方式二
安装vscode插件 px to rem, 设置搜索px, 设置根节点大小, 输入px会自动转换rem
方式三
- 设置根节点大小 /style/reset.css
html {
/* rem 相对于根元素 html 的fonts-size */
/* 浏览器默认 16px * 62.5% = 10px, 1rem = 10px */
font-size: 62.5%;
}
- 使用rem单位
div {
font-size: 1.4rem;
}
方式二、三动态设置根元素fontSize大小
/utils/rem.js
(function flexible (window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
或者 rem.js
// 基准大小
const baseSize = 16
// 设置 rem 函数
function setRem () {
// 当前页面宽度相对于 1920 宽的缩放比例,可根据自己需要修改。
const scale = document.documentElement.clientWidth / 1920
// 设置页面根节点字体大小, 字体大小最小为12
let fontSize = (baseSize * Math.min(scale, 2))>12 ? (baseSize * Math.min(scale, 2)): 12
document.documentElement.style.fontSize = fontSize + 'px'
}
//初始化
setRem()
//改变窗口大小时重新设置 rem,这里最好加上节流
window.onresize = function () {
setRem()
}
main.js 引入 import './utils/rem.js';