当前位置: 首页 > news >正文

Vue3 Echarts 3D立方体柱状图实现教程

文章目录

  • 前言
  • 一、实现原理
  • 二、series ——type: "pictorialBar" 简介
    • 2.1 常用属性
  • 三、代码实战
    • 3.1 封装一个echarts通用组件 echarts.vue
    • 3.2 实现一个立方体柱状图
      • (1)首先实现一个基础柱状图
      • (2)添加立方体棱线
      • (3)添加上下2个菱形平面
      • 完整代码:
    • 3.3 实现一个渐变立方体柱状图
      • 完整代码:
  • 总结


前言

在前端开发的数据可视化场景中,ECharts 是一个强大且灵活的工具,它能创建各种复杂而美观的图表。本文将详细阐述如何利用 ECharts实现3D立方体柱状图

在这里插入图片描述


一、实现原理

3D立方体柱状图实现原理跟上一篇文章Vue3 Echarts 3D圆柱体柱状图实现
,实现方式是类似的,总共分3个部分组合合成上下2个菱形面+中间基础柱状图,基础柱状图又可根据实际需要拆成左右2个柱状图组合而成或者通过单个柱子水平线性渐变模拟中间棱线。

在这里插入图片描述

如上图所示上下2个独立菱形面往普通柱状图靠,调到合适位置变成如下效果:

在这里插入图片描述

上下两个菱形可通过 echarts ——series—— type: “pictorialBar” 自定义图形实现,请查看下章节介绍

基础柱状图通过线性渐变颜色模拟菱线,核心代码:

{type: "bar",barWidth: 50, //柱子宽度itemStyle: {//柱子样式color: {//渐变颜色type: "linear",x: 0,y: 0,x2: 1,//水平方向y2: 0,colorStops: [{ offset: 0, color: "rgba(57, 206, 255, 1)" },{ offset: 0.49, color: "rgba(57, 206, 255, 1)" },{ offset: 0.50, color: "rgba(57, 206, 255, 0.9)" },{ offset: 0, color: "rgba(57, 206, 255, 1)" },],},}

上述代码通过水平渐变方式使得柱状图正中间1%宽度颜色透明度降低0.1模拟菱线。

二、series ——type: “pictorialBar” 简介

type: “pictorialBar” 是 ECharts 中的一种特殊柱状图类型,它允许使用自定义图形(如图片、形状)来代替传统的柱状条,为数据可视化增添更多创意和灵活性。

type: "pictorialBar"也被叫作象形柱图。它首先是个柱状图,但是柱状图的柱子并不显示。这些柱子我们称为『基准柱(reference bar)』,根据基准柱来定位和显示各种象形图形(包括图片)。

每个象形图形根据基准柱的定位,是通过 symbolPosition、symbolOffset 来调整其于基准柱的相对位置。

2.1 常用属性

  • symbol 图形类型,默认圆形

    可选值 ‘circle’(圆形), ‘rect’(直角长方形), ‘roundRect’(圆角长方形), ‘triangle’(三角形), ‘diamond’(菱形), ‘pin’(漏斗), ‘arrow’(箭头), ‘none’(无)

  • symbolSize 图形的大小,默认值 [‘100%’, ‘100%’]

可以用数组分开表示宽和高,例如 [20, 10] 表示标记宽为20,高为10,也可以设置成诸如 10 这样单一的数字,表示 [10, 10]。
可以设置成绝对值(如 10),也可以设置成百分比(如 ‘120%’、[‘55%’, 23])。

  • symbolPosition 图形的定位位置

可选择 start’:图形边缘与柱子开始的地方内切,‘end’:图形边缘与柱子结束的地方内切。‘center’:图形在柱子里居中。

  • symbolOffset 图形相对于原本位置的偏移,默认[0, 0]

  • data 系列中的数据内容数组。数组项通常为具体的数据项。

更多属性可以查阅官方文档

本例需求通过上面几个属性我们就能构建一个菱形面

例如:

 {type: "pictorialBar",symbol:'diamond',//菱形symbolSize: [50, 22],//50x22尺寸symbolOffset: [0, -11],//向上偏移11pxsymbolPosition:'end',//位于顶部}

三、代码实战

以vue3为代码为示例

3.1 封装一个echarts通用组件 echarts.vue

echarts.vue

<template><div class="echarts-box"><div ref="echartRef" class="charts" ></div></div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, watch, nextTick, markRaw } from 'vue';
import * as echarts from 'echarts';
const props = defineProps({// 图表配置data: {type: Object,default: () => {},},
});
const echartRef = ref();let dom = null;//设置图表配置
const setOptions = (options) => {//清除画布dom && dom.clear();//重新渲染dom && dom.setOption(options);
};watch(() => props.data,(val) => {nextTick(() => {//默认关闭动画setOptions({animation: false,...val});});},{ deep: true, immediate: true }
);
const emits= defineEmits(['click'])
onMounted(() => {//初始化dom = markRaw(echarts.init(echartRef.value));//点击事件dom.on('click',  (param)=> {emits('click',param)} )
});
onBeforeUnmount(() => {//离开销毁echarts.dispose(dom);dom = null;
});defineExpose({setOptions,
});
</script>
<style lang="scss" scoped>
.echarts-box {width: 100%;height: 100%;box-sizing: border-box;
}.charts {width: 100%;height: 100%;
}
</style>

上述代码封装了一个echarts通用组件,只需传入data图表配置数据就会重新渲染,需要注意的是组件默认继承父元素的宽高(100%),所以父元素需要设置尺寸。

3.2 实现一个立方体柱状图

(1)首先实现一个基础柱状图

demo.vue

<template><div class="container"><div class="echarts-view"><Echarts :data="data" /></div></div>
</template>
<script setup>
import Echarts from "./echarts.vue";
import { ref } from "vue";const data = ref({//位置grid: {left: "5%",right: "5%",bottom: "10%",top: "15%",},//提示框tooltip: {},//图例legend: {show: true,right: "5%",textStyle: {color: "#fff",fontSize: 14,},},//x轴xAxis: {type: "category",//坐标轴轴线axisLine: {show: true,lineStyle: {color: "rgba(0, 176, 255,0.3)",},},//刻度axisTick: {show: false,},//分割线splitLine: {show: false,},// x轴文字axisLabel: {color: "#D8E6FF",fontSize: 14,margin: 15,},data: ["星期一","星期二","星期三","星期四","星期五","星期六","星期日",],},//y轴yAxis: {name: "单位:元", //单位文字nameTextStyle: {//单位样式color: "#BCD0F4",nameLocation: "start",fontSize: 14,},nameGap: 40, //单位与y轴距离type: "value",//分割线splitLine: {show: true,lineStyle: {color: "rgba(0, 176, 255, 0.2)",type: "dashed",},},//坐标轴轴线axisLine: {show: false,},//刻度数值文字样式axisLabel: {color: "#BCD0F4",fontSize: 14,},},//数据series: [//中间柱状图{type: "bar",barWidth: 50, //柱子宽度itemStyle: {//柱子样式color:'rgba(57, 206, 255, 1)'},name: "收入",data: [200, 500, 300, 250, 360, 700, 556],},],
});
</script>
<style scoped>
.container {width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;background-color: #0a2270;align-items: center;
}
.echarts-view {height: 700px;width: 1200px;
}
</style>

运行效果:

在这里插入图片描述

上述代码渲染一个最基础的柱状图

(2)添加立方体棱线

修改柱子颜色为渐变

 ............{type: "bar",barWidth: 50, //柱子宽度itemStyle: { //柱子样式color: {//渐变颜色type: "linear",x: 0,y: 0,x2: 1,//水平渐变y2: 0,colorStops: [{ offset: 0, color: "rgba(57, 206, 255, 1)" },{ offset: 0.49, color: "rgba(57, 206, 255, 1)" },{ offset: 0.50, color: "rgba(57, 206, 255, 0.9)" },//降低透明度模拟棱线{ offset: 0, color: "rgba(57, 206, 255, 1)" },],},},name: "收入",data: [200, 500, 300, 250, 360, 700, 556],},
.....
.....

运行效果:
在这里插入图片描述

(3)添加上下2个菱形平面

(底面可以根据实际情况选择是否添加)

 ............//中间基础柱状图{type: "bar",barWidth: 50, //柱子宽度itemStyle: { //柱子样式color: {//渐变颜色type: "linear",x: 0,y: 0,x2: 1,//水平渐变y2: 0,colorStops: [{ offset: 0, color: "rgba(57, 206, 255, 1)" },{ offset: 0.49, color: "rgba(57, 206, 255, 1)" },{ offset: 0.50, color: "rgba(57, 206, 255, 0.9)" },//降低透明度模拟棱线{ offset: 0, color: "rgba(57, 206, 255, 1)" },],},},name: "收入",data: [200, 500, 300, 250, 360, 700, 556],//顶部菱形面{type: "pictorialBar",symbol:'diamond',symbolSize: [50, 22],symbolOffset: [0, -11],//向上偏移菱形高度一半symbolPosition:'end',z: 12,itemStyle: {color: props.pictorialBarColor[0],},name: "收入",data: [200, 500, 300, 250, 360, 700, 556],},//底部菱形面{type: "pictorialBar",symbol:'diamond',symbolSize: [50, 15],symbolOffset: [0, 7.5],向上偏移菱形高度一半z: 12,itemStyle: {color: props.pictorialBarColor[1],},name: "收入",data: [200, 500, 300, 250, 360, 700, 556],},

运行效果:

在这里插入图片描述

底部菱形不加效果:

在这里插入图片描述

完整代码:

bar-3d.vue (立方体柱状图组件封装)

<!-- 立方体柱状图 -->
<template><div class="bar-wrap"><Echarts :data="data" /></div>
</template><script setup>
import Echarts from "../components/echarts.vue";
import { computed } from "vue";
const props = defineProps({grid: {type: Object,default: () => ({left: "5%",right: "5%",bottom: "10%",top: "15%",}),},legend:{type: Object,default: () => {},},series: {type: Object,default: () => {},},xAxis: {type: Array,default: () => {},},yAxis: {type: Object,default: () => {},},//单位unit: {type: String,default: "",},//上下菱形面颜色pictorialBarColor: {type: Array,default: () => ["#21F3FF", " rgba(33, 243, 255,0.8)"],},
});//echarts配置
const data = computed(() => {let { name, data } = props.series;//series配置处理let seriesData = [//中间柱状图{type: "bar",barWidth: 50, //柱子宽度barGap:0,itemStyle: {//柱子样式color: {//渐变颜色type: "linear",x: 0,y: 0,x2: 1,y2: 0,colorStops: [{ offset: 0, color: "rgba(57, 206, 255, 1)" },{ offset: 0.49, color: "rgba(57, 206, 255, 1)" },{ offset: 0.50, color: "rgba(57, 206, 255, 0.9)" },{ offset: 0, color: "rgba(57, 206, 255, 1)" },],},},...props.series,},//顶部菱形面{type: "pictorialBar",symbol:'diamond',symbolSize: [50, 22],symbolOffset: [0, -11],symbolPosition:'end',z: 12,itemStyle: {color: props.pictorialBarColor[0],},name,data: data.map((item) => {return {value: item,itemStyle: {opacity: item > 0 ? 1 : 0, //值为0时隐藏},};}),},//底部菱形面{type: "pictorialBar",symbol:'diamond',symbolSize: [50, 15],symbolOffset: [0, 7.5],z: 12,itemStyle: {color: props.pictorialBarColor[1],},name,data: data.map((item) => {return {value: item,itemStyle: {opacity: item > 0 ? 1 : 0,},};}),},];return {grid: props.grid,tooltip: {},legend: {show: true,right: "5%",textStyle: {color: "#fff",fontSize: 14,},...props.legend},//x轴xAxis: {type: "category",axisLine: {show: true,lineStyle: {color: "rgba(0, 176, 255,0.3)",},},axisTick: {show: false,},splitLine: {show: false,},// x轴文字axisLabel: {color: "#D8E6FF",fontSize: 14,margin: 15,},data: props.xAxis,},//y轴yAxis: {name: props.unit ? `单位:${props.unit}` : "", //单位文字nameTextStyle: {//单位样式color: "#BCD0F4",nameLocation: "start",fontSize: 14,},nameGap: 40, //单位与y轴距离type: "value",splitLine: {show: true,lineStyle: {color: "rgba(0, 176, 255, 0.2)",type: "dashed",},},axisLine: {show: false,},//刻度数值样式axisLabel: {color: "#BCD0F4",fontSize: 14,},...props.yAxis,},series: seriesData,};
});
</script>
<style scoped lang="scss">
.bar-wrap {width: 100%;height: 100%;
}
</style>

页面调用

demo.vue

<template><div class="container"><div class="echarts-view"><Bar3D unit="元" :xAxis="xAxisData" :series="seriesData" /></div></div>
</template>
<script setup>
import Bar3D from "./components/bar-3d2.vue";
import { ref } from "vue";//x轴标签
const xAxisData = ref(["星期一","星期二","星期三","星期四","星期五","星期六","星期日",
]);//数据
const seriesData = ref({name: "收入",data: [200, 500, 300, 250, 360, 700, 556],
});</script>
<style scoped>
.container {width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;background-color: #0a2270;align-items: center;
}
.echarts-view {height: 700px;width: 1200px;
}
</style>

3.3 实现一个渐变立方体柱状图

需求升级需要实现一个从上到下渐变透明的立方体柱状图如下图所示:

在这里插入图片描述

该需求渐变方向是垂直方向,而前面例子我们用水平渐变方向来模拟菱线,柱状图无法同时使用2个方向来实现渐变效果。显然一个柱状图无法满足需求。这个时候我们需要使用2个小柱状图左右合并成一个大柱状图,而贴合的分界线由于颜色的差异形成一条菱线。再者去掉底部菱形。

在这里插入图片描述
设置柱状图间距为0变为

在这里插入图片描述

核心代码:

//左边柱状图{type: "bar",barWidth: 25, //柱子宽度barGap:0,//设置左右柱状图间距为0itemStyle: {//柱子样式color: {//渐变颜色type: "linear",x: 0,y: 0,x2: 0,y2: 1,//垂直方向渐变colorStops: [{ offset: 0, color: "rgba(57, 206, 255, 1)" },{ offset: 1, color: "rgba(45, 72, 173, 0.1)" },],},},name: "收入",data: [200, 500, 300, 250, 360, 700, 556],},//右边柱状图{type: "bar",barWidth: 25, //柱子宽度itemStyle: {//柱子样式color: {type: "linear",x: 0,y: 0,x2: 0,y2: 1,colorStops: [{ offset: 0, color: "rgba(36, 201, 255, 1)" },{ offset: 1, color: "rgba(20, 43, 128, 0.2)" },],},},name: "收入",data: [200, 500, 300, 250, 360, 700, 556],},//顶部圆面{type: "pictorialBar",symbol:'diamond',..........

完整代码:

bar-3d.vue (立方体柱状图(垂直渐变)组件封装)

<!-- 柱状图 -->
<template><div class="bar-wrap"><Echarts :data="data" /></div>
</template><script setup>
import Echarts from "../components/echarts.vue";
import { computed } from "vue";
const props = defineProps({grid: {type: Object,default: () => ({left: "5%",right: "5%",bottom: "10%",top: "15%",}),},legend:{type: Object,default: () => {},},series: {type: Object,default: () => {},},xAxis: {type: Array,default: () => {},},yAxis: {type: Object,default: () => {},},//单位unit: {type: String,default: "",},//上下圆面颜色pictorialBarColor: {type: Array,default: () => ["#21F3FF", "rgba(31,97,234,0.4)"],},
});//echarts配置
const data = computed(() => {let { name, data } = props.series;//series配置处理let seriesData = [//左边柱状图{type: "bar",barWidth: 25, //柱子宽度barGap:0,itemStyle: {//柱子样式color: {//渐变颜色type: "linear",x: 0,y: 0,x2: 0,y2: 1,colorStops: [{ offset: 0, color: "rgba(57, 206, 255, 1)" },{ offset: 1, color: "rgba(45, 72, 173, 0.1)" },],},},...props.series,},//右边柱状图{type: "bar",barWidth: 25, //柱子宽度itemStyle: {//柱子样式color: {type: "linear",x: 0,y: 0,x2: 0,y2: 1,colorStops: [{ offset: 0, color: "rgba(36, 201, 255, 1)" },{ offset: 1, color: "rgba(20, 43, 128, 0.2)" },],},},...props.series,},//顶部菱面{type: "pictorialBar",symbol:'diamond',symbolSize: [50, 22],symbolOffset: [0, -11],symbolPosition:'end',z: 12,itemStyle: {color: props.pictorialBarColor[0],},name,data: data.map((item) => {return {value: item,itemStyle: {opacity: item > 0 ? 1 : 0, //值为0时隐藏},};}),},];return {grid: props.grid,tooltip: {},legend: {show: true,right: "5%",textStyle: {color: "#fff",fontSize: 14,},...props.legend},//x轴xAxis: {type: "category",axisLine: {show: true,lineStyle: {color: "rgba(0, 176, 255,0.3)",},},axisTick: {show: false,},splitLine: {show: false,},// x轴文字axisLabel: {color: "#D8E6FF",fontSize: 14,margin: 15,},data: props.xAxis,},//y轴yAxis: {name: props.unit ? `单位:${props.unit}` : "", //单位文字nameTextStyle: {//单位样式color: "#BCD0F4",nameLocation: "start",fontSize: 14,},nameGap: 40, //单位与y轴距离type: "value",splitLine: {show: true,lineStyle: {color: "rgba(0, 176, 255, 0.2)",type: "dashed",},},axisLine: {show: false,},//刻度数值样式axisLabel: {color: "#BCD0F4",fontSize: 14,},...props.yAxis,},series: seriesData,};
});
</script>
<style scoped lang="scss">
.bar-wrap {width: 100%;height: 100%;
}
</style>

总结

通过组合使用 type: bar 和 type: "pictorialBar"灵活应用我们成功实现了 3D 立方体柱状图。你可以根据实际需求,进一步调整图表的样式和配置,创造出更加美观和实用的可视化效果。对于3D柱状图的实现方法还有其他的方式例如结合three.js 3D渲染引擎来实现等,而本文介绍的方法是相对比较简单的方式,具体开发中可根据实际需要进行选择。

相关文章:

  • Github 热点项目 Qwen3 通义千问全面发布 新一代智能语言模型系统
  • Tomcat 服务频繁崩溃的排查与解决方法
  • 读论文笔记-LLaVA:Visual Instruction Tuning
  • 12.SpringDoc OpenAPI 功能介绍(用于生成API接口文档)
  • Qt QWebEngine应用和网页的交互
  • QCefView应用和网页的交互
  • HBuider中Uniapp去除顶部导航栏-小程序、H5、APP适用
  • scGPT-spatial:持续预训练scGPT用于空间转录组
  • 驱动开发系列54 - Linux Graphics QXL显卡驱动代码分析(一)设备初始化
  • 比亚迪再获国际双奖 以“技术为王”书写中国汽车出海新篇章
  • python查看指定的进程是否存在
  • 鸿蒙移动应用开发--ArkTS语法进阶实验
  • BOTA新六维力传感器PixONE:用12维度力矩与运动感测,驱动人形机器人力控未来
  • 电子病历高质量语料库构建方法与架构项目(智能数据目录篇)
  • 数据隐私在Web3环境下的重要性及实现方法
  • 蓝牙语音遥控国产适用芯片HS6621
  • 适配 AGP8.5,maven 私服发布报错(七)
  • 鸿蒙文件上传-从前端到后端详解,对比jq请求和鸿蒙arkts请求区别,对比new FormData()和鸿蒙arktsrequest.uploadFile
  • 从高端制造到民生场景:天元轻量化软件的“破局”之路
  • Access开发:轻松一键将 Access 全库表格导出为 Excel
  • 国家卫健委对近日肖某引发舆情问题开展调查
  • 马上评|扩大高速免费救援范围,打消出行后顾之忧
  • 深交所修订创业板指数编制方案,引入ESG负面剔除机制
  • “女乘客遭顺风车深夜丢高速服务区”续:滴滴永久封禁两名涉事司机账号
  • 看见“看得见的手”,看见住房与土地——读《央地之间》
  • 中国建设银行浙江省分行原党委书记、行长高强接受审查调查