echarts学习: 图表自适应

745 阅读4分钟

1.实现基本功能

我最近在研究如何封装echarts组件,几乎每个相关的文章都会给组件添加图表自适应的功能。其原理也很简单,就是通过侦听windowresize事件,当事件触发时就调用chartInstance.resize方法重置图表尺寸。

基本代码如下:

// 重置图表尺寸
function resize() {
  instance.value &&
      instance.value.resize({
        animation: {
          duration: 300,
        },
      })
}

onMounted(() => {
  window.addEventListener('resize',resize)
})
onUnmounted(() => {
  window.removeEventListener('resize',resize)
})

2.使用防抖优化自适应函数

因为这里涉及到了resize事件,为了保证前端的性能,建议对自适应函数进行防抖处理。 我使用了Loash中的debounce方法来实现防抖。

import { debounce } from 'lodash-es'
// 重置图表尺寸
function resize() {
  instance.value &&
    instance.value.resize({
      animation: {
        duration: 300,
      },
    })
}

// 进行防抖处理
const debounceResize = debounce(resize, 300, { maxWait: 800 })

onMounted(() => {
  window.addEventListener('resize', debounceResize)
})
onUnmounted(() => {
  window.removeEventListener('resize', debounceResize)
})

什么是防抖?

概念防抖是指当函数在一定时间内被频繁触发的时候,为了保证前端性能和用户体现,只执行最后一次的触发。
实现原理当函数被触发时,将其放入一个计时器中,等到期后再执行函数。在计时器到期之前,如果函数被重复触发,就清除之前的计时器并重新开始计时。
适用场景resizescroll、输出框内容校验等操作。
防抖与节流的区别在我看来防抖和节流的区别在于:防抖执行规定时间内函数的最后一次触发;节流执行规定时间内函数的第一次触发。那么哪些函数应该防抖?哪些又应该节流呢?我认为在规定的时间内,重复执行结果相同的函数应该节流;重复执行结果不同的函数应该防抖。

3.误入歧途

在实现了上述的“自适应”之后,我就进行了测试,我满以为加上了上面的代码我的图表的尺寸就会跟着窗口尺寸的变化而变化,可结果却让我大跌眼镜。

可以看到,我改变浏览器窗口的尺寸,但图表的尺寸却不会变化。

在经过这个挫折之后,我开始停下来思考。我发现自己之前对“自适应功能”的理解是错误的。我之前的理解如下:

实际上上述理解存在一个严重的错误,那就是:窗口尺寸的变化和图表尺寸的变化有什么关系?它们其实没有直接联系。

正确的逻辑应该如下图所示,窗口尺寸的变化导致容器尺寸的变化,然后再调用chartInstance.resize方法改变图表的尺寸。

而在我之前的代码中将容器的尺寸设置为固定值,因此无论窗口的尺寸怎么变化,容器的尺寸都不会变,进而图表的尺寸也不会变。因此想要实现图表自适应,除了echarts组件中的相关代码外,还需要项目本身有自适应的方案(例如,基于rem或者scale的方案)。

当然最简单的方法,我们只需要将容器的尺寸设置为相对单位vw/vh就可以体验图表自适应的效果了。

  <div ref="container" style="width: 60vw; height: 50vh;"></div>

4.使用ResizeObserver代替resize事件

在经过了反思之后,我发现自己之所以会出现之前的错误理解大致有两个方面的原因。

一方面是因为我个人的不求甚解,没有去仔细的思考图表自适应的原理,只是机械的模仿别人的写法。

另一方面也是因为我在一定程度上被误导了,目前我所看到的所有文章中都是直接侦听windowresize事件,实际上我认为准确的讲不应该侦听window的尺寸变化,而是应该侦听容器的尺寸变化。

那么问题来了,怎么侦听元素的尺寸变化呢?元素可没有resize事件。

答案是可以使用ResizeObserver元素尺寸侦听器,ResizeObserver 接口可以监听 Element 或者 SVGElement 边界尺寸的变化。其详细用法请参考MDN中与ResizeObserver的相关文档

接着使用ResizeObserver重写自适应相关的代码:

import { debounce } from 'lodash-es'
// 重置图表尺寸
function resize() {
  instance.value &&
    instance.value.resize({
      animation: {
        duration: 300,
      },
    })
}

// 进行防抖处理
const debounceResize = debounce(resize, 300, { maxWait: 1000 })

let resizeObserver = new ResizeObserver(debounceResize)

onMounted(() => {
  init()
  resizeObserver.observe(container.value)
})
onUnmounted(() => {
  instance.value?.dispose()
  resizeObserver.disconnect()
  resizeObserver = null
})

参考资料

  1. Documentation - Apache ECharts
  2. lodash.debounce | Lodash中文文档 | Lodash中文网
  3. JS—节流与防抖_js节流防抖-CSDN博客
  4. ResizeObserver - Web API | MDN