引自 https://juejin.cn/post/7009081081760579591#heading-27 感谢!!!
前端大屏可视化项目适配方案
- 1. 全局适配
- 1.1 css scale 适配方案
- 1.2 vw+vh适配方案(有些细节处采用媒体查询处理)
- 1.2.1 按照设计稿的尺寸,将`px`按比例计算转为`vw`和`vh`
- 1.2.2 借助scss函数实现计算
- 1.2.3 动态DOM元素适配
- 2. echats及第三方组件适配(配置型)
// * 默认缩放值
const scale = {
width: '1',
height: '1',
}
// * 设计稿尺寸(px)-- 需跟ui确认
const baseWidth = 1920
const baseHeight = 1080
// const baseWidth = document.body.clientWidth
// const baseHeight = document.body.clientHeight
// * 需保持的比例(默认1.77778)
const baseProportion = parseFloat((baseWidth / baseHeight).toFixed(5))
export default {
data() {
return {
// * 定时函数
drawTiming: null
}
},
mounted () {
this.calcRate()
window.addEventListener('resize', this.resize)
},
beforeDestroy () {
window.removeEventListener('resize', this.resize)
},
methods: {
calcRate () {
const appRef = this.$refs["appRef"]
// const geoMap = document.getElementById('geomap-container')
if (!appRef) return
// 当前宽高比
const currentRate = parseFloat((window.innerWidth / window.innerHeight).toFixed(5))
if (appRef) {
if (currentRate > baseProportion) {
// 表示更宽
scale.width = ((window.innerHeight * baseProportion) / baseWidth).toFixed(5)
scale.height = (window.innerHeight / baseHeight).toFixed(5)
appRef.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`
// geoMap.style.transform = `scale(${-scale.width}, ${-scale.height}) translate(-50%, -50%)`
} else {
// 表示更高
scale.height = ((window.innerWidth / baseProportion) / baseHeight).toFixed(5)
scale.width = (window.innerWidth / baseWidth).toFixed(5)
appRef.style.transform = `scale(${scale.width}, ${scale.height}) translate(-50%, -50%)`
// geoMap.style.transform = `scale(${-scale.width}, ${-scale.height}) translate(-50%, -50%)`
}
}
},
resize () {
clearTimeout(this.drawTiming)
this.drawTiming = setTimeout(() => {
this.calcRate()
}, 200)
}
},
}
px
按比例计算转为vw
和vh
即:
网页宽度=1920px
网页高度=1080px
我们都知道
网页宽度=100vw
网页宽度=100vh
所以,在1920x*1080px的屏幕分辨率下
1920px = 100vw
1080px = 100vh
这样一来,以一个宽300px和200px的div来说,其作所占的宽高,以vw和vh为单位,计算方式如下:
vwDiv = (300px / 1920px ) * 100vw
vhDiv = (200px / 1080px ) * 100vh
所以,就在1920*1080的屏幕分辨率下,计算出了单个div的宽高
当屏幕放大或者缩小时,div还是以vw和vh作为宽高的,就会自动适应不同分辨率的屏幕
npm install [email protected] [email protected] --save
在项目静态资源文件夹下新建一个`utils.scss`文件,定义好设计稿的宽度和高度两个变量;
使用scss内置的`math.div`函数,定义两个`vw`和`vh`的计算函数;
传入具体的像素值,其帮我们自动计算出vw和vh的值;
utils.scss 文件
//使用scss的math函数,https://sass-lang.com/documentation/breaking-changes/slash-div
@use "sass:math";
//默认设计稿的宽度
$designWidth:1920;
//默认设计稿的高度
$designHeight:1080;
//px转为vw的函数
@function vw($px) {
@return math.div($px , $designWidth) * 100vw; // math.div无效时可尝试用下面这句
// @return calc( $px/$designWidth) * 100vw;
}
//px转为vh的函数
@function vh($px) {
@return math.div($px , $designHeight) * 100vh; // math.div无效时可尝试用下面这句
// @return calc($px/$designHeight )* 100vw;
}
const path = require('path')
const resolve = dir => {
return path.join(__dirname, dir)
}
module.exports = {
publicPath: './',
chainWebpack: config => {
config.resolve.alias
.set('_c', resolve('src/components')) // key,value自行定义,比如.set('@@', resolve('src/components'))
},
configureWebpack: {
externals: {
'AMap': 'AMap' // 高德地图配置
}
},
css:{
//全局配置utils.scss,详细配置参考vue-cli官网
loaderOptions:{
sass:{
prependData:`@import "@/assets/scss/utils.scss";` // 路径根据个人实际情况做修改
}
}
}
}
有的时候可能不仅在.vue
文件中使用,比如在js中动态创建的DOM元素,它可能是直接渲染到html中的
let oDiv = document.createElement('div')
document.body.appendChild(oDiv)
这样的话,此处用了以下两种处理方式,来给创建的div设置样式
scr/styles
目录下新建一个global.scss
文件,在main.js中引入 // global.scss文件
.global-div{
width: vw(300);
height: vw(200);
background-color: green;
}
// 在main.js中引入
import './styles/global.scss' // 路径根据个人需求更改
// 使用时给div配置className
let oDiv = document.createElement('div')
oDiv.className = "global-div"
src/utils
目录下新建一个styleUtil.js
文件,内容如下://定义设计稿的宽高
const designWidth = 1920;
const designHeight = 1080;
let styleUtil = {
// px转vw
px2vw: function (_px) {
return _px * 100.0 / designWidth + 'vw';
},
// px转vh
px2vh: function (_px) {
return _px * 100.0 / designHeight + 'vh';
},
};
export default styleUtil;
使用时,单独设置宽高等属性
import styleUtil from "./src/utils/styleUtil.js" // 注意使用个人路径
let oDiv = document.createElement('div')
oDiv.style.width = styleUtil.px2vw(300)
oDiv.style.height = styleUtil.px2vh(200)
oDiv.style.margin = styleUtil.px2vh(20)
不过这种使用方式有种弊端,就是屏幕尺寸发生变化后,需要手动刷新一下才能完成自适应调整
src/utils
目录下新建一个resizeEchart.js
文件,内容如下:/* Echarts图表字体、间距自适应 */
export const fitChartSize = (size,defalteWidth = 1920) => { // 默认宽高问设计
let clientWidth = window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth;
if (!clientWidth) return size;
let scale = (clientWidth / defalteWidth);
return Number((size*scale).toFixed(3));
}
// 在main.js中引入
import {fitChartSize} from './utils/resizeEchart.js'
Vue.prototype.fitChartSize = fitChartSize;
// 实际使用
axisLabel: {
textStyle: {
color: '#4CC8F8',
fontSize: _this.fitChartSize(14),
verticalAlign: "bottom",
align: "right",
padding: [0,_this.fitChartSize(30),_this.fitChartSize(9),0]
}
}
// 此方法也适用于其他通过配置项配置的组件库 eg: dataV
patrolData: [
{
id: 1,
name: '巡查里程',
number: {
number: [0],
textAlign: 'center',
toFixed: 2,
content: '{nt} 公里',
style: {
fontSize: this.fitChartSize(26),
fontWeight: 'bold',
fill: '#e5b048',
fontFamily: 'D-DIN'
}
}
}
]
element-resize-detector
这是一个用于监听DOM元素尺寸变化的插件。我们已经对窗口缩放做了监听,但是有时候其父级容器的大小也会动态改变的。 我们对父级容器的宽度进行监听,当父级容器的尺寸发生变化时,echart能调用自身的resize方法,保持视图正常。
当然,这个不适用于tab选项卡的情况,在tab选项卡中,父级容器从display:none到有实际的clientWidth,可能会比注册一个resizeDetector先完成,所以等开始监听父级容器resize的时候,可能为时已晚。
安装 element-resize-detector
npm install element-resize-detector
使用
// 在你的chart组件中引入
import ResizeListener from "element-resize-detector";
// 此处我在chart初始化时调用了该方法 -- 调用时机根据个人需求而定
this.addChartResizeListener(myChart)
window.addEventListener('resize',function() {
myChart.resize()
})
},
// 对chart元素尺寸进行监听,当发生变化时同步更新echart视图
addChartResizeListener (myChart) {
const instance=ResizeListener({
strategy: "scroll",
callOnAdd: true,
});
instance.listenTo(this.$el,() => {
if(!myChart) return;
myChart.resize();
});
}