Vue3 + Echarts 实现词云图

目录

1. package.json

2. templete

3. import

4. 常用配置

5. props、emits

6. 实现逻辑

6.1 添加响应式变量

6.2 处理传入的 props 图表数据 dataProcessing

6.3 绘制词云图 drawChart

6.4 重新设置图表大小 resize

6.5 图表初始化操作 onMounted

6.6 图表卸载操作 onUnmounted

6.7 监听 options 变化,重绘词云图

6.8 监听 data 变化,重绘词云图

6.9 监听自定义样式变化,重绘词云图

7. 使用示例


效果展示:

1. package.json

{
  "peerDependencies": {
    "vue": ">=3.0.0",
    "echarts": ">=5.2.2",
    "echarts-wordcloud": "2.0.0",
    "lodash": "^4.17.21"
  }
}

2. templete

一定不要忘记设置 存放图表的 容器宽高

<template>
  <!-- 外层容器 -->
  <div
    class="word-cloud-container"
    :style="{ ...curStyle?.wrapper?.default }"
  >
    <!-- 图表容器 -->
    <div
      ref="chart"
      class="word-cloud"
    />
  </div>
</template>

<style lang="scss">
.word-cloud-container {
  .word-cloud {
    width: 100%;
    height: 100%;
  }
}
</style>

3. import

// 引入 Echarts
import * as echarts from 'echarts';
import { EChartsOption } from 'echarts/types/dist/echarts';
// 引入词云外部插件
import 'echarts-wordcloud';
// 引入 lodash 中的 merge、深克隆
import merge from 'lodash/merge';
import cloneDeep from 'lodash/cloneDeep';

4. 常用配置

shape —— 词云的形状,默认是 circle,可选的参数有 cardioid 、 diamond 、 triangle-forward 、 triangle 、 star

left / top / right / bottom —— 词云的位置,默认是 center center

width / height —— 词云的宽高,默认是 75% 80%

sizeRange —— 词云的文字字号范围,默认是[12, 60] ,词云会根据提供原始数据的 value 对文字的字号进行渲染。以默认值为例, value 最小的渲染为 12px ,最大的渲染为 60px ,中间的值按比例计算相应的数值

rotationRange / rotationStep —— 词云中文字的角度,词云中的文字会随机的在 rotationRange 范围内旋转角度,渲染的梯度就是 rotationStep ,这个值越小,词云里出现的角度种类就越多。如果是45度到135度,可能旋转的角度就是 -90 -45 0 45 90 

gridSize —— 词云中每个词的间距

drawOutOfBound —— 是否允许词云在边界外渲染,直接使用默认参数 false 就可以,否则容易造成词重叠

textStyle —— 词云中文字的样式, normal 是初始的样式, emphasis 是鼠标移到文字上的样式

其他的可以参考 Echarts 官方文档中的配置项

5. props、emits

  props: {
    // 自定义组件样式
    cStyle: {
      type: Object as PropType<ComponentCustomStyle>,
      default: () => ({
        // 外层容器
        wrapper: {
          default: {},
        },
      }),
    },
    // echarts原生配置
    options: {
      type: Object as PropType<EChartsOption>,
      default: () => ({}),
      required: false,
    },
    // 传入数组数据 如果不传入数据 会使用默认数据
    // 如果 options 设置了 series 属性 则会忽略 data 的数据
    data: {
      type: Array as PropType<CommonItem[]>,
      default: () => ([]),
    },
    // 别名 及 样式设置
    seriesSetting: {
      type: Object,
      default: () => ({
        // 词云文本样式
        wordStyle: {
          type: Object,
          default: () => ({}),
        },
        // 取 resData 数据中的别名设置
        data: {
          nameKey: '', // 名称别名
          dataKey: '', // 数据别名
        },
      }),
    },
    wordCloud: {
      type: Object,
      default: () => ({}),
    },
  },

  emits: ['com-click'],

6. 实现逻辑

6.1 添加响应式变量

    // props 传入的自定义样式
    const curStyle = computed(() => props.cStyle);

    // 图表元素

    const charts = ref<HTMLElement | null>(null);

    // 图表实例
    let myChart: any = null;

    const state = reactive({
      // 图表数据
      chartData: [] as CommonItem[],
    });

6.2 处理传入的 props 图表数据 dataProcessing

    /**
     *  数据处理
     */
    const dataProcessing = () => {
      if (props.seriesSetting?.data && props.data) {
        // 数据别名
        const { nameKey, dataKey } = props.seriesSetting.data;
        // 对传入的数组数据进行键值的处理,以符合图表数据的要求
        state.chartData = props.data.map((item: CommonItem) => ({
          name: item[nameKey] || '--',
          value: item[dataKey] || 0,
        }));
      }
    };

6.3 绘制词云图 drawChart

合并 props 传入的数据 及 默认数据

options 中的 series 优先级高于 data

    /**
     * 绘制图形
     */
    const drawChart = () => {
      /* 只要是能配置的属性 都需要将传入的数据(props)与默认的数据合并 */
      const { color, series } = props.options;
      let seriesData: any;
      // options 中的 series 优先级最高
      if (series) {
        seriesData = (series as any).map((item) => merge(
          {
            type: 'wordCloud',
            // 单词之间的间隔大小
            gridSize: 12,
            // 最小字体和最大字体 必须写一个范围不能直接写每个字的大小
            sizeRange: [12, 30],
            // 字体旋转角度的范围,水平
            rotationRange: [-45, 0, 45, 90],
            // 词云形状
            shape: 'circle',
            width: '80%',
            height: '100%',
            left: 'center',
            top: 'center',
            right: null,
            bottom: null,
            // 词云是否显示完整,超出画布的也显示
            drawOutOfBound: false,
            textStyle: {
              fontWeight: '500',
              // 字体随机颜色
              color: (params) => (color && color[params.dataIndex]) || defaultOptions.color[params.dataIndex]
            || `rgb(${[Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255)].join(',')})`,
            },
            ...props?.wordCloud,
          },
          item,
        ));
      } else if (props.data && props.seriesSetting?.data) {
        // data 的优先级其次
        seriesData = merge({
          type: 'wordCloud',
          // 单词之间的间隔大小
          gridSize: 12,
          // 最小字体和最大字体 必须写一个范围不能直接写每个字的大小
          sizeRange: [12, 30],
          // 字体旋转角度的范围,水平
          rotationRange: [-45, 0, 45, 90],
          // 词云形状
          shape: 'circle',
          width: '80%',
          height: '100%',
          left: 'center',
          top: 'center',
          right: null,
          bottom: null,
          // 词云是否显示完整,超出画布的也显示
          drawOutOfBound: false,
          textStyle: {
            fontWeight: 'normal',
            // 字体随即色
            color: (params) => (color && color[params.dataIndex]) || defaultOptions.color[params.dataIndex]
            || `rgb(${[Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255)].join(',')})`,
          },
          data: state.chartData,
        },
        props?.wordCloud,
        props?.seriesSetting?.wordStyle);
      }

      // 合并默认配置
      let option: any = merge(
        {
          series: seriesData,
        },
        defaultOptions,
      );

      // 合并数据
      option = merge(option, props.options);

      // 画布清除
      myChart.clear();

      try {
        myChart.setOption(option);
      } catch (error) {
        console.log(error);
      }
    };

6.4 重新设置图表大小 resize

    /**
     * 重新设置图表的尺寸
     */
    const resize = () => {
      nextTick(() => {
        myChart.resize();
      });
    };

6.5 图表初始化操作 onMounted

实例化 echarts 对象

处理传入数据

绘制图表

添加系列点击事件

监听视窗尺寸变化,重置图表尺寸

    onMounted(() => {
      if (charts.value) {
        // 实例化 echarts 对象
        myChart = echarts.init(charts.value);
        // 处理传入数据
        dataProcessing();
        // 绘制图表
        drawChart();
        // 系列点击事件(此组件为点击词)
        myChart.on('click', (params: unknown) => {
          emit('com-click', params);
        });
      }
      // 监听视窗尺寸变化 重置图表尺寸
      window.addEventListener('resize', resize);
    });

6.6 图表卸载操作 onUnmounted

移除监听事件

    onUnmounted(() => {
      // 移除监听事件
      window.removeEventListener('resize', resize);
    });

6.7 监听 options 变化,重绘词云图

    /**
     *  监听options、词云样式变化 重绘词云
     */
    watch(() => [props.options, props.seriesSetting.wordStyle, props.wordCloud], () => {
      drawChart();
    },
    {
      deep: true,
    });

6.8 监听 data 变化,重绘词云图

    /**
     *  监听 data 的变化, 处理数据、重绘词云
     */
    watch(() => [props.data, props.seriesSetting.data], () => {
      dataProcessing();
      drawChart();
    },
    {
      deep: true,
    });

6.9 监听自定义样式变化,重绘词云图

    /**
     *  监听 cStyle 变化
     */
    watch(() => [props.cStyle], () => {
      nextTick(() => {
        myChart.resize();
      });
    },
    {
      deep: true,
    });

7. 使用示例

单词颜色随机生成,可设置词云形状(数据量大时才行)

<template>
  <chart-word-cloud
    :options="chartOptions"
    :c-style="{
      wrapper: {
        default: {
          height: '300px',
          width: '300px',
        }
      }
    }"
  >
  </chart-word-cloud>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from 'vue';
export default defineComponent({
  setup() {
    const state = reactive({
      chartOptions: {
        color: ['#0DE0E8', '#33B897'],
        series: [
          {
            data: [
              { name: 'Sam S Club', value: 1000 },
              { name: 'Macys', value: 6181 },
              { name: 'Amy Schumer', value: 4386 },
              { name: 'Jurassic World', value: 1001 }
            ]
          }
        ]
      }
    });
    return {
      ...toRefs(state),
    };
  },
});
</script>

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lyrelion

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

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

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

打赏作者

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

抵扣说明:

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

余额充值