echarts根据上级元素的大小自动更新echarts(element-resize-detector)
问题产生的原因:
在项目中有时需要因为其他样式的改变而被动改变echarts图例的大小,比如在我们管理系统中左侧的菜单栏的收缩,会导致右侧内容区的宽度变大,所以我们对应的echarts也要发生位置的变化:
我这里将所有echarts元素都始于一个chart组件,组价的价值主要在于生成一个dom,接受父级传入的echarts的options,并创建echarts图例:
代码:
<!--
* @Descripttion:
* @version:
* @Author: ZhangJunQing
* @Date: 2022-06-08 16:01:36
* @LastEditors: ZhangJunQing
* @LastEditTime: 2022-08-10 17:38:41
-->
<template>
<div class="chart" ref="chartDom"></div>
</template>
<script>
export default {
props: {
option: {
type: Object,
required: true,
default: {},
},
},
data() {
return {
echartsInstance: null,
};
},
methods: {
init() {
if (!this.echartsInstance) {
let charts = this.$refs.chartDom;
this.echartsInstance = this.$echarts.init(charts);
}
// this.echartsInstance.showLoading({
// text: '暂无数据',
// showSpinner: false,
// textColor: '#646464',
// maskColor: 'rgba(255, 255, 255, 1)',
// fontSize: '16px',
// fontWeight: 'bold',
// })
this.echartsInstance.setOption(this.option);
// this.echartsInstance.hideLoading()
},
},
watch: {
option: {
handler(newOption) {
this.echartsInstance.clear();
this.echartsInstance.resize();
this.echartsInstance.setOption(newOption);
},
deep: true
},
},
mounted() {
this.init();
},
};
</script>
<style lang="less" scoped>
.chart {
// width: 300px;
// height: 300px;
width: 100%;
height: 100%;
}
</style>
但是有个问题,即便我们设置window自带的resize方法,但是我们改变左侧菜单的收缩,并不会出发resize方法,因为并没有改变当前页面的视图大小,于是使用了一个第三方插件,简直简直简直好用。
element-resize-detector
npm i element-resize-detector
引入:
import elementResizeDetectorMaker from "element-resize-detector"
使用:
let myChart = elementResizeDetectorMaker();
myChart.listenTo(this.$refs.chartDom, () => {
this.$nextTick(() => {
this.echartsInstance.resize();
});
});
我们在创建echarts实例的时候,使用即可,全部代码如下:
<!--
* @Descripttion:
* @version:
* @Author: ZhangJunQing
* @Date: 2022-06-08 16:01:36
* @LastEditors: ZhangJunQing
* @LastEditTime: 2022-08-10 17:38:41
-->
<template>
<div class="chart" ref="chartDom"></div>
</template>
<script>
import elementResizeDetectorMaker from "element-resize-detector"
export default {
props: {
option: {
type: Object,
required: true,
default: {},
},
},
data() {
return {
echartsInstance: null,
};
},
methods: {
init() {
if (!this.echartsInstance) {
let charts = this.$refs.chartDom;
this.echartsInstance = this.$echarts.init(charts);
let myChart = elementResizeDetectorMaker();
myChart.listenTo(this.$refs.chartDom, () => {
this.$nextTick(() => {
this.echartsInstance.resize();
});
});
}
// this.echartsInstance.showLoading({
// text: '暂无数据',
// showSpinner: false,
// textColor: '#646464',
// maskColor: 'rgba(255, 255, 255, 1)',
// fontSize: '16px',
// fontWeight: 'bold',
// })
this.echartsInstance.setOption(this.option);
// this.echartsInstance.hideLoading()
},
},
watch: {
option: {
handler(newOption) {
this.echartsInstance.clear();
this.echartsInstance.resize();
this.echartsInstance.setOption(newOption);
},
deep: true
},
},
mounted() {
this.init();
},
};
</script>
<style lang="less" scoped>
.chart {
// width: 300px;
// height: 300px;
width: 100%;
height: 100%;
}
</style>
将elementResizeDetectorMaker的实例函数,变成防抖即可:
myChart.listenTo(this.$refs.chartDom,
() => {
this.debounceFun(() => {
this.echartsInstance && this.$nextTick(() => {
this.echartsInstance.resize();
})
}, 18)
}
);
}
debounceFun
/**
* @param {函数} fn
* @param {防抖时间间隔} wait
*/
//防抖函数
export const debounce = () => {
let timer = null;
const newDebounce = function (fn, wait, ...args) {
return new Promise((resolve, reject) => {
if (timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(_ => {
try {
resolve(fn(...args));
} catch (e) {
reject(e);
}
}, wait);
});
};
return newDebounce;
};
防抖函数使用:
data() {
return {
debounceFun: debounce(),
echartsInstance: null,
};
},
改造之后的全部代码:
main.js
import elementResizeDetectorMaker from "element-resize-detector"
Vue.prototype.$erd = elementResizeDetectorMaker();
char:
<!--
* @Descripttion:
* @version:
* @Author: ZhangJunQing
* @Date: 2022-06-08 16:01:36
* @LastEditors: ZhangJunQing
* @LastEditTime: 2022-08-10 17:38:41
-->
<template>
<div class="chart" ref="chartDom"></div>
</template>
<script>
// debounce
/**
* @param {函数} fn
* @param {防抖时间间隔} wait
*/
//防抖函数
/**
export const debounce = () => {
let timer = null;
const newDebounce = function (fn, wait, ...args) {
return new Promise((resolve, reject) => {
if (timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(_ => {
try {
resolve(fn(...args));
} catch (e) {
reject(e);
}
}, wait);
});
};
return newDebounce;
};
*/
// this.$erd
/**
* import elementResizeDetectorMaker from "element-resize-detector"
* Vue.prototype.$erd = elementResizeDetectorMaker();
*/
import { debounce } from '@/utils/ToolFunctions'
export default {
props: {
option: {
type: Object,
required: true,
default: {},
},
},
data() {
return {
debounceFun: debounce(),
echartsInstance: null,
};
},
methods: {
init() {
if (!this.echartsInstance && !this.$echarts.getInstanceByDom(this.$refs.chartDom)) {
let charts = this.$refs.chartDom;
this.echartsInstance = this.$echarts.init(charts);
// let myChart = ;
// if (myChart == null) {
// myChart = this.$echarts.init(charts);
// this.echartsInstance = this.$echarts.init(charts);
// }
this.$erd.listenTo(this.$refs.chartDom,
() => {
this.debounceFun(() => {
this.echartsInstance && this.$nextTick(() => {
this.echartsInstance.resize();
})
}, 18)
}
);
}
this.echartsInstance.hideLoading()
// this.echartsInstance.showLoading({
// text: '暂无数据',
// showSpinner: false,
// textColor: '#646464',
// maskColor: 'rgba(255, 255, 255, 1)',
// fontSize: '16px',
// fontWeight: 'bold',
// })
this.echartsInstance.setOption(this.option);
// this.echartsInstance.hideLoading()
},
},
watch: {
option: {
handler(newOption) {
// 显示暂无数据
if (newOption.error) {
// 实例生成
if (this.echartsInstance) {
this.echartsInstance.clear();
this.echartsInstance.showLoading({
text: '暂无数据',
showSpinner: false,
textColor: '#646464',
maskColor: 'rgba(255, 255, 255, 1)',
fontSize: '16px',
fontWeight: 'bold',
})
} else {
// 实例不存在
this.$nextTick(() => {
// 判断当前dom节点有没有 被 注册
if (!this.$echarts.getInstanceByDom(this.$refs.chartDom)) {
let charts = this.$refs.chartDom;
this.echartsInstance = this.$echarts.init(charts);
}
this.echartsInstance.resize();
this.echartsInstance.showLoading({
text: '暂无数据',
showSpinner: false,
textColor: '#646464',
maskColor: 'rgba(255, 255, 255, 1)',
fontSize: '16px',
fontWeight: 'bold',
})
})
}
} else {
// 正常流程
if (this.echartsInstance) {
this.$nextTick(() => {
this.echartsInstance.clear();
this.echartsInstance.resize();
this.echartsInstance.hideLoading()
this.echartsInstance.setOption(newOption);
})
}
}
},
deep: true,
immediate: true
},
},
mounted() {
this.init();
},
destroyed() {
this.echartsInstance = null
this.$refs.chartDom && this.$erd.removeAllListeners(this.$refs.chartDom)
this.$refs.chartDom && this.$refs.chartDom.clear();
}
};
</script>
<style lang="less" scoped>
.chart {
width: 100%;
height: 100%;
}
</style>