Vue3 echarts自适应解决方案
实战
resize.js
import {nextTick, ref} from "vue";
import {debounce} from 'throttle-debounce';
/*chart 是echarts图的实例*/
export const chart = ref();
/*检测侧边栏是否缩放*/
let sidebarElm;
/*使用element-resize-detector 来监听侧边栏是否产生变化*/
const elementResizeDetectorMaker = require("element-resize-detector");
const erd = elementResizeDetectorMaker();
/*使用防抖debounce函数,减少resize的次数*/
const chartResizeHandler = debounce(100, false, () => {
if (chart.value) {
chart.value.resize()
}
})
/*在侧边栏宽度变化后,执行该函数*/
const sidebarResizeHandler = (e) => {
nextTick(() => {
chartResizeHandler()
})
}
/*添加窗口大小变化监听*/
const initResizeEvent = () => {
window.addEventListener('resize', chartResizeHandler)
}
/*移除窗口大小变化监听*/
const destroyResizeEvent = () => {
window.removeEventListener('resize', chartResizeHandler)
}
/*初始化 sider监听*/
const initSidebarResizeEvent = () => {
/*获取侧边栏的document*/
sidebarElm = document.getElementsByClassName('sider-content')[0];
if (sidebarElm) {
erd.listenTo(sidebarElm, sidebarResizeHandler)
}
}
/*移除 sider监听*/
const destroySidebarResizeEvent = () => {
if (sidebarElm) {
erd.removeListener(sidebarElm)
}
}
export const mounted = () => {
initResizeEvent();
initSidebarResizeEvent();
}
export const beforeDestroy = () => {
destroyResizeEvent();
destroySidebarResizeEvent();
}
实际使用:
<template>
<div style="height: 264px">
<div id="object-line" style="height: 264px;width: 100%"/>
</div>
</template>
<script setup>
import * as echarts from 'echarts';
import {nextTick, onBeforeUnmount, onMounted} from "vue";
import {mounted, beforeDestroy, chart} from "../../../../util/resize/resize";
const initLineChart = () => {
const lchart = echarts.init(document.getElementById('object-line'));
lchart.setOption(option);
chart.value = lchart;
}
onMounted(() => {
mounted();
nextTick(() => {
initLineChart();
});
})
onBeforeUnmount(() => {
beforeDestroy();
})
//x轴
const tdata = [
['2019-11-1', 32],
['2019-11-2', 55],
['2019-11-3', 80],
['2019-11-4', 55],
['2019-11-5', 80],
['2019-11-6', 32],
['2019-11-7', 32],
['2019-11-8', 32],
['2019-11-9', 32],
['2019-11-10', 32],
['2019-11-11', 32],
]
const bdata = [
['2019-11-1', 12],
['2019-11-2', 45],
['2019-11-3', 90],
['2019-11-6', 42],
['2019-11-7', 52],
['2019-11-8', 42],
['2019-11-9', 32],
['2019-11-10', 22],
['2019-11-11', 12],
]
const option = {
/*鼠标悬浮*/
tooltip: {},
/*tool*/
toolbox: {
feature: {
magicType: {show: true, type: ['line', 'bar']},
restore: {show: true},
saveAsImage: {
show: true,
title: '',
}
}
},
backgroundColor: 'white',
legend: {
data: ['t2', 't1'],
bottom: '2%'
},
xAxis: {
type: 'time',
/*不显示x轴*/
axisTick: {show: false},
axisLine: {
lineStyle: {
width: 0
}
},
data: [],
/*x轴标签样式*/
axisLabel: {
color: '#888888',
fontSize: 12,
show: true,
showMaxLabel: false,
showMinLabel: false,
formatter: '{MM}-{dd}',
}
},
yAxis: {
type: 'value',
splitLine: {
show: true,
lineStyle: {
color: '#F5F5F5'
},
},
axisLabel: {
show: false
},
},
/*图的位置*/
grid: {
x: 20,
y: 40,
x2: 20,
y2: 40,
containLabel: true
},
series: [
{
name: 't1',
data: tdata,
type: 'line',
symbolSize: 8,
lineStyle: {
color: '#F62681', //改变折线颜色
width: 3,
},
itemStyle: {
borderWidth: 12,
color: '#F62681', //改变拐点颜色
}
},
{
name: 't2',
data: bdata,
type: 'bar',
barWidth: 30,
itemStyle: {
color: '#1CA9C9',
borderRadius: [4, 4, 0, 0],
}
}
]
};
</script>
<style scoped>
</style>
导入包
-
element-resize-detector
安装 npm install element-resize-detector --save
-
throttle-debounce
安装 npm install throttle-debounce --save
Throttle-debounce 说明
介绍一下 throttle
和 debounce
,它们都可以用于 函数节流 从而提升性能,但它们还是存在一些不同:
- debounce:将短时间内多次触发的事件合并成一次事件响应函数执行(往往是在第一次事件或者在最后一次事件触发时执行),即该段时间内仅一次真正执行事件响应函数。
- throttle:假如在短时间内同一事件多次触发,那么每隔一段更小的时间间隔就会执行事件响应函数,即该段时间内可能多次执行事件响应函数。
虽然每天最烦等电梯要花上十几分钟,但还是可以用坐电梯来举例子:
- debounce:假如我在电梯里面正准备关门,这时 A 想要坐电梯,那么出于礼貌我会按下开门键,然后等他走进电梯后再尝试关门;等 A 进电梯后,又发现 B 也想要坐电梯,那么同样出于礼貌我会按下开门键,然后等他走进电梯。那么假如一直有人想要坐电梯的话,我就会不断地延后按下关门键的时机,直至没有人想要坐电梯(现实生活中我这样做的话,估计每天除了坐电梯就可以什么都不做了)。
- throttle:实际上我每天都有工作要完成,不可能在电梯里无限地等别人。那么这回我任性一点,规定我只等 30 秒,不管到时候有没有人想要坐电梯,我都会按下关门键走掉。
从上面两个例子中可以看出两者最大的区别在于只要有事件发生(有人想坐电梯),若使用了 throttle
方法,那么在一段时间内事件响应函数一定会执行(30秒内我按下关门键);若使用了 debounce
方法,那么只有事件停止发生后(我发现没有人想坐电梯)才会执行。
throttle
限制回调函数的执行频率
/**
* 节流(限制函数的执行频率)
* @param delay 延迟的时间
* @param noTrailing 在最后一次调用时是否执行 callback,true 不执行,false 执行
* @param callback 目标回调函数
* @param debounceMode
*/
throttle(delay, noTrailing, callback, debounceMode)
dobounceMode:
为 true 时,在被调用时,先执行 callback,在没有被调用时,在指定的延迟之后执行 clear,如果在clear 执行之前继续调用,会重置定时器;
为 false 时,在被调用时,不会执行 callback,在指定的延迟之后执行 callback,如果在 callback 执行之前继续调用,会重置定时器
debounce
限制回掉函数的执行频率,但是不同于 debounce 的是,debounce 能保证在一系列调用的时间内,回调函数只执行一次
/**
* 去抖(限制函数的执行频率)
* @param delay 延迟的时间
* @param atBegin
* @param callback 目标回调函数
*/
debounce(delay, atBegin, callback)
atBegin:
为 true 时,在被调用时,会马上执行 callback,如果在延迟时间之前继续调用,不会执行 callback;
为 false 时,在被调用时,不会执行 callback,在延迟时间之后会执行 callback,如果在延迟时间之前继续调用,会重置定时器
问题: 一个页面出现两个echarts图,会导致只有一个生效!
修改resize.js
import {nextTick, ref} from "vue";
import {debounce} from 'throttle-debounce';
export default function () {
const chart = ref();
let sidebarElm;
const elementResizeDetectorMaker = require("element-resize-detector");
const erd = elementResizeDetectorMaker();
const chartResizeHandler = debounce(100, false, () => {
if (chart.value) {
chart.value.resize()
}
})
const sidebarResizeHandler = (e) => {
nextTick(() => {
chartResizeHandler()
})
}
const initResizeEvent = () => {
window.addEventListener('resize', chartResizeHandler)
}
const destroyResizeEvent = () => {
window.removeEventListener('resize', chartResizeHandler)
}
const initSidebarResizeEvent = () => {
sidebarElm = document.getElementsByClassName('sider-content')[0];
if (sidebarElm) {
erd.listenTo(sidebarElm, sidebarResizeHandler)
}
}
const destroySidebarResizeEvent = () => {
if (sidebarElm) {
erd.removeListener(sidebarElm)
}
}
const mounted = () => {
initResizeEvent();
initSidebarResizeEvent();
}
const beforeDestroy = () => {
destroyResizeEvent();
destroySidebarResizeEvent();
}
return {
chart,
mounted,
beforeDestroy,
}
}
使用
<template>
<div style="height: 264px">
<div id="object-line" style="height: 264px;width: 100%"/>
</div>
</template>
<script setup>
import * as echarts from 'echarts';
import {nextTick, onBeforeUnmount, onMounted} from "vue";
import resize from "../../../../util/resize/resize";
const {chart, mounted, beforeDestroy} = resize();
const initLineChart = () => {
const lchart = echarts.init(document.getElementById('object-line'));
lchart.setOption(option);
chart.value = lchart;
}
onMounted(() => {
mounted();
nextTick(() => {
initLineChart();
});
})
onBeforeUnmount(() => {
beforeDestroy();
})
//x轴
const tdata = [
['2019-11-1', 32],
['2019-11-2', 55],
['2019-11-3', 80],
['2019-11-4', 55],
['2019-11-5', 80],
['2019-11-6', 32],
['2019-11-7', 32],
['2019-11-8', 32],
['2019-11-9', 32],
['2019-11-10', 32],
['2019-11-11', 32],
]
const bdata = [
['2019-11-1', 12],
['2019-11-2', 45],
['2019-11-3', 90],
['2019-11-6', 42],
['2019-11-7', 52],
['2019-11-8', 42],
['2019-11-9', 32],
['2019-11-10', 22],
['2019-11-11', 12],
]
const option = {
/*鼠标悬浮*/
tooltip: {},
/*tool*/
toolbox: {
feature: {
magicType: {show: true, type: ['line', 'bar']},
restore: {show: true},
saveAsImage: {
show: true,
title: '',
}
}
},
backgroundColor: 'white',
legend: {
data: ['t2', 't1'],
bottom: '2%'
},
xAxis: {
type: 'time',
/*不显示x轴*/
axisTick: {show: false},
axisLine: {
lineStyle: {
width: 0
}
},
data: [],
/*x轴标签样式*/
axisLabel: {
color: '#888888',
fontSize: 12,
show: true,
showMaxLabel: false,
showMinLabel: false,
formatter: '{MM}-{dd}',
}
},
yAxis: {
type: 'value',
splitLine: {
show: true,
lineStyle: {
color: '#F5F5F5'
},
},
axisLabel: {
show: false
},
},
/*图的位置*/
grid: {
x: 20,
y: 40,
x2: 20,
y2: 40,
containLabel: true
},
series: [
{
name: 't1',
data: tdata,
type: 'line',
symbolSize: 8,
lineStyle: {
color: '#F62681', //改变折线颜色
width: 3,
},
itemStyle: {
borderWidth: 12,
color: '#F62681', //改变拐点颜色
}
},
{
name: 't2',
data: bdata,
type: 'bar',
barWidth: 30,
itemStyle: {
color: '#1CA9C9',
borderRadius: [4, 4, 0, 0],
}
}
]
};
</script>
<style scoped>
</style>