美文网首页
echarts 实现复杂的气泡图,有标记线,y轴可不展示 x轴刻

echarts 实现复杂的气泡图,有标记线,y轴可不展示 x轴刻

作者: 南土酱 | 来源:发表于2024-11-26 10:30 被阅读0次

本文不适合新手观看(很费时间)。本文初心也只是 给自己的气泡图研发做个总结并且分享代码。 文章不清晰之处还请谅解,不懂的可提问。

\color{#228B22}{前端总结,不对之处,欢迎质疑。可以的话顺手点个赞吧~~!}
\color{red}{警: 禁止抄袭,转载说明出处 🤨}

本文也对鼠标置顶的提示框 tooltip 进行了自定义。使用 rich 富文本去实现。
效果图.jpg

本文重点是 根据时间戳来生成对应时间上的 气泡数据。区分 series 项。重点也是 series 这个配置

首先是基本配置。

computed: {
    // 基本配置
    baseConfig: function () {
      return {
        // title: {
        //   text: "当前剩余在翼循环:1234   剩余飞行小时:3456",
        //   textStyle: {
        //     fontSize: 10,
        //   },
        // },
        legend: {
          right: "2%",
          itemWidth: 8,
          textStyle: { fontSize: 10 },
        },
        grid: {
          left: 30,
          bottom: 30,
          top: 25,
          right: 30,
          containLabel: true,
        },
        yAxis: {
          type: "value",
          interval: 0.5,
          data: Array.from({ length: this.yAxisCount }, (_, i) => i),
          max: this.yAxisCount,
          min: 0,
          splitLine: {
            show: true,
            lineStyle: {
              color: this.yAxisLineStyle,
              width: 1,
            },
          },
          axisLabel: { show: false },
          axisTick: { show: false },
          axisLine: { show: false },
        },
      };
    },
}

视觉上 y轴的 顶部和 底部线条不需要展示。这里采用 白色颜色 和 背景颜色来达到隐藏效果

//设置 y轴线的颜色来达到视觉效果
    yAxisLineStyle: function() {
      if (this.yAxisCount === 2) {
        return ["#fff", "#fff", "#F1F1F1", "#fff", "#fff"];
      }
      if (this.yAxisCount === 3) {
        return ["#fff", "#fff", "#F1F1F1", "#fff", "#fff", "#F1F1F1", "#fff"];
      }
    },

此时气泡图的数据 和 y轴刻度无关。 只是为了区分 不同项的数据。用 不同y值来分割开。故而 使用 yaxis.interval = 0.5 。并且设置 min = 0 ,max 为 数据项的数量。
data 的数据声明如下。不需要太复杂:

  data() {
    return {
    //总共需要几条y轴
      yAxisCount: 0,
      //x轴跨度最大最小 时间戳
      minTime: 0,
      maxTime: 0,
    };
  },

另外的 prop , 这里是重点。 其中的 seriesItemList 就是你要传入的 气泡数据集 。 参数配置看如下。
markLineList 就是标记线 , 你可以自定义标记线。 本图的 标记线是 需求所需。位置也是 基于 时间戳即可。

props: {
    /**
     * @params seriesItemList
     * name 数据集名称
     * color 数据气泡颜色以及 toopTip颜色
     * yAxisValue y轴的值 (传 1 或 2)
     * data 数据集 Array类型
     *  [ x轴时间戳, 气泡值大小 , toolTips 提示框]
     *  toolTips : { 
     *    label : 数据项名称
     *    value : 数据值
     * }
     * 
     * Example : 
     * {
     *  name: "计划维修费",
     *  yAxisValue: 1,
        color: "red",
        data: [
          [ 
            1597470795000, 
            2396973,
            {
            title: "第三次送修",
            tipItem: [
              { label: "TSN", value: 123456 },
              { label: "csn", value: 12345 },
              { label: "总成本", value: 1054654 },
              ]
            }
          ]
        ]
      }
    */
    seriesItemList: {
      type: Array,
      default: () => [],
    },
    /**
     * @params markLineList
     * name 标记线名称
     * value x轴的值 时间戳
     * Example :
     * {
     *    name: "预计处置日期",
          value: 1728986303000
        }
     */
    markLineList: {
      type: Array,
      default: () => [],
    },
    //必须设定气泡图的时间跨度,限制图表显示范围。
    /**
     * @timeSpan
     * [ 起始时间戳 , 结束时间戳]
     */
    timeSpan: {
      type: Array,
      default: () => [],
    },
  },

标记线的 配置也是 额外的方法去构建。 如下::

 generatorMarkLineItem(lineList) {
      const mlList = lineList.map(item => ({
        xAxis: item.value,
        label: {
          show: true,
          formatter: params =>
            this.defineMarkLineContent(item.name, params.value),
          rich: {
            a: { color: "grey", padding: [24, 0, 2, 0] },
            b: { color: "grey" },
          },
          position: "start",
        },
        symbol: "none",
        lineStyle: { type: "dotted", color: "#D9D9D9" },
      }));
      mlList.push({
        xAxis: this.getTodayTime(),
        name: "",
        label: {
          show: true,
          formatter: params => this.formatDay(params.value),
          fontSize: 15,
          padding: [0, 0, 5, 0],
        },
        symbol: "none",
        lineStyle: { type: "dotted", color: "#178ED3" },
      });
      return mlList;
    },

有些标记线可能需要标记当天。故而最后用了 push 去 添加一个当天的。可根据需求删除。
标记线鼠标置顶会展示label 数据。本文用的 富文本 rich 去自定义。不做介绍。
标记线构建完毕需要设置到一个 series 项里边。不然不会生效。本文采用添加一个空的 series 防止被干扰。
在本需求中, x 轴需要设置为 value。并且时间间隔是 2年, 故而 interval 是 两年。
echarts 会去自动计算你的 x轴时间的起始和结束,故而一定要设置 max 和min

  setEchartOption(seriesConfig, markLine) {
      // 这个push一定要的,防止标记线被关闭
      //把标记线放在最后一个
      seriesConfig.push({
        name: "",
        type: "scatter",
        data: [],
        markLine: {
          symbol: "diamond",
          symbolSize: "8",
          silent: true,
          data: markLine,
        },
      });

      return {
        ...this.baseConfig,
        xAxis: {
          type: "value",
          // 预期的刻度值是每两年的 1 月
          // 如果设置为365天,则会出现前一年12月的刻度值容易误解
          // 但是设置为366天,也会出现 2 月的刻度值,综合考虑,设置为366天
          interval: 24 * 60 * 60 * 366 * 2 * 1000,
          min: this.minTime,
          max: this.maxTime,
          axisLabel: {
            formatter: val => {
              return this.formatYear(val);
            },
            margin: 15,
            color: "black",
          },
          axisLine: { show: false },
          axisTick: { show: false },
        },

        tooltip: {
          trigger: "item",
          axisPointer: {
            type: "line",
          },
          formatter: params => this.defineTooltipContent(params),
          borderWidth: 1,
          borderColor: "white",
          padding: 0,
        },
        series: seriesConfig,
      };
    },

整个组件源代码如下。 需要哪些配置项可自取。也可立即使用。

<template>
  <div class="">
    <div ref="main" style="width: 100%; height: 210px"></div>
  </div>
</template>
<script>
export default {
  name: "engineRepairPlanCharts",
  props: {
    /**
     * @params seriesItemList
     * name 数据集名称
     * color 数据气泡颜色以及 toopTip颜色
     * yAxisValue y轴的值 (传 1 或 2)
     * data 数据集 Array类型
     *  [ x轴时间戳, 气泡值大小 , toolTips 提示框]
     *  toolTips : { 
     *    label : 数据项名称
     *    value : 数据值
     * }
     * 
     * Example : 
     * {
     *  name: "计划维修费",
     *  yAxisValue: 1,
        color: "red",
        data: [
          [ 
            1597470795000, 
            2396973,
            {
            title: "第三次送修",
            tipItem: [
              { label: "TSN", value: 123456 },
              { label: "csn", value: 12345 },
              { label: "总成本", value: 1054654 },
              ]
            }
          ]
        ]
      }
    */
    seriesItemList: {
      type: Array,
      default: () => [],
    },
    /**
     * @params markLineList
     * name 标记线名称
     * value x轴的值 时间戳
     * Example :
     * {
     *    name: "预计处置日期",
          value: 1728986303000
        }
     */
    markLineList: {
      type: Array,
      default: () => [],
    },
    //必须设定气泡图的时间跨度,限制图表显示范围。
    /**
     * @timeSpan
     * [ 起始时间戳 , 结束时间戳]
     */
    timeSpan: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      //总共需要几条y轴
      yAxisCount: 0,
      //x轴跨度最大最小 时间戳
      minTime: 0,
      maxTime: 0,
    };
  },
  watch: {
    seriesItemList: {
      handler(newVal) {
        if (newVal.length <= 0) return;
        this.generatorTimeBetweenList();
        this.$nextTick(() => {
          this.initChart();
        });
      },
      deep: true,
      immediate: true,
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.initChart();
    });
  },
  computed: {
    // 基本配置
    baseConfig: function() {
      return {
        legend: {
          right: "2%",
          itemWidth: 30,
          textStyle: { fontSize: 16 },
        },
        grid: {
          left: 30,
          bottom: 30,
          top: 45,
          right: 30,
          containLabel: true,
        },
        yAxis: {
          type: "value",
          interval: 0.5,
          max: this.yAxisCount,
          min: 0,
          splitLine: {
            show: true,
            lineStyle: {
              // 通过 yAxisLineStyle 来设置 y轴线的颜色,与背景色一致时则展示出隐藏效果
              color: this.yAxisLineStyle,
              width: 1,
            },
          },
          axisLabel: { show: false },
          axisTick: { show: false },
          axisLine: { show: false },
        },
      };
    },
    //设置 y轴线的颜色来达到视觉效果
    yAxisLineStyle: function() {
      if (this.yAxisCount === 2) {
        return ["#fff", "#fff", "#F1F1F1", "#fff", "#fff"];
      }
      if (this.yAxisCount === 3) {
        return ["#fff", "#fff", "#F1F1F1", "#fff", "#fff", "#F1F1F1", "#fff"];
      }
    },
  },
  methods: {
    initChart() {
      // 清空图表
      if (this.seriesItemList.length <= 0) return;

      //此处的 prop是  this.markLineList
      const marklinelist = this.markLineList;
      // 此处的 test 的 prop 是 this.seriesItemList
      const series = this.seriesItemList.map(item =>
        this.generatorSeriesItem(item)
      );

      const markLine = this.generatorMarkLineItem(marklinelist);

      // 初始化图表
      const myChart = this.$echarts.init(this.$refs.main);

      const option = this.setEchartOption( series, markLine);

      console.log('options', option)
      // TODO 会出现历史数据,需要清空图表,原因待查
      myChart.clear();
      myChart.setOption(option);
      window.addEventListener("resize", () => {
        myChart.resize();
      });
    },
    // 设置图表配置
    setEchartOption(seriesConfig, markLine) {
      // 这个push一定要的,防止标记线被关闭
      //把标记线放在最后一个
      seriesConfig.push({
        name: "",
        type: "scatter",
        data: [],
        markLine: {
          symbol: "diamond",
          symbolSize: "8",
          silent: true,
          data: markLine,
        },
      });

      return {
        ...this.baseConfig,
        xAxis: {
          type: "value",
          // 预期的刻度值是每两年的 1 月
          // 如果设置为365天,则会出现前一年12月的刻度值容易误解
          // 但是设置为366天,也会出现 2 月的刻度值,综合考虑,设置为366天
          interval: 24 * 60 * 60 * 366 * 2 * 1000,
          min: this.minTime,
          max: this.maxTime,
          axisLabel: {
            formatter: val => {
              return this.formatYear(val);
            },
            margin: 15,
            color: "black",
          },
          axisLine: { show: false },
          axisTick: { show: false },
        },

        tooltip: {
          trigger: "item",
          axisPointer: {
            type: "line",
          },
          formatter: params => this.defineTooltipContent(params),
          borderWidth: 1,
          borderColor: "white",
          padding: 0,
        },

        series: seriesConfig,
      };
    },
    //生成 markLineItem
    generatorMarkLineItem(lineList) {
      const mlList = lineList.map(item => ({
        xAxis: item.value,
        label: {
          show: true,
          formatter: params =>
            this.defineMarkLineContent(item.name, params.value),
          rich: {
            a: { color: "grey", padding: [24, 0, 2, 0] },
            b: { color: "grey" },
          },
          position: "start",
        },
        symbol: "none",
        lineStyle: { type: "dotted", color: "#D9D9D9" },
      }));
      mlList.push({
        xAxis: this.getTodayTime(),
        name: "",
        label: {
          show: true,
          formatter: params => this.formatDay(params.value),
          fontSize: 15,
          padding: [0, 0, 5, 0],
        },
        symbol: "none",
        lineStyle: { type: "dotted", color: "#178ED3" },
      });
      return mlList;
    },
    //生成 series 项
    generatorSeriesItem(item) {
      const maxYaxis = item.yAxisValue + 1;
      const data = item.data.map(row => [
        row[0],
        item.yAxisValue >= 2 ? item.yAxisValue + 0.5 : item.yAxisValue,
        row[1],
        row[2],
      ]);
      if (item.data[0]) {
        const firstCopyData = item.data[0];
        //添加一个不存在的值 去支撑图表的y轴高度
        data.push([firstCopyData[0], maxYaxis, ""]);
      }
      if (this.yAxisCount < maxYaxis) {
        this.yAxisCount = maxYaxis;
      }
      return {
        name: item.name,
        type: "scatter",
        symbolSize: val => this.symbolSizeComputer(val[2]),
        data,
        label: {
          formatter: params => params.value[2],
          position: "bottom",
          show: true,
        },
        itemStyle: {
          color: item.color,
        },
      };
    },
    //获取数据对应的配置
    getToolTipConfig(name, color) {
      let bottomFontColor = "#fff";
      let titleShow = true;
      let dateDayShow = true;
      if (name === "基准维修费") {
        bottomFontColor = "#000";
      } else if (name === "使用费") {
        bottomFontColor = "#000";
        titleShow = false;
        dateDayShow = false;
      }
      return {
        color,
        bottomFontColor,
        titleShow,
        dateDayShow,
      };
    },
    //气泡的大小
    symbolSizeComputer(data) {
      return Math.sqrt(data || 0) / 1e2;
    },
    //获取当天的时间戳
    getTodayTime() {
      const now = new Date();
      return new Date(
        `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`
      ).getTime();
    },
    //生成 x轴的 区间值
    generatorTimeBetweenList() {
      //使用传入参数的时间
      let startTime = this.getNewYearTimestamp(this.timeSpan[0]);
      const endTime = this.getNewYearTimestamp(this.timeSpan[1]);
      const realEndTime = new Date(endTime);
      realEndTime.setFullYear(endTime.getFullYear() + 1);
      this.minTime = startTime.getTime();
      this.maxTime = realEndTime.getTime();
    },
    // 格式化日期 只显示年
    formatYear(time) {
      var date = new Date(time);
      var y = 1900 + date.getYear();
      return y;
    },
    //格式化日期 年月日
    formatDay(time) {
      var date = new Date(time);
      var y = 1900 + date.getYear();
      var m = "0" + (date.getMonth() + 1);
      var d = "0" + date.getDate();
      return `${y}-${m.substring(m.length - 2, m.length)}-${d.substring(
        d.length - 2,
        d.length
      )}`;
    },
    getNewYearTimestamp(time) {
      const year = new Date(time);
      year.setMonth(0); // 设置月份为1月(0表示1月)
      year.setDate(1); // 设置日期为1日
      year.setHours(0, 0, 0, 0); // 设置时间为0时0分0秒0毫秒
      return year;
    },
    //定义 提示组件内容
    defineTooltipContent(params) {
      const config = this.getToolTipConfig(params.seriesName, params.color);
      const date = config.dateDayShow
        ? this.formatDay(params.data[0])
        : this.formatYear(params.data[0]);
      const tipData = params.data[3];
      if (!tipData) return `<div class="tip-item">${date}</div>`;

      const tipItem = tipData.tipItem;
      let child = "";
      if (tipItem && tipItem.length > 0) {
        child = tipItem.reduce((pre, cur, idx) => {
          let bg = "";
          if (idx === tipItem.length - 1) {
            bg = `<div class="tip-bottom tip-item" style="background-color:${config.color};color:${config.bottomFontColor}"><div>`;
          } else {
            bg = `<div class="tip-item"> <div ><span style='color: ${config.color}'>♦ </span>`;
          }
          return (pre += `${bg}${cur.label}</div><div>${cur.value}</div></div>`);
        }, "");
      }

      return (
        `<div class="tip-item"><div>${date}</div><div>${
          config.titleShow ? tipData.title : ""
        }</div></div>` + child
      );
    },
    //定义标线的显示内容
    defineMarkLineContent(label, date) {
      return [`{a|${label}}`, `{b|${this.formatDay(date)}}`].join("\n");
    },
  },
};
</script>
<style lang="scss" scoped>
/deep/ .tip-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  padding: 2px 7px;

  > div:last-of-type {
    text-align: right;
    padding-left: 20px;
  }
}

/deep/ .tip-bottom {
  color: white;
  border-radius: 0 0 5px 5px;
  padding: 4px 7px;
}

.engine-repair-plan-charts {
  margin-left: -5px;
  margin-right: -2px;
}
</style>

相关文章

网友评论

      本文标题:echarts 实现复杂的气泡图,有标记线,y轴可不展示 x轴刻

      本文链接:https://www.haomeiwen.com/subject/jybfsjtx.html