Echarts Label 过长展示省略号

最近在使用 Echarts 完成一个漏斗图的需求,为了达到视觉的要求,过程中是用了一些 Hack 的方式,在这里总结一下。

效果如图:

需要解决4个问题:

  • Label 过长展示省略号
  • 中间绿色百分比的 Tag 需要动态固定在两个漏斗图之间的间隙中
  • 漏斗图和坐标系相结合
  • 漏斗图的数值大小应对坐标系 x 轴的长度,所以漏斗数值越大,横向需要越宽,二者是对应的
  • 由于具有多个漏斗,每个漏斗的颜色不一样,按照顺序从上至下颜色变淡
  • x 轴 Label 数值过大被挤在一起

首先是配置项,通过在线的示例配出,坐标轴和漏斗图

const globalOptions = {
    xAxis: {
        type: 'value',
        splitLine: {
          show: true,
          lineStyle: {
            type: 'dotted'  // 控制虚线
          },
          color: '#D8D8D8',
        },
      splitNumber: 3, // 控制 x  Label 过长挤在一起的情况,Echarts 会根据此值做适配,并非固定的
      axisLine: {
          show: true,
          lineStyle: {
            color: 'rgba(42, 51, 59, 0.1)'
          }
        },
        axisLabel: {
          color: 'rgba(42, 51, 59, .5)',
          interval: 1
        },
      },
    yAxis: [
      {
        splitLine: false,
        axisLine: {
          lineStyle: {
            color: 'rgba(42, 51, 59, 0.1)'
          }
        },
        axisLabel: {
          show: false
        },
        axisTick: {
          show: false
        }
      }
    ],
    grid: {	// 设置漏斗图图的位置
      left: 140,
      top: 50,
      right: 50,
      bottom: 20,
      y: 10
    },
    // 默认一种颜色
    color: ['rgba(63, 99, 245, 1)', 'rgba(63, 99, 245, 0.75)', 'rgba(63, 99, 245, 0.625)', 'rgba(63, 99, 245, 0.4)', 'rgba(63, 99, 245, 0.5)', 'rgba(63, 99, 245, 0.375)', 'rgba(63, 99, 245, 0.25)', 'rgba(63, 99, 245, 0.125)'],
    series: [
      {
        name: '漏斗图',
        type: 'funnel',
        width: 'calc(100% - 100)',
        left: 140,
        top: 80,
        bottom: '10%',
        funnelAlign: 'left', // 漏斗图样式
        gap: 1, // 漏斗间隙 1px
        sort: 'none',
        label: {
          color: '#2A333B',
          position: 'left',
          verticalAlign: 'bottom',
          formatter: [
            '{a|{c}}',
            '{b|{b}}',
          ].join('\n'),
          rich: {
            a: {
              fontSize: 14,
              align: 'right',
              color: 'rgba(42, 51, 59, .85)',
              lineHeight: 30,

            },
            b: {
              align: 'right',
              fontSize: 14,
              color: 'rgba(42, 51, 59, .50)',

            }
          }
        },
        labelLine: { // 标签的视觉引导线样式
          normal: {
            show: false, // 是否显示引导线
          }
        },
        data: [],
      },
      {
        name: 'line',
        type: 'line',
        top: '5%',
        symbolSize: 0, // symbol的大小设置为0
        showSymbol: false, // 不显示symbol
        lineStyle: {
          width: 0, // 线宽是0
          color: 'rgba(0, 0, 0, 0)' // 线的颜色是透明的
        },
        data: []
      }
    ]
  }
  1. 由于 Echarts 的配置项传入的值可以是百分比或者数值,并且直接对应 Css 的规则,所以,假设传入 left: 100 就代表 left: 100px,通过这个特征,我们就可以动态的计算出绿色标签的位置永远处于两个漏斗的中间。
  2. 漏斗图的每一块高度都是相同的

如何计算标签 top 的位置(这里采用 absolute 定位, 标签的 left 可以通过 css 计算)

const echartsPoint = [{
    top: 0
}]

// 计算出每个漏斗块的高度
const each = domHeight / len;

// 标签的个数比漏斗的数据少一个
const len = echartsPoint.length;

echartsPoint.map((p, idx) => {
     
    if (idx < len - 1) {

      // 漏斗之间的间隔为 1px 故取 0.5  12 为每个 tag 的高度
      p.top = `${(idx + 1) * each + (.5 * idx) - 12}px`
    }
})

如何处理漏斗图 Label 过长的问题

上述的问题,除了 Tag 使用 DOM 去模拟外,还有漏斗图 Label 过长的问题,其余的都是可以通过配置项解决。

首先漏斗图的宽度是不能变的,所以左侧的 Label 只能采用超出使用…的方式

// 切割文字
formatter: function (params, index) {
    // 超出省略
    params = params.toString();
    var maxlength= 8;
    if (params.length>maxlength) {
        return params.substring(0, maxlength-1)+'...';
    } else{
        return params;
    }
}

添加个 DOM,采用 absolute 定位,初始时隐藏,Echarts 实例可以监听 mousemove 事件,鼠标移入时展示完整的 Label,mouseout 时隐藏

chartInstance.on('mousemove', (params) => {
  console.log(params);
  if (params.componentSubType === 'funnel') {

    this.$refs.labelTip[0].style.left = params.event.event.layerX+10 + 'px'
    this.$refs.labelTip[0].style.top = params.event.event.layerY+20 + 'px'
    this.$refs.labelTip[0].style.display = 'block'
    this.$refs.labelTip[0].style.position = 'absolute'
    this.$refs.labelTip[0].innerText= params.name
  }
})
chartInstance.on('mouseout', (params) => {
  this.$refs.labelTip[0].style.display = 'none'
})

最终效果