resize使用了ResizeObserver https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver
echart的resize功能需要监听的不是window,所以window.onresize消耗太大关键有时甚至起不到作用。我们需要找到一个可以监听单个儿元素的方法。ResizeObserver就是这么个东西。监听时observer,走前用下unobserve。
debounce就是一个非常朴素的防抖:设置一个定时器,下次方法执行前先将定时器清除,再开始新一轮计时。
图就不放了,只是个非常朴素的柱状图,主要是为了练习Vue3的组件传值、封装有debounce的resize。
代码:
封装的俩函数 👇
/**
* 防抖
* @param fn
* @param time
*/
type ArgsType = Element | ResizeObserver;
export const debounce = (fn: (...args: ArgsType[]) => void, delay = 200) => {
let timer = 0;
return (...args: ArgsType[]) => {
if(timer) {
clearTimeout(timer);
timer = 0
}
timer = window.setTimeout(() => {
fn(...args)
}, delay)
}
}
/**
* 监听DOMresize:需要传入一个Element,和一个resize时的callback。使用时,需要在页面销毁前resizeObserver.unobserve()
* @param el
*/
export const domResize = (el: Element, callback: (...args: ArgsType[]) => void) => {
const debounceCallback: (...args: ArgsType[]) => void = debounce(callback);
const resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
const dom = entries[0].target;
debounceCallback(dom, resizeObserver)
});
// 监听元素
resizeObserver.observe(el);
};
组件代码:
<script setup lang="ts">
import {ComponentInternalInstance} from 'vue'
import { getKey } from '@/utils/ancode'
import { init, registerTheme, EChartsOption } from 'echarts'
import {domResize} from '@/utils/utils'
import westeros from './westeros.json'
// #region ---- 组件传值
const props = defineProps({
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '100%'
},
title: {
type: Object,
default: () => {
return {
text: ''
}
}
},
tooltip: {},
xAxis: {},
series: {}
})
const emit = defineEmits(['resize']);
// #endregion
// #region ---- DOM
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// ref随机名儿
const refEchart: Ref<string> = ref("");
// chart容器的DOM
const echartEl = ref();
// chart实例
const chartInstance = shallowRef(); // 页面注销时echart需要dispose
// resizeObserver实例
const resizeObserverObj = shallowRef(); // 页面注销时,监听元素的方法需要传值回去unobserve
const initRef = () => {
// 给ref获取随机名字
refEchart.value = getKey();
nextTick(() => {
// 获取echart元素
echartEl.value = proxy?.$refs[refEchart.value];
})
}
// #endregion
// 画图
const drawEchart = () => {
// 注册更新的主题
registerTheme('westeros', westeros);
nextTick(() => {
chartInstance.value = init(echartEl.value, 'westeros');
// 绘制图表
chartInstance.value.setOption({
title: {
text: props.title.text
},
tooltip: props.tooltip,
xAxis: props.xAxis,
yAxis: {},
series: props.series
} as EChartsOption);
// 这个方法原本想着将动作传递出去,可以在页面中设置图表的宽高比。一旦容器宽高发生变化,我们就在页面中按照宽高比进行设置。可是这个有绕。所以呈现在页面中有卡顿情况。暂时先留着,但没用
domResize(echartEl.value, (dom, resizeObserver) => {
emit('resize', dom);
chartInstance.value.resize();
resizeObserverObj.value = resizeObserver;
})
})
}
onMounted(() => {
initRef();
drawEchart();
})
onBeforeUnmount(() => {
// 这俩顺序不能乱,必须先将监听卸载,才能销毁echart实例
resizeObserverObj.value.unobserve(echartEl.value);
chartInstance.value?.dispose();
})
</script>
<template>
<div :ref="refEchart" :style="{ width: props.width, height: props.height }"></div>
</template>
使用页面代码:
<script setup lang="ts">
import {ComponentInternalInstance} from 'vue'
import { getKey } from '@/utils/ancode'
import { init, registerTheme, EChartsOption } from 'echarts'
import {domResize} from '@/utils/utils'
import westeros from './westeros.json'
// #region ---- 组件传值
const props = defineProps({
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '100%'
},
title: {
type: Object,
default: () => {
return {
text: ''
}
}
},
tooltip: {},
xAxis: {},
series: {}
})
const emit = defineEmits(['resize']);
// #endregion
// #region ---- DOM
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// ref随机名儿
const refEchart: Ref<string> = ref("");
// chart容器的DOM
const echartEl = ref();
// chart实例
const chartInstance = shallowRef(); // 页面注销时echart需要dispose
// resizeObserver实例
const resizeObserverObj = shallowRef(); // 页面注销时,监听元素的方法需要传值回去unobserve
const initRef = () => {
// 给ref获取随机名字
refEchart.value = getKey();
nextTick(() => {
// 获取echart元素
echartEl.value = proxy?.$refs[refEchart.value];
})
}
// #endregion
// 画图
const drawEchart = () => {
// 注册更新的主题
registerTheme('westeros', westeros);
nextTick(() => {
chartInstance.value = init(echartEl.value, 'westeros');
// 绘制图表
chartInstance.value.setOption({
title: {
text: props.title.text
},
tooltip: props.tooltip,
xAxis: props.xAxis,
yAxis: {},
series: props.series
} as EChartsOption);
// 这个方法原本想着将动作传递出去,可以在页面中设置图表的宽高比。一旦容器宽高发生变化,我们就在页面中按照宽高比进行设置。可是这个有绕。所以呈现在页面中有卡顿情况。暂时先留着,但没用
domResize(echartEl.value, (dom, resizeObserver) => {
emit('resize', dom);
chartInstance.value.resize();
resizeObserverObj.value = resizeObserver;
})
})
}
onMounted(() => {
initRef();
drawEchart();
})
onBeforeUnmount(() => {
// 这俩顺序不能乱,必须先将监听卸载,才能销毁echart实例
resizeObserverObj.value.unobserve(echartEl.value);
chartInstance.value?.dispose();
})
</script>
<template>
<div :ref="refEchart" :style="{ width: props.width, height: props.height }"></div>
</template>