echarts实现立体柱状图

实现效果图如下:
在这里插入图片描述
上面除了立体图之外还增加了背景图。注意,可以发现这个图的右下角是是和x轴平齐的,如果右下角也要折角,可以根据代码修改下描点的点位就可以了。
完整代码如下:

<template>
  <div id="bar-chart" ref="barChartRef"></div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue';
import * as echarts from 'echarts'
const barChartRef = ref()
let barChart = null
let options = {}
const data = ref([])

const imgSrc =
    'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAI6CAYAAACw4c+4AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQyIDc5LjE2MDkyNCwgMjAxNy8wNy8xMy0wMTowNjozOSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0NWI1MDY3Mi1kMzI4LTNmNDQtOTA2NC0xNDhlOGFlNWUzYmIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzI5MUNFMEQyOUJCMTFFRTg4ODhBOEM3M0UzMDIwQUMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzI5MUNFMEMyOUJCMTFFRTg4ODhBOEM3M0UzMDIwQUMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTggKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NWExOTdhNTctNDIzYi01MDQyLThkMGMtZjMxZTIwNWRlYTViIiBzdFJlZjpkb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6NjE2MTU1ZmUtY2M3Ni1mNTQ3LThlNzItMmZkOTcxZmU0Mjk5Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+hTdV3gAABDNJREFUeNrs1iESQAAURVG+JFqCGV0QVNuwZEGwEkk1qhmSH5Tz6m0vnbLtp7m4bRz6Zlm3vXiZ9mzhhFwLJ+RaOCHXwgm5Vh1n3Tnoewsn5Fo4AWMwBmMwxkEYgzEYgzEOwhiMwRiMcRDGYAzGYIyDMAZjMAZjNIzBGIzBGA1jMAZjMEbDGIzBGIzRMAZjMAZjNIzBGIzBGA1jMAZjMAZjHIQxGIMxGOMgjMEYjMEYB2EMxmAMxjgIYzAGYzDGQRiDMRiDMRrGYAzGYIyGMRiDMRijYQzGYAzGaBiDMRiDMRrGYAzGYAzGOAFjMAZjMMZBGIMxGIMxDsIYjMEYjHEQxmAMxmCMgzAGYzAGYzSMwRiMwRgNYzAGYzBGwxiMwRiM0TAGYzAGYzSMwRiMwRgNYzAGYzAGYzAGYzAGYzDGQRiDMRiDMQ7CGIzBGIxxEMZgDMZgjIMwBmMwBmM0jMEYjMEYDWMwBmMwRsMYjMEYjNEwBmMwBmM0jMEYjMEYjHECxmAMxmCMgzAGYzAGYxyEMRiDMRjjIIzBGIzBGAdhDMZgDMZoGIMxGIMxGsZgDMZgjIYxGIMxGKNhDMZgDMZoGIMxGIMxGsZgDMZgDMY4CGMwBmMwxkEYgzEYgzEOwhiMwRiMcRDGYAzGYIyDMAZjMAZjNIzBGIzBGA1jMAZjMEbDGIzBGIzRMAZjMAZjNIzBGIzBGIxxAsZgDMZgjIMwBmMwBmMchDEYgzEY4yCMwRiMwRgHYQzGYAzGOAhjMAZjMEbDGIzBGIzRMAZjMAZjNIzBGIzBGA1jMAZjMEbDGIzBGIzBGAdhDMZgDMY4CGMwBmMwxkEYgzEYgzEOwhiMwRiMcRDGYAzGYIyGMRiDMRijYQzGYAzGaBiDMRiDMRrGYAzGYIyGMRiDMRiDMU7AGIzBGIxxEMZgDMZgjIMwBmMwBmMchDEYgzEY4yCMwRiMwRgNYzAGYzBGwxiMwRiM0TAGYzAGYzSMwRiMwRgNYzAGYzBGwxiMwRiMwRgHYQzGYAzGOAhjMAZjMMZBGIMxGIMxDsIYjMEYjHEQxmAMxmCMhjEYgzEYo2EMxmAMxmgYgzEYgzEaxmAMxmCMhjEYgzEYgzFOwBiMwRiMcRDGYAzGYIyDMAZjMAZjHIQxGIMxGOMgjMEYjMEYDWMwBmMwRsMYjMEYjNEwBmMwBmM0jMEYjMEYDWMwBmMwRsMYjMEYjMEYB2EMxmAMxjgIYzAGYzDGQRiDMRiDMQ7CGIzBGIxxEMZgDMZgjIYxGIMxGKNhDMZgDMZoGIMxGIMxGsZgDMZgjIYxGIMxGIMxGIMxGIMxGOMgjMEYjMEYB2EMxmAMxjgIYzAGYzDGQRiDMRiDMRrGYAzGYIyGMRiDMRijYQzGYAzGaBiDMRiDMRrGYAzGYIyGMRiDMRiDMQ7CmP/aJcAAaiRuLcLv/0cAAAAASUVORK5CYII='
const patternImg = new Image()
patternImg.src = imgSrc

const initChart = async () => {
  if (barChart) {
    barChart.dispose()
    barChart = null
  }

  await nextTick()
  barChart = echarts.init(barChartRef.value)

  // 数据
  data.value = [
    { name: '西瓜', value: 10 },
    { name: '草莓', value: 20 },
    { name: '苹果', value: 15 },
    { name: '菠萝', value: 28 },
    { name: '葡萄', value: 13 }
  ]

  const xData = data.value.map(item => item.name) // x轴的label数据
  const yData = data.value.map(item => item.value) // series中data的数据

  options = {
    tooltip: {
      trigger: 'axis',
    },
    grid: { 
      top: 20,
      left: 30,
      right: 10,
      bottom: 30
    },
    xAxis: {
      type: 'category',
      data: xData,
      // 坐标轴线
      axisLine: {
        show: true,
        lineStyle: {
          color: '#FFFFFF',
          opacity: 0.4,
          width: 1,
          type: 'solid'
        }
      },
      // 坐标轴刻度
      axisTick: {
        show: false
      },
      // 坐标轴刻度标签
      axisLabel: {
        show: true,
        color: '#FFFFFF',
        fontWeight: 400,
        fontSize: 12,
        margin: 12
      },
      // 分隔线
      splitLine: {
        show: true,
        lineStyle: {
          color: '#FFFFFF',
          width: 1,
          opacity: 0.15,
          type: 'dashed'
        }
      }
    },
    yAxis: {
      type: 'value',
      // max: 1000,
      // 坐标轴名称和样式
      name: '',
      minInterval: 1,
      nameTextStyle: {
        padding: [0, 8, 0, 0],
        fontSize: 12,
        color: '#8F9297',
        fontWeight: 400
      },
      // 坐标轴线
      axisLine: {
        show: true,
        lineStyle: {
          color: '#FFFFFF',
          opacity: 0.14,
          width: 1
        }
      },
      // 坐标轴刻度
      axisTick: {
        show: false
      },
      // 坐标轴刻度标签
      axisLabel: {
        show: true,
        color: '#8F9297',
        fontSize: 12,
        fontWeight: 400,
      },
      // 分隔线
      splitLine: {
        show: true,
        lineStyle: {
          color: '#FFFFFF',
          width: 1,
          opacity: 0.15,
          type: 'dashed'
        }
      }
    },
    series: [
      {
        data: yData,
        type: 'custom',
        renderItem: function (params, api) {
          return getRenderItem(params, api)
        },
        itemStyle: {
          color: {
            image: patternImg,
            repeat: 'repeat'
          }
        }
      }
    ]
  }
  // 定义柱状图左侧图形元素
  const leftRect = echarts.graphic.extendShape({
    shape: {
      x: 0,
      y: 0,
      width: 21, //柱状图宽
    },
    buildPath: function (ctx, shape) {
      const api = shape.api
      const xAxisPoint = api.coord([shape.xValue, 0]) // 根据列表中index的值转化为坐标点
      
      const p0 = [shape.x - shape.width, shape.y]  // 左上点位
      const p1 = [shape.x - shape.width, xAxisPoint[1]] // 左下点位
      const p2 = [shape.x, xAxisPoint[1]] // 右下点位
      const p3 = [shape.x, shape.y] // 右上点位
      
      ctx.moveTo(p0[0], p0[1])
      ctx.lineTo(p1[0], p1[1])
      ctx.lineTo(p2[0], p2[1])
      ctx.lineTo(p3[0], p3[1])
      ctx.lineTo(p0[0], p0[1])
      ctx.closePath()
    }
  })
  // 定义柱状图右侧图形元素
  const rightRect = echarts.graphic.extendShape({
    shape: {
      x: 0,
      y: 0,
      width: 11,
      zWidth: 11,
      zHeight: 11
    },
    buildPath: function (ctx, shape) {
      const api = shape.api
      const xAxisPoint = api.coord([shape.xValue, 0]) // 坐标点
      const p0 = [shape.x, shape.y] // 左上点位
      const p1 = [shape.x, xAxisPoint[1]] // 左下点位
      const p2 = [shape.x + shape.width, xAxisPoint[1]] // 右下点位
      const p3 = [shape.x + shape.width, shape.y - shape.zHeight] // 右上点位
      
      ctx.moveTo(p0[0], p0[1])
      ctx.lineTo(p1[0], p1[1])
      ctx.lineTo(p2[0], p2[1])
      ctx.lineTo(p3[0], p3[1])
      ctx.lineTo(p0[0], p0[1])
      ctx.closePath()
    }
  })
    
  // 定义柱状图顶部图形元素
  const topRect = echarts.graphic.extendShape({
    shape: {
      x: 0,
      y: 0,
      width: 21,
      zWidth: 11,
      zHeight: 11
    },
    buildPath: function (ctx, shape) {
      const p0 = [shape.x - shape.width + shape.zWidth, shape.y - shape.zHeight] // 左上点位
      const p1 = [shape.x - shape.width, shape.y] // 左下点位
      const p2 = [shape.x, shape.y] // 右下点位
      const p3 = [shape.x + shape.zWidth, shape.y - shape.zHeight] // 右上点位
      
      ctx.moveTo(p0[0], p0[1])
      ctx.lineTo(p1[0], p1[1])
      ctx.lineTo(p2[0], p2[1])
      ctx.lineTo(p3[0], p3[1])
      ctx.lineTo(p0[0], p0[1])
      ctx.closePath()
    }
  })
  // 定义柱状图背景图形元素
  const bgRect = echarts.graphic.extendShape({
    shape: {
      x: 0,
      y: 0,
      width: 32,
      zWidth: 11,
      zHeight: 0
    },
    buildPath: function (ctx, shape) {
      const api = shape.api
      const xAxisPoint = api.coord([shape.xValue, 0]) // 坐标点
      const p0 = [shape.x - shape.width + shape.zWidth, 20] // 左上点位 根据grid.top的位置得到高
      const p1 = [shape.x - shape.width + shape.zWidth, xAxisPoint[1]] // 左下点位
      const p2 = [shape.x + shape.zWidth, xAxisPoint[1]] // 右下点位
      const p3 = [shape.x + shape.zWidth, 20] // 右上点位 根据grid.top的位置得到高
      
      ctx.moveTo(p0[0], p0[1])
      ctx.lineTo(p1[0], p1[1])
      ctx.lineTo(p2[0], p2[1])
      ctx.lineTo(p3[0], p3[1])
      ctx.lineTo(p0[0], p0[1])
      ctx.closePath()
    }
  })

  // 注册图形元素
  echarts.graphic.registerShape('leftRect', leftRect)
  echarts.graphic.registerShape('rightRect', rightRect)
  echarts.graphic.registerShape('topRect', topRect)
  echarts.graphic.registerShape('bgRect', bgRect)

  // 渲染图形
  function getRenderItem(params, api) {
    const location = api.coord([api.value(0), api.value(1)])  // 根据 data的index值和data转化为坐标像数值
    return {
      type: 'group',
      children: [
        {
          type: 'bgRect',
          shape: {
            api,
            xValue: api.value(0),
            yValue: api.value(1),
            x: location[0],
            y: location[1]
          },
          style: api.style()
        },
        {
          type: 'leftRect',
          shape: {
            api,
            xValue: api.value(0), // index值
            yValue: api.value(1), // data值
            x: location[0], // x像素
            y: location[1] // y像素
          },
          style: {
            fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 1, color: 'rgba(57,160,247,0)' },
              { offset: 0, color: 'rgba(57, 159, 246,1)' }
            ])
          }
        },
        {
          type: 'rightRect',
          shape: {
            api,
            xValue: api.value(0),
            yValue: api.value(1),
            x: location[0],
            y: location[1]
          },
          style: {
            fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 1, color: 'rgba(32,130,213,0)' },
              { offset: 0, color: 'rgba(32,130,213,1)' }
            ])
          }
        },
        {
          type: 'topRect',
          shape: {
            api,
            xValue: api.value(0),
            yValue: api.value(1),
            x: location[0],
            y: location[1]
          },
          style: {
            fill: '#3392E3'
          }
        }
      ]
    }
  }
  options && barChart.setOption(options, true)
}

onMounted(() => {
  initChart()
})
</script>

<style lang="scss" scoped>
#bar-chart {
  width: 500px;
  height: 400px;
  background-color: #142331;
}
</style>

这里最关键的实现思路就是把各个点位计算出来,然后连线。如果实在不懂这个意思的话建议是了解下canvas绘画或者仔细阅读下echarts中custom的自定义图表。最简单就是打印一下getRenderItem(params, api)中的参数代表什么意思。canvas的图都是从左上角(0,0)为起点。
欢迎评论。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值