Echarts-添加多个-label-与动态调整-position-的方案

49次阅读

共计 2617 个字符,预计需要花费 7 分钟才能阅读完成。

上周在实现一个 echarts 图表需求的时候总结了一些小技巧,目前网上没有看到到类似的方案,记录分享一下。

观察下面的图表:

首先这是一个柱状堆积图,每一条柱子有两部分堆积形成。介绍一下数据意义方便理解需求:

一条柱代表一个任务,左半边的长度代表完成任务人数的比例,右半边的长度代表未完成任务人数的比例,加起来必定是 100%,所以每条柱子都一样长占满整行。柱子内的数字为具体人数,最右侧百分比为完成人数的比例。

我们快速实现一个差不多的图表:

const myChart = echarts.init(document.getElementById('main'));

const option = {
  dataset: {
    source: [['任务名', '完成率', '未完成率', '完成人数', '未完成人数'],
      ['任务 1',  50,      50,       5,         5],
      ['任务 2',  4,       96,       2,         50]
    ]
  },
  yAxis: {type: 'category'},
  xAxis: {},
  grid: {containLabel: true},
  series: [
    {
      type: 'bar',
      stack: 'samestack',
      label: {
        show: true,
        position: 'insideRight',
        formatter: '{@[3]}'
      }
    },
    {
      type: 'bar',
      stack: 'samestack',
      label: {
        show: true,
        position: 'insideRight',
        formatter: '{@[4]}'
      }
    }
  ]
};

myChart.setOption(option);

看起来像是这样:

这个图表有两个问题:

  1. echart 中没有办法简单添加最右侧的百分比 label
  2. 左半边柱子在数量太小的时候没空间容纳数字

前面说了 echarts 没法设置多个 label,但它支持相当强大的富文本配置。对于第一个问题,我们可以通过富文本标签模拟一个额外的 label。首先,修改右半边柱子的 formatter,让完成率也显示在同一个 label 中。

[
      {// 左半边...},
      {
      type: 'bar',
      stack: 'samestack',
      label: {
              show: true,
        position: 'insideRight',
        formatter: '{people|{@[4]}} {percentage|{@[1]}%}',
        rich: {
            people: {color: 'white'},
          percentage: {color: 'red'}
        }
      }
    }
]

效果如下:

要把红色的百分比移出柱子外需要 label.distancerich.percentage.width 两个配置:

通过 width 给 percentage 这一个文本块一个固定的宽度,再给 distance 设置赋值配合 position: 'insideRight' 就可以让百分比的文本移出柱子外面。宽度设置为多少并不重要,因为文本是左对齐且没有超出裁剪,所以只要保持一致即可:

label: {
      show: true,
    position: 'insideRight',
    distance: -1,
    formatter: '{people|{@[4]}} {percentage|{@[1]}%}',
    rich: {
        people: {color: 'white'},
      percentage: {
              color: 'red',
        width: 1
      }
    }
  }

效果如下:

到这里第一个问题就解决了,可以继续细调以完全还原设计稿。

我们现在继续看一下另一个问题:如果柱子太窄,柱子内的文本会没有充足空间显示完。

以左半边柱子为例,为了让它在数值较小的情况下也能完全显示,我希望它在 20% 以下的时候显示在柱子外,20% 或以上的时候才显示在柱子内,如下图所示:

同样,这个功能也没有现成的,echarts 也不支持针对单个柱子动态改变 label.position 配置。但我们可以通过预先计算出内部、外部要显示的内容,并在 dataset 中增加额外字段的方式达到这个目的。首先可以先通过 js 为 dataset 扩展两个字段:

const options = {
    dataset: {
          source: [['任务名', '...', '已完成(内部)', '已完成(外部)'],
      ['任务 1',  '...', 50,            '看不见我'],
      ['任务 2',  '...', '看不见我',      2]
    ]
  },
  // ...
}

表中的 '看不见我' 仅为演示所用,实际使用中使用空字符串即可。

然后用解决第一个问题相同的方式,在一个 label 中同时显示“已完成(内部)”和“已完成(外部)”两个字段的内容,就可以完成这个需求(没这么简单):

[
      {
          // ...
      label: {
          // ...
        distance: -0,                                  // 3
        formatter: '{inside|{@[5]}}{outside|{@[6]}}',  // 1
        rich: {
            inside: {
                  color: 'white',
            width: 0,                                  // 3
            align: 'right'                             // 2
          },
          outside: {
                  color: 'red',
            width: 0,                                  // 3
            align: 'left'                              // 2
          }
        }
      },
      z: 4                                             // 4
    },
      {// 右半边柱子...}
]

我们给左半边柱子的 label 定义了两个富文本格式:insideoutside

  1. formatter 中同时显示 已完成(内部)已完成(外部)的内容,但总有其中一个是空字符串,以起到选择性渲染在柱子内部或外部的作用。
  2. inside 右对齐,文字变多时向左边生长,outside 相反
  3. 前面说过 width 具体数值不重要,设置成 0 也是没有问题的
  4. 由于左边柱子先渲染,会被右边盖住,所以提高 z 值让左边柱子的 label 高于右半边不被遮挡

得到的效果如下:

发生了什么。。。文本对齐的配置没有生效,全部变成居中挤在一起了。略经搜索之后了解到是 ZRender 的一个 bug 导致的。先不去深究,具体在这个例子中的表现是 formatter 中排前面的不能右对齐,排后面的不能左对齐。

那快速 hack 一下在 formatter 中把内外部渲染标签的顺序调换就好了。顺便把 dataset 中的 '看不见我' 改成 '' 以查看最终的效果。

{
    // ...
    // outside 放前面,inside 换到后面
    formatter: '{outside|{@[6]}}{inside|{@[5]}}',
    // ...
}

至此我们完美还原了设计稿,并且还优化了一个它未考虑到的边界条件。考虑到篇幅,还有一些旁枝末节的还原工作全都省略掉了,最终效果如下(请脑补最开头那张蓝色图表):

正文完
 0