ECharts 初窥

668 阅读2分钟

是什么

PS:以下定义来自百科

Echarts是一个基于JavaScript的开源可视化库,由百度开发和维护。它提供了丰富的图表类型和交互方式,可以帮助用户快速地创建各种数据可视化图表,包括折线图、柱状图、饼图、散点图、地图等等。

Echarts支持多种数据格式,包括JSON、XML、CSV等,同时也支持动态数据的更新和异步加载。

Echarts还提供了丰富的配置选项和API接口,可以满足不同用户的需求。

Echarts的优点在于其易用性、灵活性和可扩展性,可以应用于各种领域,如数据分析、商业智能、地理信息系统等。

简单示例

// 用vue3绘制柱状图

<template>
  <div class="chart-container">
    <div ref="chart" class="chart"></div>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import * as echarts from 'echarts';

export default {
  name: 'BarChart',
  setup() {
    const chartRef = ref(null);

    onMounted(() => {
      const chart = echarts.init(chartRef.value);

      const options = {
        title: {
          text: '柱状图示例',
        },
        tooltip: {},
        xAxis: {
          data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20, 5],
          },
        ],
      };

      chart.setOption(options);
    });

    return {
      chartRef,
    };
  },
};
</script>

<style>
.chart-container {
  width: 100%;
  height: 400px;
}

.chart {
  width: 100%;
  height: 100%;
}
</style>

简单封装

<template>
  <div ref="chart" class="chart" :style="{ width: width, height: height }"></div>
</template>

<script lang="ts">
import { defineComponent, ref, watch, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';

export default defineComponent({
  name: 'ECharts',
  props: {
    options: {
      type: Object as () => echarts.EChartOption,
      required: true,
    },
    width: {
      type: Number,
      default: 400,
    },
    height: {
      type: Number,
      default: 400,
    },
    theme: {
      type: String,
      default: 'light',
    },
    autoResize: {
      type: Boolean,
      default: true,
    },
    resizeDelay: {
      type: Number,
      default: 200,
    },
    events: {
      type: Object as () => Record<string, Function>,
      default: () => ({}),
    },
  },
  setup(props, { emit }) {
    const chartRef = ref<HTMLDivElement | null>(null);
    let chart: echarts.ECharts | null = null;
    let resizeTimer: ReturnType<typeof setTimeout> | null = null;

    const initChart = () => {
      if (!chart) {
        chart = echarts.init(chartRef.value!, props.theme);
      }
      chart.setOption(props.options);
      bindEvents();
    };

    const resizeChart = () => {
      if (chart) {
        chart.resize();
      }
    };

    const debouncedResizeChart = () => {
      if (resizeTimer) {
        clearTimeout(resizeTimer);
      }
      resizeTimer = setTimeout(() => {
        resizeChart();
      }, props.resizeDelay);
    };

    const bindEvents = () => {
      for (const eventName in props.events) {
        if (Object.prototype.hasOwnProperty.call(props.events, eventName)) {
          chart?.on(eventName, props.events[eventName]);
        }
      }
    };

    const unbindEvents = () => {
      for (const eventName in props.events) {
        if (Object.prototype.hasOwnProperty.call(props.events, eventName)) {
          chart?.off(eventName, props.events[eventName]);
        }
      }
    };

    onMounted(() => {
      initChart();
      if (props.autoResize) {
        window.addEventListener('resize', debouncedResizeChart);
      }
    });

    onUnmounted(() => {
      if (chart) {
        chart.dispose();
        chart = null;
      }
      if (props.autoResize) {
        window.removeEventListener('resize', debouncedResizeChart);
      }
      unbindEvents();
    });

    watch(
      () => props.options,
      () => {
        if (chart) {
          chart.setOption(props.options);
        }
      }
    );

    watch(
      () => props.events,
      (newEvents, oldEvents) => {
        for (const eventName in oldEvents) {
          if (Object.prototype.hasOwnProperty.call(oldEvents, eventName)) {
            chart?.off(eventName, oldEvents[eventName]);
          }
        }
        for (const eventName in newEvents) {
          if (Object.prototype.hasOwnProperty.call(newEvents, eventName)) {
            chart?.on(eventName, newEvents[eventName]);
          }
        }
      },
      { deep: true, immediate: true }
    );

    return {
      chartRef,
    };
  },
  emits: ['click', 'legendselectchanged', 'legendselected', 'legendunselected', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu', 'datazoom', 'datarangeselected', 'timelinechanged', 'timelineplaychanged', 'restore', 'dataviewchanged', 'magictypechanged', 'geoselectchanged', 'geoselected', 'geounselected', 'pieselectchanged', 'pieselected', 'pieunselected', 'mapselectchanged', 'mapselected', 'mapunselected', 'axisareaselected', 'focusnodeadjacency', 'unfocusnodeadjacency', 'brush', 'brushselected', 'rendered', 'finished', 'click', 'dblclick', 'mousedown', 'mousemove', 'mouseup', 'mouseover', 'mouseout', 'globalout', 'contextmenu', 'legendselectchanged', 'legendselected', 'legendunselected', 'datazoom', 'datarangeselected', 'timelinechanged', 'timelineplaychanged', 'restore', 'dataviewchanged', 'magictypechanged', 'geoselectchanged', 'geoselected', 'geounselected', 'pieselectchanged', 'pieselected', 'pieunselected', 'mapselectchanged', 'mapselected', 'mapunselected', 'axisareaselected', 'focusnodeadjacency', 'unfocusnodeadjacency', 'brush', 'brushselected', 'rendered', 'finished'],
});
</script>

<style>
.chart {
  width: 100%;
  height: 100%;
}
</style>

封装思路解析

1、封装前需要明确的:自己的需求是什么?自己封装的这个组件承担的任务是什么?注意不能过度封装,避免组件失去灵活性。

2、该组件接收一些props来配置图表,包括widthheight``optionsthemeautoResizeresizeDelayevents。其中,options是必需的,它包含ECharts的配置选项。其他props都有默认值。

3、在组件的setup函数中,首先定义了一些变量和函数。其中包括chartRef,一个Vue ref,用于指向图表的DOM元素。还有chart,表示ECharts的实例;resizeTimer,一个用于处理图表自适应大小的计时器;initChart,初始化ECharts实例并渲染图表;resizeChart,重新计算图表大小;debouncedResizeChart,将resizeChart函数封装为防抖函数;bindEventsunbindEvents,分别用于绑定和解绑ECharts的事件。

4、在Vue组件的生命周期函数onMountedonUnmounted中分别初始化和销毁ECharts实例,并根据autoResize的值来添加或删除窗口大小调整的事件监听器。此外,还在watch选项中监听optionsevents的变化,以更新图表和事件监听器。

5、声明了一个emits选项,列出了所有可能由该组件发出的事件,以便在父组件中进行监听。

如何使用基础组件

<template>
  <div>
    <EChart
      :options="chartOptions"
      :height="400"
      :width="600"
      :autoresize="true"
      @chart-click="handleChartClick"
    />
  </div>
</template>

<script>
import EChart from "@/components/EChart.vue";
import { reactive } from "vue";

export default {
  name: "MyChart",
  components: {
    EChart,
  },
  setup() {
    const state = reactive({
      chartOptions: {
        xAxis: {
          type: "category",
          data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
        },
        yAxis: {
          type: "value",
        },
        series: [
          {
            data: [820, 932, 901, 934, 1290, 1330, 1320],
            type: "line",
          },
        ],
      },
      loading: false,
    });

    const handleChartClick = (params) => {
      console.log(params);
    };

    return { state, handleChartClick };
  },
};
</script>

代码解析: 在这个示例中,我们使用了 组件,并绑定了以下 props:

  • options:传递给EChart的配置项。
  • height:图表的高度。
  • width:图表的宽度。
  • autoresize:是否根据父容器的大小自适应大小

还绑定了一个自定义事件 @chart-click,用于在图表被点击时触发回调函数 handleChartClick,其中 params 是被点击点的信息对象。

优秀案例

juejin.cn/post/707883…

参考链接

juejin.cn/post/716938…

juejin.cn/post/699551…

juejin.cn/post/716938…