Vue中使用Echarts---小记

发布时间 2023-07-31 16:46:22作者: 青柠i

Echarts使用小结

vue环境使用echarts

1. 柱状图

1.1 分析

当前需求:

  • 有横向滚动条;
  • 柱条的颜色为渐变色;
  • 鼠标悬浮自定义展示(展示数据百分比)

示例图:

柱状图的无法像饼状图可以获取每条数据的百分比,因此可能需要前端自己来计算(或后端直接返回百分比)。而按照普通方法:当前柱条数据除以总数据再进行小数位取舍,最后把各个柱条算出的百分比相加,往往达不到100%,一般是超了或者少了一点点。

因此,我们需要使用echarts中对百分比处理的方法来处理,echarts中使用“最大余额法”来计算。我们可以加以改造一下,以完成需求:

/**
 * 百分比算法--最大余额法
 * @param valueList 数值数组
 * @param idx 索引下标
 * @param precision 精确度
 */
function getPercentWithPrecision(valueList, idx, precision) {
  if (!valueList[idx]) {
    return 0;
  }

  const sum = valueList.reduce((acc, val) => acc + (isNaN(val) ? 0 : val), 0);
  if (sum === 0) {
    return 0;
  }

  const digits = Math.pow(10, precision);
  const votesPerQuota = valueList.map(
    (val) => ((isNaN(val) ? 0 : val) / sum) * digits * 100
  );
  const targetSeats = digits * 100;

  const seats = votesPerQuota.map((votes) => Math.floor(votes));
  let currentSum = seats.reduce((acc, val) => acc + val, 0);

  const remainder = votesPerQuota.map((votes, idx) => votes - seats[idx]);

  // Has remainding votes.
  while (currentSum < targetSeats) {
    // Find next largest remainder.
    let max = Number.NEGATIVE_INFINITY;
    let maxId = null;
    for (let i = 0, len = remainder.length; i < len; ++i) {
      if (remainder[i] > max) {
        max = remainder[i];
        maxId = i;
      }
    }

    // Add a vote to max remainder.
    ++seats[maxId];
    remainder[maxId] = 0;
    ++currentSum;
  }

  return seats[idx] / digits;
}

1.2 柱状图Option配置

// 模拟数据
const arr = [];
for (let i = 0; i < 20; i++) {
  arr.push({
    name: `指标值${i + 1}覆盖数`,
    value: Math.round(Math.random() * 10000)
  });
}
arr.push({
  name: '其他标签值覆盖数',
  value: Math.round(Math.random() * 10000)
});

// 配置
const option = {
  // 标题
  title: {
    subtext: '更新周期:天',
    right: 20
  },
  // 缩放(滚动条)
  dataZoom: {
    // brushSelect: false, // 是否开启刷选功能。brush 区域你可以按住鼠标左键后框选出选中部分。
    type: 'slider',
    height: 20,
    start: 0, // 范围开始(总数据的百分比)
    end: 35 // 结束位置(总数据的百分比)
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: { type: 'shadow' },
    // 自定义悬浮展示信息
    formatter: (params) => {
      const item = params[0];
      const valueArr = arr.map((i) => i.value);
      return `<div style="font-size: 13px;"><div>${item.name}</div><div>${
        item.value
      }(${this.getPercentWithPrecision(
        valueArr,
        item.dataIndex,
        2
      )}%)</div></div>`;
    }
  },
  xAxis: {
    data: arr.map((i) => i.name),
    // x轴保证刻度线和标签对齐
    axisTick: { alignWithLabel: true }
  },
  yAxis: {},
  series: [
    {
      type: 'bar',
      // barWidth: 30,
      barMinWidth: 20, // 柱条最小宽度
      barMaxWidth: 40, // 柱条最大宽度
      data: arr.map((i) => i.value),
      itemStyle: {
        // 柱条背景渐变色
        color: {
          type: 'linear',
          x: 0,
          y: 0,
          x2: 0,
          y2: 1,
          colorStops: [
            {
              offset: 0,
              color: '#4EAAFF' // 0% 处的颜色
            },
            {
              offset: 1,
              color: '#2864FE' // 100% 处的颜色
            }
          ],
          global: false // 缺省为 false
        }
      }
    }
  ]
};

2. 饼图

2.1 圆环图-1

2.1.1 分析

需求:

  • 饼图展示的数据过多,导致图例多,因此使用滚动图例;
  • 并且要求图例要展示值和百分比占比,这里同样需要使用上面提到的百分比-最大余额算法(getPercentWithPrecision)
  • 自定义悬浮提示;

示例图:

2.1.2 Option配置

// 模拟数据
const arr2 = [];
for (let i = 0; i < 20; i++) {
  arr2.push({
    name: `指标值${i + 1}覆盖数`,
    value: Math.round(Math.random() * 10000)
  });
}
// 饼图数据的值--数组
const valueArr = arr2.map((item) => item.value);

// 配置
const option = {
  // 自定义饼图背景色
  color: [
    '#5470c6',
    '#91cc75',
    '#fac858',
    '#ee6666',
    '#73c0de',
    '#3ba272',
    '#fc8452',
    '#9a60b4',
    '#ea7ccc'
  ],
  series: [
    {
      type: 'pie',
      radius: ['40%', '70%'], // 环形图范围
      labelLine: { show: false }, // 饼图引导线隐藏
      label: { show: false }, // 饼图引导线指向的文字隐藏
      left: '-40%', // 饼图向左位移(这块自己调试, 因为图例较长,可能会出现图例贴饼图过近)
      data: arr2,
      emphasis: {
        // 饼图某项高亮的样式调整
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }
  ],
  legend: {
    icon: 'circle', // 图例颜色图标为圆圈
    type: 'scroll', // 图例为滚动
    orient: 'vertical',
    right: 200,
    top: 20,
    bottom: 20,
    // 图例文字样式
    textStyle: {
      // 富文本样式
      rich: {
        a: { width: 120 },
        b: { width: 65 },
        c: { fontWeight: 'bold' }
      }
    },
    // 自定义图例,参数只有name,因此需要算法算出百分比
    formatter: (name) => {
      const val = arr2.find((item) => item.name === name);
      const valIndex = arr2.findIndex((item) => item.name === name);
      return [
        `{a|${name}}`,
        `{b|${this.getPercentWithPrecision(valueArr, valIndex, 2)}%}`,
        `{c|${val.value}}`
      ].join('');
    }
  },
  // 悬浮提示文本
  tooltip: {
    trigger: 'item',
    // 自定义展示文本
    formatter: (params) => `<div>
                    <span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>
                    <span style="font-size:12px;margin-left:2px">${params.name}</span>
                    <div style="font-size:12px;font-weight:bold;margin-top:7px;margin-left:20px">${params.percent}%(${params.value})</div>
                    </div>`
  }
};

2.2 圆环图-2

2.2.1 分析

需求:

  • 初始化默认选中一项,并高亮;
  • 饼图背景色为渐变色;
  • 选中某项,饼图中间展示选中的模块信息,并且悬浮文字自定义;
  • 图例自定义,展示数值

示例图:

2.2.2 Option配置

2.2.2.1 饼图配置
// 模拟数据
const arr = [
  { value: 44, name: '自定义标签' },
  { value: 137, name: '衍生标签' },
  { value: 54, name: '组合标签' }
];

// 配置
const option = {
  // 图例
  legend: {
    bottom: '6',
    left: 'center',
    icon: 'circle',
    itemGap: 30, // 图例间的间距
    // 自定义图例的样式--使用富文本
    textStyle: {
      rich: {
        a: { padding: [0, 12, 0, 0] },
        b: { fontWeight: 'bold' }
      }
    },
    // 自定义图例
    formatter: (name) => {
      const value = arr.find((i) => i.name === name).value;
      return [`{a|${name}}`, `{b|${value}}`].join('');
    }
  },
  // 悬浮提示信息
  tooltip: {
    trigger: 'item',
    // 自定义悬浮信息
    formatter: (params) => `<div>
                    <span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background:linear-gradient(180deg, ${params.color.colorStops[0].color} 0%, ${params.color.colorStops[1].color} 100%);"></span>
                    <span style="font-size:12px;margin-left:2px">${params.name}</span>
                    <div style="font-size:12px;font-weight:bold;margin-top:7px;margin-left:20px">${params.value}(${params.percent}%)</div>
                    </div>`
  },
  series: [
    {
      type: 'pie',
      radius: ['48%', '70%'],
      labelLine: { show: false },
      label: {
        show: false,
        position: 'center' // 显示位置展示在饼图中心
      },
      data: arr,
      itemStyle: {
        normal: {
          // 渐变色配置
          color: (params) => {
            const colorMap = {
              组合标签: {
                type: 'linear',
                x: 0,
                y: 0,
                x2: 0,
                y2: 1,
                colorStops: [
                  { offset: 0, color: '#FFAE34' },
                  { offset: 1, color: '#FFD234' }
                ]
              },
              衍生标签: {
                type: 'linear',
                x: 0,
                y: 0,
                x2: 0,
                y2: 1,
                colorStops: [
                  { offset: 0, color: '#F1672E' },
                  { offset: 1, color: '#FC946A' }
                ]
              },
              自定义标签: {
                type: 'linear',
                x: 0,
                y: 0,
                x2: 0,
                y2: 1,
                colorStops: [
                  { offset: 0, color: '#24F0CB' },
                  { offset: 1, color: '#04DEB6' }
                ]
              }
            };
            return colorMap[params.name];
          }
        }
      },
      // 饼图高亮时
      emphasis: {
        // 饼图样式
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        },
        // 文字展示,居中且样式更改
        label: {
          show: true,
          rich: {
            a: { fontSize: 22, padding: [10, 2], fontWeight: 'bold' },
            b: { fontSize: 12 }
          },
          formatter: (params) =>
            [
              `{a|${params.name}}`,
              `{b|${params.percent}%(${params.value})}`
            ].join('\n')
        }
      }
    }
  ]
};
2.2.2.2 初始化默认选中
// data中定义默认选中哪个
// data() {
//    currenthighlightTagNum: '自定义标签' 
// }

const myChart = this.echarts.init(this.$refs.xxxxx);
// 使用上面配置的option
myChart.setOption(option);

// 默认高亮某项
myChart.dispatchAction({
  type: 'highlight',
  name: this.currenthighlightTagNum
});

// 鼠标移入,判断是否是已经选中了的,如果是则取消高亮
myChart.on('mouseover', (e) => {
  if (e.name !== this.currenthighlightTagNum) {
    myChart.dispatchAction({
      type: 'downplay',
      name: this.currenthighlightTagNum
    });
  }
});

//当鼠标离开时,把当前项置为选中
myChart.on('mouseout', (e) => {
  // 记录当前高亮
  this.currenthighlightTagNum = e.name;
  myChart.dispatchAction({ type: 'highlight', name: e.name });
});

3. 堆叠柱状图

3.1 分析

需求:

  • 自定义悬浮信息,包含百分比信息;
  • 有横向滚动条;

实例图:

3.2 Option配置

// 数据模拟
const arr1 = [];
const arr2 = [];
const arr3 = [];
//根据某年某月计算出具体日期
function getDaysInMonth(year, month) {
  const daysOfMonth = [];
  month = parseInt(month, 10);
  const lastDayOfMonth = new Date(year, month, 0).getDate();
  for (let i = 1; i <= lastDayOfMonth; i++) {
    if (i < 10) {
      daysOfMonth.push(`${year}-${month}-0${i}`);
    } else {
      daysOfMonth.push(`${year}-${month}-${i}`);
    }
    arr1.push(Math.round(Math.random() * 3000) + 1000);
    arr2.push(Math.round(Math.random() * 1000));
    arr3.push(Math.round(Math.random() * 1000) + 500);
  }
  return daysOfMonth;
}
// 模拟日期
const dateArr = getDaysInMonth(2023, 7);

// 配置
const option = {
  // 图例
  legend: {
    top: 10,
    icon: 'circle'
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: { type: 'shadow' },
    // 自定义悬浮信息
    formatter: (params) => {
      const valueArr = params.map((i) => i.value);
      let str = `<div><div>${params[0].name}</div>`;
      for (let i = 0; i < params.length; i++) {
        const item = params[i];
        str += `<div><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${
          item.color
        };"></span>${
          item.seriesName
        }</div><div style="margin-left:15px;margin-bottom:4px;">${
          item.value
        }(${this.getPercentWithPrecision(valueArr, i, 2)}%)</div>`;
      }
      str += '</div>';
      return str;
    }
  },
  // x轴保证刻度线和标签对齐
  xAxis: { data: dateArr, axisTick: { alignWithLabel: true } },
  yAxis: {},
  // 滚动条设置
  dataZoom: {
    type: 'slider',
    // zoomLock: true,
    height: 20,
    start: 0,
    end: 35
  },
  series: [
    {
      barMinWidth: 20,
      barMaxWidth: 40,
      name: '指标一',
      data: arr1,
      type: 'bar',
      itemStyle: { color: '#04DEB6' },
      emphasis: { focus: 'series' },
      stack: '指标分布' // 堆叠组
    },
    {
      name: '指标二',
      data: arr2,
      type: 'bar',
      itemStyle: { color: '#1890FF' },
      emphasis: { focus: 'series' },
      stack: '指标分布'
    },
    {
      name: '指标三',
      data: arr3,
      type: 'bar',
      itemStyle: { color: '#F1672E' },
      emphasis: { focus: 'series' },
      stack: '指标分布'
    }
  ]
};

4. 折线图

4.1 分析

需求:

  • 自定义悬浮信息;
  • 有横向滚动条;
  • 折线面积图(设置渐变背景色)

实例图:

4.2 Option配置

// 模拟数据
const arr = [];
const lastMonth = [];
for (var i = 0; i < 30; i++) {
  const date = new Date(new Date().setDate(new Date().getDate() - i));
  const month =
    date.getMonth() + 1 < 9 ? `0${date.getMonth() + 1}` : date.getMonth() + 1;
  const day = date.getDate() < 9 ? `0${date.getDate()}` : date.getDate();
  lastMonth.unshift(`${month}-${day}`);
  arr.push(Math.round(Math.random() * 40) + 10);
}

// 配置
const option = {
  xAxis: {
    boundaryGap: false, // 起始点设在Y轴
    data: lastMonth
  },
  yAxis: {},
  // 自定义展示悬浮信息
  tooltip: {
    trigger: 'axis',
    formatter: '{a}<br />{b}: {c}个',
    axisPointer: {
      type: 'line',
      label: { backgroundColor: '#6a7985' },
      lineStyle: { type: 'dashed' }
    }
  },
  // 横向滚动条
  dataZoom: {
    type: 'slider',
    height: 20,
    start: 0,
    end: 35
  },
  series: [
    {
      name: '已发布标签',
      data: arr,
      type: 'line',
      // 线条颜色设置颜色
      lineStyle: {
        color: '#13C2C2',
        width: 3
      },
      // 线条中的节点设置颜色
      itemStyle: { color: '#13C2C2' },
      // 面积背景渐变色
      areaStyle: {
        color: new this.echarts.graphic.LinearGradient(0, 0, 0, 1, [
          {
            offset: 0,
            color: 'rgba(19,194,194, 0.3)'
          },
          {
            offset: 1,
            color: 'rgba(19,194,194,0)'
          }
        ])
      }
    }
  ]
};

5. 矩形树图

5.1 分析

需求:

  • 默认展示第一级内容,点击可下钻到下一级
  • 以面积来表示对应值的多少

示例图:

一级 二级

5.2 Option配置

// 模拟数据
const colorList0 = ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'];
const colorList1 = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
const colorList2 = ['#dd6b66', '#759aa0', '#e69d87', '#8dc1a9', '#ea7e53', '#eedd78', '#73a373', '#73b9bc', '#7289ab', '#91ca8c', '#f49f42'];
const colorList3 = ['#37A2DA', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF'];
const dataArr = [
  {
    name: '集团',
    value: 50,
    // color,// 当前节点的下级目录下使用的颜色
    children: [
      {
        name: '统一消费者业务',
        value: 30,
        // color: colorList1,// 当前节点的下级目录下使用的颜色
        children: [
          { name: '统一', value: 10 },
          { name: '消费者', value: 6 },
          {
            name: '业务',
            value: 14,
            children: [
              { name: '陈平安', value: 9 },
              { name: '剑来', value: 4 },
              { name: '春风', value: 1 }
            ]
          }
        ]
      },
      {
        name: '国窖会员',
        value: 4
      },
      {
        name: '二级目录1',
        value: 4
      },
      {
        name: '二级目录2',
        value: 4
      },
      {
        name: '沪系会员',
        value: 8
      }
    ]
  }
];

// option配置
const option = {
  series: [
    {
      name: '标签类目分布', // 顶层名称
      type: 'treemap', // 矩形树图
      leafDepth: 1,
      roam: true, // 可缩放可位移
      top: 25,
      breadcrumb: { bottom: 10 }, // 顶部面包屑(层级关系)
      itemStyle: { normal: { gapWidth: 5 } }, // 矩形间的间隙宽度
      // 直接定义每一层次的颜色使用
      levels: [
        { color: colorList0 },
        { color: colorList1 },
        { color: colorList2 },
        { color: colorList3 }
      ],
      data: dataArr
    }
  ]
};