echarts 3D堆叠柱图

77 阅读7分钟

参考1:juejin.cn/post/719387…
参考2:juejin.cn/post/723252…

1.png

标签

 <div id="app">
   <!-- 复制结束 -->
   <div :style="{'width':echartsConf.width, 'height':echartsConf.height}" :id="echartsConf.cntainer"></div>
   <!-- 以下开始复制 -->
 </div>

样式

 html,
 body {
     width: 100vw;
     height: 100vh;
     overflow: hidden;
     box-sizing: border-box;
     margin: 0px;
 }
 ​
 body {
     background: #032139;
     padding: 20px;
     display: flex;
 }
 ​
 #app {
     margin: 0 auto;
 }
 ​
 /* 以下开始复制  ==> 替换#app */
 #app {
     pointer-events: all;
     /* overflow: hidden; */
     text-align: left;
     box-sizing: border-box;
 }
 ​
 #app * {
     box-sizing: border-box;
 }
 ​
 .toolTipTitle {
     width: 100px;
     color: #fff;
 }

js

 /* ----------------------------------------------ecahrts立体柱图(对象) 开始---------------------------------------------- */
 /**
  * 左面
  * */
 const DH_LeftShape = echarts.graphic.extendShape({
   buildPath(ctx, shape) {
     const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
     // 侧面宽度
     const WIDTH = 15;
     // 斜角高度
     const OBLIQUE_ANGLE_HEIGHT = 3.6;
 ​
     const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
     const p2 = [basicsXAxis - WIDTH, bottomYAxis];
     const p3 = [basicsXAxis, bottomYAxis];
     const p4 = [basicsXAxis, topBasicsYAxis];
 ​
     ctx.moveTo(p1[0], p1[1]);
     ctx.lineTo(p2[0], p2[1]);
     ctx.lineTo(p3[0], p3[1]);
     ctx.lineTo(p4[0], p4[1]);
   },
 });
 /**
  * 右面
  * */
 const DH_RightShape = echarts.graphic.extendShape({
   buildPath(ctx, shape) {
     const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
     // 侧面宽度
     const WIDTH = 15;
     // 斜角高度
     const OBLIQUE_ANGLE_HEIGHT = 3.6;
 ​
     const p1 = [basicsXAxis, topBasicsYAxis];
     const p2 = [basicsXAxis, bottomYAxis];
     const p3 = [basicsXAxis + WIDTH, bottomYAxis];
     const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
 ​
     ctx.moveTo(p1[0], p1[1]);
     ctx.lineTo(p2[0], p2[1]);
     ctx.lineTo(p3[0], p3[1]);
     ctx.lineTo(p4[0], p4[1]);
   },
 });
 /**
  * 顶面
  * */
 const DH_TopShape = echarts.graphic.extendShape({
   buildPath(ctx, shape) {
     const { topBasicsYAxis, basicsXAxis } = shape;
     // 侧面宽度
     const WIDTH = 15;
     // 斜角高度
     const OBLIQUE_ANGLE_HEIGHT = 3.6;
 ​
     const p1 = [basicsXAxis, topBasicsYAxis];
     const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
     const p3 = [basicsXAxis, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT * 2];
     const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
 ​
     // 设置线条颜色为红色
     ctx.strokeStyle = 'rgba(255,255,255,1)';
 ​
     // 设置线条颜色为红色
     ctx.beginPath();
     ctx.moveTo(p1[0], p1[1]);
     ctx.lineTo(p2[0], p2[1]);
     ctx.lineTo(p3[0], p3[1]);
     ctx.lineTo(p4[0], p4[1]);
     // ctx.stroke();
 ​
     // 绘制其他图形或者操作
 ​
     ctx.closePath();
   },
 });
 ​
 //注册图形
 echarts.graphic.registerShape('DH_LeftShape', DH_LeftShape);
 echarts.graphic.registerShape('DH_RightShape', DH_RightShape);
 echarts.graphic.registerShape('DH_TopShape', DH_TopShape);
 ​
 function DH_GetColumnRenderItem(params, api) {
   // 基础坐标
   const basicsCoord = api.coord([api.value(0), api.value(1)]);
   // 顶部基础 y 轴
   const topBasicsYAxis = basicsCoord[1];
   // 基础 x 轴
   const basicsXAxis = basicsCoord[0];
   // 底部 y 轴
   const bottomYAxis = api.coord([api.value(0), 0])[1];
 ​
   //不透明的颜色
   let resColor;
   if (Object.prototype.toString.call(api.style().fill) === '[object String]') {
     resColor = api.style().fill;
   } else {
     resColor = api.style().fill.colorStops[0].color;
   }
 ​
   //加载动画效果
   const animationSet = {
     enterFrom: {
       // 淡入
       style: {
         opacity: 0,
       },
       //飞出方向
       y: 20,
     },
     enterAnimation: {
       duration: 1000,
       delay: 300,
     },
   };
 ​
   return {
     type: 'group',
     // 组合柱体
     children: [
       {
         type: 'DH_LeftShape',
         shape: {
           topBasicsYAxis,
           basicsXAxis,
           bottomYAxis,
         },
 ​
         style: api.style(),
         ...animationSet,
       },
       //提亮一个侧面(在侧面绘制完成后在铺一层上去),使其左右看起来有阴影感
       {
         type: 'DH_LeftShape',
         shape: {
           topBasicsYAxis,
           basicsXAxis,
           bottomYAxis,
         },
         style: {
           // 新添加的渐变效果
           fill: new echarts.graphic.LinearGradient(1, 0, 1, 1, [
             {
               offset: 0,
               color: 'rgba(255,255,255, 0.1)', // 新渐变结束颜色
             },
             {
               offset: 0.1,
               color: 'rgba(255,255,255,0.1)', // 新渐变结束颜色
             },
             {
               offset: 1,
               color: 'rgba(155,155,155,0)', // 新渐变起始颜色
             },
           ]),
         },
         ...animationSet,
       },
       {
         type: 'DH_RightShape',
         shape: {
           topBasicsYAxis,
           basicsXAxis,
           bottomYAxis,
         },
         style: api.style(),
         ...animationSet,
       },
 ​
       {
         type: 'DH_TopShape',
         shape: {
           topBasicsYAxis,
           basicsXAxis,
           bottomYAxis,
         },
         style: {
           ...api.style(),
           fill: resColor,
           // stroke: resColor, // 设置线条颜色为白色
           // lineWidth: 0.5, // 设置线条粗细,单位为像素
           // lineDash: [0.5, 1], // 设置虚线样式,数组中的值分别表示实线和虚线的长度
         },
         ...animationSet,
       },
     ],
   };
 }
 /* ----------------------------------------------ecahrts立体柱图(对象) 结束---------------------------------------------- */
 ​
 new Vue({
   el: '#app',
   data() {
     return {
       /* 颜色列表 */
       colorList: [
         [
           {
             offset: 0,
             color: 'rgba(203,144,45,1)',
           },
           {
             offset: 1,
             color: 'rgba(203,144,45,0.03)',
           },
         ],
         [
           {
             offset: 0,
             color: 'rgba(65,205,58,1)',
           },
           {
             offset: 1,
             color: 'rgba(65,205,58,0.03)',
           },
         ],
         [
           {
             offset: 0,
             color: 'rgba(38,200,165,1)',
           },
           {
             offset: 1,
             color: 'rgba(38,200,165,0.03)',
           },
         ],
         [
           {
             offset: 0,
             color: 'rgba(37,160,254,1)',
           },
           {
             offset: 1,
             color: 'rgba(37,160,254,0.03)',
           },
         ],
         [
           {
             offset: 0,
             color: 'rgba(140,110,240,1)',
           },
           {
             offset: 1,
             color: 'rgba(140,110,240, 0.03)',
           },
         ],
         [
           {
             offset: 0,
             color: 'rgba(37,144,249,1)',
           },
           {
             offset: 1,
             color: 'rgba(37,144,249, 0.03)',
           },
         ],
         [
           {
             offset: 0,
             color: 'rgba(71,224,148,1)',
           },
           {
             offset: 1,
             color: 'rgba(71,224,148, 0.03)',
           },
         ],
         [
           {
             offset: 0,
             color: 'rgba(216,155,249,1)',
           },
           {
             offset: 1,
             color: 'rgba(216,155,249, 0.03)',
           },
         ],
         [
           {
             offset: 0,
             color: 'rgba(19,206,231,1)',
           },
           {
             offset: 1,
             color: 'rgba(19,206,231, 0.03)',
           },
         ],
       ],
       /* 数据对象 */
       echartsConf: {
         //宽高
         width: '532px',
         height: '277px',
         // width: '100%',
         // height: '100%',
         //echarts容器
         cntainer: 'barEcahrts' + new Date().getTime(),
         //echarts实例对象
         instantiation: '',
         yUnits: ['个'],
         xScale: ['A', 'B', 'C', 'D'],
         data: [
           {
             name: '春节',
             data: [121, 106, 21, 86],
           },
           {
             name: '元宵节',
             data: [122, 93, 113, 131],
           },
           {
             name: '端午节',
             data: [125, 138, 125, 43],
           },
           {
             name: '中秋节',
             data: [94, 114, 133, 67],
           },
         ],
         //数据对象
         options: '',
       },
     };
   },
 ​
   mounted() {
     this.duplicateColor_ = this.duplicateColor();
     this.initEchartsAndUpdate();
   },
   computed: {
     //复制颜色 => 解决当数据长度大于颜色长度时,echarts的颜色循环会出现问题
     duplicateColor() {
       return (dataLength) => {
         let colorList = [...this.colorList];
         //倍数
         let time = Math.ceil(dataLength / colorList.length);
 ​
         if (time > 1) {
           for (let i = 1; i < time; i++) {
             colorList.push(...this.colorList);
           }
         }
 ​
         //颜色结果集
         return colorList;
       };
     },
   },
   methods: {
     //获取图例
     getlegendData(lables, resultColor) {
       const data = [];
       lables.forEach((item, index) => {
         data.push({
           name: item.name,
           itemStyle: {
             color: new echarts.graphic.LinearGradient(
               0,
               0,
               0,
               1,
               resultColor[index]
             ),
           },
         });
       });
       return data;
     },
     /* 获取渲染所需要的数据 */
     getRendererData(targetData, maxLength) {
       let rendergraphData = [];
       //处理数据
       targetData.reduce(
         (lastV, nowV) => {
           let resObj = {
             name: nowV.name,
             data: [],
             //插入的颜色偏移
             colorOffset: 0,
           };
 ​
           let nowData = nowV.data;
           let oldData = lastV.data;
 ​
           let offsetNum = 0;
 ​
           for (let i = 0; i < maxLength; i++) {
             //当前值
             let a = Number(nowData[i] || '0');
             //上一个累加过的值
             let b = Number(oldData[i] || '0');
             let sum = a + b;
             //插入的颜色偏移值 => 产生视觉差,可通过开启图例点击就知道具体效果了
             if (sum - a) {
               offsetNum += Number((a / sum).toFixed(2));
             }
 ​
             resObj.data.push(sum);
           }
 ​
           if (offsetNum) {
             let finalOffset = Number((offsetNum / maxLength).toFixed(2));
             //补差
             resObj.colorOffset = finalOffset + 0.12;
           }
 ​
           rendergraphData.push(resObj);
 ​
           return resObj;
         },
         {
           name: '--',
           data: [],
         }
       );
       console.log(rendergraphData);
       return rendergraphData;
     },
     /* 处理颜色偏移 => 产生视觉差 */
     setColorOffset(targetColors, targetData) {
       let colorsUpdateOffset = JSON.parse(JSON.stringify(targetColors));
       //设置透明度偏移位置
       colorsUpdateOffset.forEach((item, index) => {
         if (targetData[index]?.colorOffset) {
           let color_ = item[0].color.split(',');
           let endDiaphaneity = 0 + ')';
           color_[color_.length - 1] = endDiaphaneity;
 ​
           let finalColor = color_.join(',');
 ​
           item.push({
             offset: targetData[index].colorOffset,
             color: finalColor,
           });
         }
       });
       return colorsUpdateOffset;
     },
     /* 获取tooltip弹窗 */
     getTooltipPopup(params, targetData) {
       //targetData => 为实际展示的数据
       let str = params[0].name + '<br/>';
       params.forEach((item) => {
         //横向 => targetData对应的目标索引
         let x = item.seriesIndex;
         //纵向 => 对应的目标内的数据索引
         let y = item.dataIndex;
         //展示
         str +=
           item.marker +
           item.seriesName +
           ':' +
           targetData[x].data[y] +
           '<br/>';
       });
       return str;
     },
 ​
     /**
      * 创建时间:2023-08-29 11:25:49
      * 作者:Ayo
      * !模块名:echarts初始化及更新
      * 描述:
      *
      * 参数说明
      * @参数1:{参数类型} 描述
      *
      * 返回值
      * @Null:{返回值类型} 描述
      */
     initEchartsAndUpdate: function () {
       let that = this;
       let echartsConf = this.echartsConf;
 ​
       if (echartsConf.instantiation) {
         echartsConf.instantiation.dispose &&
           echartsConf.instantiation.dispose();
         echartsConf.instantiation = null;
       }
 ​
       //验证将要使用的数据是否存在
       if (
         !echartsConf.xScale &&
         !Array.isArray(echartsConf.xScale) &&
         !echartsConf.xScale.length
       ) {
         //若数据不存在的处理方式
         //......
         console.error('无数据');
         return;
       }
 ​
       //获取挂载的容器并生成实例
       echartsConf.instantiation = echarts.init(
         document.getElementById(echartsConf.cntainer)
       );
 ​
       let resultColor = Object.freeze(
         that.duplicateColor(echartsConf.data.length)
       );
 ​
       // 找到最长的数组长度
       const maxLength = echartsConf.data.reduce((lastV, nowV) => {
         return Math.max(nowV?.data?.length || 0, lastV);
       }, 0);
 ​
       //格式渲染数据
       let rendergraphData = this.getRendererData(echartsConf.data, maxLength);
       //获取颜色偏移 => 视觉差
       let colorsUpdateOffset = this.setColorOffset(resultColor, rendergraphData);
 ​
       //echarts数据处理
       echartsConf.options = {
         color: resultColor,
         tooltip: {
           show: true,
           trigger: 'axis',
           // formatter: '{a}:{c}',
           formatter: (params) => {
             return this.getTooltipPopup(params, echartsConf.data);
           },
           backgroundColor: 'RGBA(0, 49, 85, 0.5)',
           backgroundColor: 'RGBA(0, 49, 85, 0.5)',
           borderColor: 'rgba(0, 151, 251, 0)',
           confine: true,
           borderRadius: 2,
           textStyle: {
             color: '#BCE9FC',
             fontSize: 18,
             align: 'left',
           },
         },
         grid: {
           left: '0px',
           top: '40px',
           right: '0px',
           bottom: '0px',
           containLabel: true, //显示区域自适应
         },
         xAxis: {
           type: 'category',
           //两端是否留白,柱图慎用
           // boundaryGap: false,
           //24个
           data: echartsConf.xScale,
           axisTick: {
             show: true,
             alignWithLabel: true, // 刻度与标签对齐
           },
           axisLabel: {
             //坐标轴刻度标签的相关设置。
             color: '#BFEBFF',
             fontSize: 14,
             fontWeight: 400,
             fontFamily: '微软雅黑',
             interval: 0, //坐标轴刻度标签的显示间隔(在类目轴中有效) 0:显示所有  1:隔一个显示一个 :3:隔三个显示一个...
             //设置文本过长超出隐藏...表示
             formatter: function (params) {
               var val = '';
               if (params.length > 6) {
                 val = params.substr(0, 6) + '...';
                 return val;
               } else {
                 return params;
               }
             },
           },
           axisLine: {
             //坐标轴轴线相关设置。
             // show: true,
             lineStyle: {
               //轴线风格
               color: '#BFEBFF', //颜色
               width: 1,
             },
           },
         },
         //设置y轴单位
         graphic: [
           {
             type: 'text',
             left: '0px', // 根据需要调整位置
             top: '10px', // 根据需要调整位置
             style: {
               text: `单位:${echartsConf.yUnits[0]}`,
               textAlign: 'left',
               fill: '#BFEBFF', // 根据需要调整颜色
               fontSize: 14, // 根据需要调整字体大小
               fontFamily: '微软雅黑',
             },
             //入场动画
             enterFrom: {
               // 淡入
               style: { opacity: 0 },
               // 从左飞入
               x: 0,
             },
           },
         ],
         yAxis: {
           type: 'value',
           splitLine: {
             show: false,
             lineStyle: {
               type: 'dashed',
               color: '#BFEBFF',
             },
           },
           splitArea: {
             show: true,
             areaStyle: {
               color: ['rgba(255,255,255,0.04)', 'transparent'],
             },
           },
           axisLine: {
             //坐标轴轴线相关设置。
             show: true,
             lineStyle: {
               //轴线风格
               color: '#BFEBFF', //颜色
               width: 1,
             },
           },
           axisLabel: {
             //坐标轴刻度标签的相关设置。
             color: '#BFEBFF',
             fontSize: 14,
             fontFamily: '微软雅黑',
             interval: 0, //坐标轴刻度标签的显示间隔(在类目轴中有效) 0:显示所有  1:隔一个显示一个 :3:隔三个显示一个...
             margin: 10, //刻度标签与轴线之间的距离。
             textStyle: {
               align: 'right',
             },
           },
         },
         legend: {
           width: 460,
           orient: 'horizontal', //horizontal vertical
           // left: '233px',
           right: '0px',
           top: '4px',
           // top: 'middle', // 图例垂直居中
           bottom: 'middle', // 文字垂直居中
           icon: 'rect', //图例形状为矩形
           itemHeight: 12, //图例宽
           itemWidth: 3, //图例高
           itemGap: 20,
           //禁用图例点击,会出问题
           selectedMode: false,
           //图例文字颜色配置
           itemStyle: {},
           textStyle: {
             width: 80,
             // 'truncate' 截断,并在末尾显示ellipsis配置的文本,默认为...
             // 'break' 换行
             // 'breakAll' 换行,跟'break'不同的是,在英语等拉丁文中,'breakAll'还会强制单词内换行
             overflow: 'break',
             lineHeight: 18,
             color: '#BFEBFF',
             fontSize: 14,
             fontFamily: '微软雅黑',
             //下方属性不生效
             shadowColor: '#000000',
             shadowBlur: 0,
             shadowOffsetX: 0,
             shadowOffsetY: 2,
           },
           data: this.getlegendData(echartsConf.data, resultColor),
         },
         series: rendergraphData.map((item, index) => {
           return {
             z: maxLength - index,
             type: 'custom',
             ...item,
             renderItem: DH_GetColumnRenderItem,
             itemStyle: {
               color: () => {
                 return new echarts.graphic.LinearGradient(
                   0,
                   0,
                   0,
                   1,
                   colorsUpdateOffset[index]
                 );
               },
             },
           };
         }),
       };
       echartsConf.instantiation.setOption(echartsConf.options);
       window.onresize = echartsConf.instantiation.resize;
     },
   },
 });

演示地址:code.juejin.cn/pen/7275962…