echarts实现立体双柱图

95 阅读5分钟

记录一下最近工作遇到的echart统计图,这个效果在官方示例那边找不到,所以记录起来,如果大家遇到有类似的需求可以参考一下。

需要的立体双柱图的效果: image.png

点击查看echart配置链接

难点及解决方法
1. 怎么实现这里立体效果?

可以用多个效果来叠加实现,中间的柱子就用柱状图的效果{type:bar}实现,上面和下面的椭圆形用象形柱图的效果{type:pictorialBar}实现

2. 立体效果实现了,这个背景的立体要怎么实现?

背景的立体我使用的是设置柱状图的背景,然后在再加一个最上面的椭圆来实现;

但是这里还有一个问题,这个椭圆对应在统计图上的值我要怎么拿到(我的柱状图没有设置最大值,所以这个y轴的刻度是echart自动生成的数据),这个问题只能是分成2步走,先生成统计图,在获取统计图的y轴的最大值,然后再重新生成统计图;

获取y轴最大值:chart.getModel().getComponent('yAxis').axis.scale._extent[1];

3. 只有一个柱子立体好搞,2个柱子的要怎么弄?

用象形柱图实现椭圆的最大问题是,你配置设置symbolOffset(图形相对于原本位置的偏移)为[0,椭圆高度的一半]的时候,位置是在所在的柱子的最中间的,就是以下的效果,所以设置symbolOffset的x轴的位置才是关键

image.png

那要怎么计算呢,那就得知道几个概念:

  • 一个柱状图所占的width是100%
  • barGap:不同系列的柱间距离,为百分比(如 '20%',表示柱子宽度的 20%)。

有了这个x轴的位移就很好算了,假设我设置柱状图的barGap为'50%',那么最中间的位置是0,第一个柱子的椭圆(椭圆的中心点的位置也是0,以这个中心点来看椭圆的位移)是不是得往前移动(就用负数 - )barGap的一半('25%')和柱状图宽度的一半('50%'),也就是说我第一个柱状图的symbolOffset的x轴的位置得设置成'75%'

实现

由于得生成2遍统计图,所以最好是将配置存起来,或者弄成一个方法可以多次调用,我的实现就是封装成getOption的方法:

  • 其中xAxisData给的是y轴的值,类似于['2025-03',''2025-04',''2025-05']

  • seriesData1是第一个柱状图的数据,类似于[1,41,0]这样子

  • seriesData2是第二个柱状图的数据,类似于[1,41,0]这样子

  • 然后代码里面的vwToPx,和vhToPx不用管,你们可以直接设置成对应的px值,这个是我为了大屏页面才做的特殊处理,这么做就是为了统计图的字体大小之类的px值可以根据浏览器的窗口按照一定的比例进行缩小放大,我的代码里面设计图是1920*1080,所以可以看到我给宽度就是vwToPx(设计图的对应的px / 1920 * 100),给高度就是vhToPx(设计图的对应的px / 1080 * 100)

// 1. 定义 vw 转 px 的函数(1vw = 视窗宽度的 1%)
function vwToPx(vwValue) {
  return (window.innerWidth * vwValue) / 100;
}

// 2. 定义 vh 转 px 的函数(如需用vh,直接调用此函数)
function vhToPx(vhValue) {
  return (window.innerHeight * vhValue) / 100;
}


initSafetyManageChart(xAxisData, seriesData1, seriesData2) {
      const chart = echarts.init(this.$refs.safetyManageChart);
      const getOption = (maxY) => {
        const option = {
          backgroundColor: "transparent",
          legend: {
            icon: "roundRect",
            right: vwToPx(20 / 1920 * 100),
            itemWidth: vwToPx(14 / 1920 * 100),
            itemHeight: vhToPx(8 / 1080 * 100),
            selectedMode: false, // 禁止图例交互(隐藏某个图例柱状图上方和下方的圆环截面都会偏移)
            data: [{
              name: '问题总数',
              itemStyle: {
                color: '#00FFAE'
              }
            }, {
              name: '已处理数',
              itemStyle: {
                color: '#00C6FF'
              }

            }],
            textStyle: {
              color: '#FFFFFF',
              fontSize: vwToPx((14 / 1920) * 100)
            }

          },
          //提示框
          tooltip: {
            trigger: "axis",
            axisPointer: {
              type: "shadow",
            },
            formatter: function (param) {
              return `${param[0].name}<br />${param[0].marker}${param[0].seriesName} ${param[0].data}个<br />${param[1].marker}${param[1].seriesName} ${param[1].data}个`;
            },
          },
          title: {
            textAlign: 'left',
            top: vhToPx((10 / 1080) * 100),
            left: vwToPx((10 / 1920) * 100),
            text: '单位(个)',
            textStyle: {
              color: '#D6EAFF',
              fontSize: vwToPx((12 / 1920) * 100)
            },
          },

          grid: {
            top: vhToPx(50 / 1080 * 100),
            left: vwToPx(19 / 1920 * 100),
            bottom: vhToPx(6 / 1080 * 100),
            right: vwToPx(20 / 1920 * 100),
            containLabel: true,
          },
          animation: false,
          xAxis: [
            {
              type: "category",
              data: xAxisData,
              axisLabel: {
                color: "#D4EAFF",
                fontSize: vwToPx((14 / 1920) * 100),
              },
              axisTick: {
                show: false,
              },
              axisLine: {
                lineStyle: {
                  color: "#11446F",
                },
              },
            },
          ],
          yAxis: [
            {
              show: true,
              type: "value",
              axisLabel: {
                color: "#D4EAFF",
                fontSize: vwToPx((14 / 1920) * 100),
                margin: vwToPx((6 / 1920) * 100)
              },
              splitLine: {
                lineStyle: {
                  color: "#11446F",
                  type: 'dashed',
                  width: vwToPx((1 / 1920) * 100)

                },
              },
            },
          ],
          series: [
            {
              name: "问题总数",
              type: "bar",
              barWidth: vwToPx(13 / 1920 * 100),
              data: seriesData1,
              itemStyle: {
                color: {
                  type: 'linear',
                  x: 0,
                  y: 0,
                  x2: 0,
                  y2: 1,
                  colorStops: [
                    { offset: 0, color: '#8AFCD8' },
                    { offset: 1, color: '#1AA075' }
                  ]
                }
              },
              showBackground: true,
              backgroundStyle: {
                color: 'rgba(200, 255, 250, 0.2)'
              }
            },
            {
              name: "已处理数",
              type: "bar",
              barWidth: vwToPx(13 / 1920 * 100),
              barGap: '50%', //最后一个bar的barGap生效
              data: seriesData2,
              itemStyle: {
                color: {
                  type: 'linear',
                  x: 0,
                  y: 0,
                  x2: 0,
                  y2: 1,
                  colorStops: [
                    { offset: 0, color: '#00C6FF' },
                    { offset: 1, color: '#1173B8' }
                  ]
                }
              },
              showBackground: true,
              backgroundStyle: {
                color: 'rgba(177, 215, 255, 0.2)'
              }
            },
            // 顶部的椭圆形(象形柱图):pictorialBar
            {
              name: "问题总数",
              type: "pictorialBar",
              symbolSize: [vwToPx(13 / 1920 * 100), vhToPx(5 / 1080 * 100)],
              symbolOffset: ['-75%', -vhToPx(2.5 / 1080 * 100)],
              z: 12,
              symbolPosition: "end",
              itemStyle: {
                color: '#C8FFFA'
              },
              data: seriesData1
            },
            // 顶部的椭圆形(象形柱图):pictorialBar
            {
              name: "已处理数",
              type: "pictorialBar",
              symbolSize: [vwToPx(13 / 1920 * 100), vhToPx(5 / 1080 * 100)],
              symbolOffset: ['75%', -vhToPx(2.5 / 1080 * 100)],
              z: 12,
              symbolPosition: "end",
              itemStyle: {
                color: '#B1D7FF'
              },
              data: seriesData2
            },
          ],
        };
        if (maxY) {
          const maxYList = new Array(seriesData1.length).fill(maxY)
          option.series = [
            ...option.series,
            // 顶部的椭圆形(象形柱图):pictorialBar
            {
              name: "问题总数",
              type: "pictorialBar",
              symbolSize: [vwToPx(13 / 1920 * 100), vhToPx(5 / 1080 * 100)],
              symbolOffset: ['-75%', -vhToPx(2.5 / 1080 * 100)],
              z: 10,
              symbolPosition: "end",
              itemStyle: {
                color: 'rgba(200, 255, 250, 0.3)'
              },
              data: maxYList
            },
            // 顶部的椭圆形(象形柱图):pictorialBar
            {
              name: "已处理数",
              type: "pictorialBar",
              symbolSize: [vwToPx(13 / 1920 * 100), vhToPx(5 / 1080 * 100)],
              symbolOffset: ['75%', -vhToPx(2.5 / 1080 * 100)],
              z: 10,
              symbolPosition: "end",
              itemStyle: {
                color: 'rgba(177, 215, 255, 0.3)'
              },
              data: maxYList
            },
          ]
        }
        return option;
      }
      chart.setOption(getOption());
      //获取Y轴的最大值
      var maxY = chart.getModel().getComponent('yAxis').axis.scale._extent[1];
      chart.setOption(getOption(maxY));
      window.addEventListener('resize', () => {
        chart.setOption(getOption(maxY))
        chart.resize()
      })
    },

最终实现效果

image.png

参考链接
  1. vue+Echarts实现圆柱体柱状图

  2. echarts篇V——立体柱形图

  3. ECharts: 绘制立体柱状图【圆柱体】

  4. 在 vue2 中实现 echarts 立体方形柱状图及立体圆柱