干货!Vue3 echarts自适应解决方案

本文介绍了如何在Vue3中实现Echarts图表的自适应,包括使用`element-resize-detector`库监听侧边栏变化和防抖函数`debounce`优化resize事件。通过创建组件并暴露初始化和销毁方法,确保多个Echarts图表在页面中可以正确响应窗口和侧边栏尺寸变化。此外,还详细解释了`throttle`和`debounce`的区别,并提供了实战代码示例。
摘要由CSDN通过智能技术生成

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>

导入包

  1. element-resize-detector

    安装 npm install element-resize-detector --save

  2. throttle-debounce

    安装 npm install throttle-debounce --save

Throttle-debounce 说明

介绍一下 throttledebounce ,它们都可以用于 函数节流 从而提升性能,但它们还是存在一些不同:

  • 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>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

第七感 de秘密

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值