echarts立体柱状图

6 阅读4分钟
<!-- 准备图表容器 -->
<template>
  <div id="cubeLeft3dBar" ref="echartsRef" :style="'width:calc(100% - 380px)'"
    style="height: 298px;background:#001F39; opacity:0.6;"></div>
</template>
<script setup>
import { areaStatsApi, latestMsgApi } from '/@/api/overview/index';

import * as echarts from 'echarts';
const props = defineProps({
  nds: {
    type: Array,
    default: []
  }
})
const xData = ref([]);
const echartsRef = ref();
const initChart = (arr) => {
  // 4. 初始化 ECharts 实例并配置选项
  let myChart = echarts.init(echartsRef.value);
  myChart.clear();

  let option = {
    tooltip: {
      trigger: 'axis',
    },
    grid: {
			top: '18%',
			left: '3%',
			right: '4%',
			bottom: '3%',
			containLabel: true,
		},
    legend: {
      show: true,
      top: 10,
      left: 'center',
      icon: 'rect',
      textStyle: {
        color: '#fff', // 文字颜色(#333/rgba均可)
        // color: 'rgba(51,51,51,0.8)', // 半透明效果
        fontSize: 12,  // 文字大小
        fontWeight: '500' // 文字粗细
      },
      itemWidth: 10,
      itemHeight: 10,
      itemGap: 20,
    },
    xAxis: [{
      type: 'category',
      axisTick: {
        color: '#fff',
        alignWithLabel: true,
      },
      axisLine: {
        lineStyle: {
          color: '#6A7C82', // 轴线颜色(支持十六进制/rgba/颜色名)
          width: 1,         // 轴线宽度(默认1px)
          type: 'solid'     // 轴线类型:solid(实线)/dashed(虚线)/dotted(点线)
        }
      },
      axisLabel: {
        color: '#fff', // 核心:设置X轴标签文字颜色(支持十六进制/rgba/颜色名)
        fontSize: 12,  // 可选:字体大小
        // rotate: 30,    // 可选:标签旋转角度(避免文字重叠)
        fontWeight: '500' // 可选:字体粗细
      },
      data: xData.value,
    }],
    yAxis: [{
      type: 'value',
      min: 0,
      splitLine: {
        lineStyle: {
          color: '#fff'
        }
      },
      // splitNumber: 5,
      // axisLine: {
      //   show: false
      // },
      // axisTick: {
      //   show: false
      // },
      splitLine: {
      show: true, // 控制是否显示分割线(默认true)
      lineStyle: {
        color: '#075858', // 分割线颜色(可自定义,比如你之前用的#075858)
        type: 'dashed', // 样式:可选 solid(实线)、dashed(虚线)、dotted(点线)
        width: 1 // 分割线宽度
      }
    },
      axisLabel: {
        fontSize: 14,
        color: '#fff',
         fontWeight: '500' // 可选:字体粗细
      },

    }],
    series:
      arr
  }

  console.log('7777777777', arr, option.series)
  // 5. 渲染图表并适配窗口变化
  myChart.setOption(option);
  window.addEventListener('resize', () => {
    myChart.resize();
  });

}
let colorStart = ['#00AFED', '#FCBC28', '#00D1C4', '#99D1C9']
let colorEnd = ['#0BCBF9', '#FEC65A', '#00D4CE', '#99D1C0']

// 定义立方体核心配置参数(统一控制宽、高,可按需修改)
const cubeConfig = {
  // 立方体主体宽度(水平方向尺寸)
  cubeWidth: 16,
  // 立方体立体高度(垂直/纵深方向尺寸,控制立体效果明显程度)
  cubeHeight: 8,
  // 辅助偏移量(可选,微调立体面的对齐效果,基于宽高自动计算更优)
  offsetX: function() { return this.cubeWidth / 2.8; },
  offsetY: function() { return this.cubeHeight / 1.2; }
};

// 2. 处理数据:提取X轴类目、年度、生成系列数据
// 绘制左侧面
const CubeLeft = echarts.graphic.extendShape({
  shape: {
    x: 0,
    y: 0,
  },
  buildPath: function (ctx, shape) {
    // 从全局配置提取宽高参数,避免硬编码
    const { cubeWidth, cubeHeight } = cubeConfig;
    const halfWidth = cubeWidth / 2; // 宽度均分,优化左侧面对齐

    const xAxisPoint = shape.xAxisPoint;
    // 基于配置参数计算顶点坐标(不再写死 710)
    const c0 = [shape.x + halfWidth, shape.y];
    const c1 = [shape.x - halfWidth, shape.y];
    const c2 = [xAxisPoint[0] - halfWidth, xAxisPoint[1]];
    const c3 = [xAxisPoint[0] + halfWidth, xAxisPoint[1]];
    
    ctx.moveTo(c0[0], c0[1])
       .lineTo(c1[0], c1[1])
       .lineTo(c2[0], c2[1])
       .lineTo(c3[0], c3[1])
       .closePath();
  },
});

// 绘制右侧面
const CubeRight = echarts.graphic.extendShape({
  shape: {
    x: 0,
    y: 0,
  },
  buildPath: function (ctx, shape) {
    const { cubeWidth, cubeHeight } = cubeConfig;
    const halfWidth = cubeWidth / 2;
    const fullHeight = cubeHeight; // 立体高度(纵深方向)

    const xAxisPoint = shape.xAxisPoint;
    // 基于配置参数计算顶点坐标(不再写死 71510)
    const c1 = [shape.x + halfWidth, shape.y];
    const c2 = [xAxisPoint[0] + halfWidth, xAxisPoint[1]];
    const c3 = [xAxisPoint[0] + cubeWidth, xAxisPoint[1] - fullHeight];
    const c4 = [shape.x + cubeWidth, shape.y - fullHeight];
    
    ctx.moveTo(c1[0], c1[1])
       .lineTo(c2[0], c2[1])
       .lineTo(c3[0], c3[1])
       .lineTo(c4[0], c4[1])
       .closePath();
  },
});

// 绘制顶面
const CubeTop = echarts.graphic.extendShape({
  shape: {
    x: 0,
    y: 0,
  },
  buildPath: function (ctx, shape) {
    const { cubeWidth, cubeHeight } = cubeConfig;
    const halfWidth = cubeWidth / 2;
    const fullHeight = cubeHeight;

    // 基于配置参数计算顶点坐标(不再写死 715102)
    const c1 = [shape.x + halfWidth, shape.y];
    const c2 = [shape.x + cubeWidth, shape.y - fullHeight]; // 右点(对应右侧面高度)
    const c3 = [shape.x - (cubeWidth / 5), shape.y - fullHeight]; // 左后点,基于宽度比例计算
    const c4 = [shape.x - halfWidth, shape.y];
    
    ctx.moveTo(c1[0], c1[1])
       .lineTo(c2[0], c2[1])
       .lineTo(c3[0], c3[1])
       .lineTo(c4[0], c4[1])
       .closePath();
  },
});
// 注册三个面图形
echarts.graphic.registerShape('CubeLeft', CubeLeft);
echarts.graphic.registerShape('CubeRight', CubeRight);
echarts.graphic.registerShape('CubeTop', CubeTop);

// / 新增:配置立方体基础偏移和间距
const cubeBaseOffset = -30; // 基础左偏移(调整整体位置)
const cubeGap = 40; // 不同年度立方体之间的间距(可根据需要调整,越大间距越宽)
const cubeWidth = 25; // 单个立方体的宽度(匹配3D形状大小)
// 4. 初始化ECharts
onMounted(() => {
  areaStatsApi({ nds: props.nds }).then((res) => {
    let rawData = res.data;
    // 1. 提取X轴类目(所有name)
    xData.value = rawData.map(item => item.name);
    // 2. 提取所有年度(去重)
    const allYears = [...new Set(rawData.flatMap(item => item.vos.map(v => v.nd)))];
    // 3. 生成系列数据:每个年度对应一条折线
    let arr = [];
    console.log('allYears==', allYears);
    allYears.forEach((year, ind) => {
      let d = rawData.map(item => {
        // 找到当前item对应年度的num,无则填0
        const vosItem = item.vos.find(v => v.nd === year);
        return vosItem ? vosItem.num : 0;
      })
      let item = {
        type: 'custom',
        name: year,
        renderItem: (params, api) => {
          let cubeLeftStyle = new echarts.graphic.LinearGradient(0, 0, 0, 1, [            {              offset: 0,              color: colorStart[ind],
            },
            {
              offset: 1,
              color: colorEnd[ind],
            },
          ]);
          let cubeRightStyle = new echarts.graphic.LinearGradient(0, 0, 0, 1, [            {              offset: 0,              color: colorStart[ind],
            },
            {
              offset: 1,
              color: colorEnd[ind],
            },
          ]);
          let cubeTopStyle = {
            color: colorStart[ind],
          };
          // var location = api.coord([api.value(0), api.value(1)]);
          // location = [location[0] - 20, location[1]];
          // var location1 = api.coord([api.value(0), 0]);
          // location1 = [location1[0] - 20, location1[1]];

          // 核心修改:计算差异化横向偏移,根据年度索引ind错开
          var location = api.coord([api.value(0), api.value(1)]);
          // 新增:ind * (cubeWidth + cubeGap) 让不同年度立方体横向排列
          location = [            location[0] + cubeBaseOffset + ind * (cubeWidth + cubeGap),
            location[1]
          ];

          var location1 = api.coord([api.value(0), 0]);
          // 同步修改X轴底部位置的偏移,保证3D立方体垂直对齐
          location1 = [            location1[0] + cubeBaseOffset + ind * (cubeWidth + cubeGap),
            location1[1]
          ];


          return {
            type: 'group',
            children: [
              {
                type: 'CubeLeft',
                shape: {
                  api,
                  xValue: api.value(0),
                  yValue: api.value(1),
                  x: location[0],
                  y: location[1],
                  xAxisPoint: location1,
                },
                style: {
                  fill: cubeLeftStyle,
                },
              },
              {
                type: 'CubeRight',
                shape: {
                  api,
                  xValue: api.value(0),
                  yValue: api.value(1),
                  x: location[0],
                  y: location[1],
                  xAxisPoint: location1,
                },
                style: {
                  fill: cubeRightStyle,
                },
              },
              {
                type: 'CubeTop',
                shape: {
                  api,
                  xValue: api.value(0),
                  yValue: api.value(1),
                  x: location[0],
                  y: location[1],
                  xAxisPoint: location1,
                },
                style: {
                  fill: cubeTopStyle,
                },
              },
            ],
          };
        },
        data: d,
      }
      arr.push(item);
    })
    // arr = getSeriesData(rawData, allYears);
    console.log('serises === ', arr)
    initChart(arr);
  })
})
</script>