希望大家应该也遇到过,当我们使用echarts时,在侧边导航折叠展开时,resize方法不生效的问题。以下提供两种个人比较推荐的解决方法。
方法一
使用 setTimeout
可以看到elementui的过渡时间是0.3s
所以在监听侧边栏折叠的时间使用settimeout,0.3s后执行resize方法。
watch(() => hasCollapse.value, (val) => {
setTimeout(() => {
chart.value?.resize();
}, 300);
})
方法二(推荐)
编写动画函数执行resize方法
// animation.ts
export const useAnimation = (
duration: number,
from: number,
to: number,
callback: (value: number) => void
) => {
const startTime = Date.now();
const diff: number = to - from;
const average: number = diff / duration;
let value: number = from;
callback(value);
const _animate = () => {
const currentTime: number = Date.now();
const timeDiff: number = currentTime - startTime;
if (timeDiff >= duration) {
value = to;
callback(value);
return;
}
value = diff < 0 ? from + (timeDiff * average) : timeDiff * average;
callback(value);
requestAnimationFrame(_animate);
}
_animate();
}
注意:需要结合自定义hook,可以封装一个hook,返回容器的宽(window.innerWidth - 侧边导航菜单的宽度),这里我侧边菜单的宽度是 200(展开状态)和 60(折叠状态)
// useContainer.ts
import { computed, ref, watch } from "vue";
import { useSettingStore } from "@/store";
import { useEventListener } from "./event";
const settingStore = useSettingStore();
// 获取主体内容的宽高
export const useWidthHeight = () => {
const hasCollapse = computed<boolean>(() => {
return settingStore.hasCollapse;
})
// 高度:window.innerHeight - header 高度
const mainHeight = ref<number>(window.innerHeight - 60);
// 宽度:window.innnerWidth - aside 宽度
const mainWidth = ref<number>(window.innerWidth - (hasCollapse.value ? 60 : 200));
const updateWidthHeight = () => {
mainHeight.value = window.innerHeight - 60;
mainWidth.value = window.innerWidth - (hasCollapse.value ? 60 : 200);
}
// 挂载/销毁事件,注:useEventListener是我封装的hooks,这里跟 window.addEventListener('resize')是一样的
useEventListener(window, 'resize', updateWidthHeight);
watch(hasCollapse, (val) => {
updateWidthHeight();
})
return { height: mainHeight, width: mainWidth };
}
当监听到折叠时这个主体宽度是会变化的。
在组件中导入这两个方法
import { useAnimation } from '@/utils/animation';
import { useWidthHeight } from '@/hooks';
const { width } = useWidthHeight();
const onResize = () => {
chart.value?.resize();
}
onMounted(() => {
initChart();
window.addEventListener('resize', onResize);
})
onBeforeUnmount(() => {
window.removeEventListener('resize', onResize);
})
// 监听主体内容宽度,传入时间300ms,宽度新/旧值,在回调函数中执行resize方法
watch(width, (nVal, oVal) => {
// 侧边栏展开/折叠动画间隔0.3s,使用动画解决侧边栏展开/折叠resize不生效
useAnimation(300, nVal, oVal, () => {
chart.value?.resize();
})
})
效果不会像使用setTimeout方法,在最后突然抖动一下去resize,这个方法可以在过渡中resize,体验相对友好。
当然useAnimation这个工具函数用途不止如此,还可以做一些其他动画过渡效果,大家可以试一试,希望对你们有用。
方法三(推荐)
使用 ResizeObserver 封装 v-resize 指令
const map = new WeakMap();
const resizeOb = new ResizeObserver((entries) => {
for (const entry of entries) {
const handler = map.get(entry.target);
let width, height;
if (entry.borderBoxSize) {
const contentBoxSize = Array.isArray(entry.borderBoxSize)
? entry.borderBoxSize[0]
: entry.borderBoxSize;
width = contentBoxSize.inlineSize;
height = contentBoxSize.blockSize;
} else {
width = entry.contentRect.width;
height = entry.contentRect.height;
}
handler({ width, height });
}
});
/**
* 监听元素尺寸变化
* @example
* ```vue
* <div v-resize="handleResize"></div>
* ```
* ```js
* const handleResize = ({ width, height }) => {
* // todo
* };
* ```
*/
const vResizeEl = {
mounted(el, binding) {
const value = binding.value;
if (value && typeof value === 'function') {
map.set(el, value);
} else {
throw new Error('binding value is not a function');
}
resizeOb.observe(el);
},
unmounted(el) {
resizeOb.unobserve(el);
},
};
export default vResizeEl;
用法
<div ref="chartInstance" v-resize="handleResize"> </div>
// 响应图表大小
const handleResize = () => {
if (chartInstance.value) {
chartInstance.value.resize();
}
};