Vue3 Echarts 3D饼图(3D环形图)实现讲解附带源码
文章目录
- 前言
- 一、准备工作
- 1. 所需工具
- 2. 引入依赖
- 方式一:CDN 快速引入
- 方式二:npm 本地安装(推荐)
- 二、实现原理解析
- 三、echarts-gl 3D插件 使用回顾
- grid3D 常用通用属性:
- series 常用通用属性:
- surface(曲面图)常用专属属性:
- 快速示例:绘制球体
- 四、代码实战
- 4.1 封装一个echarts通用组件
- 4.2 实现3D饼图主体
- 4.2 添加指示线和标签
- 完整代码:
- 控制参数说明:
- 通过调整参数实现3D环形图:
- 总结
前言
在数据可视化场景中,3D 饼图凭借立体效果和空间层次感,能让数据展示更具视觉冲击力。ECharts 作为优秀的数据可视化库,通过 echarts-gl 插件轻松支持 3D 图表渲染。本文将详细阐述如何利用 ECharts实现3D饼图、3D环形图
一、准备工作
1. 所需工具
ECharts 核心库:版本需 ≥5.0(支持新特性)
echarts-gl 插件:专门处理 3D 渲染的扩展库
2. 引入依赖
方式一:CDN 快速引入
<!-- 引入 ECharts 核心库 -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script>
<!-- 引入 echarts-gl 3D 插件 -->
<script src="https://cdn.jsdelivr.net/npm/echarts-gl@2.0.8/dist/echarts-gl.min.js"></script>
方式二:npm 本地安装(推荐)
npm install echarts echarts-gl --save
二、实现原理解析
3D饼图的实现主要通过echarts-gl 3D插件去实现,分为3部分。首先通过 自定义曲面图 (type: “surface”)绘制3D饼图主体,再者通过三维折线图( type: “line3D”)绘制指示线,最后通过三维散点图(type: “scatter3D”)绘制标签
三、echarts-gl 3D插件 使用回顾
echarts-gl 3D插件使用跟echarts类似,包括xAxis3D、yAxis3D、zAxis3D、grid3D、series等配置项,可以发现就是在echarts基础上后缀加了3D
grid3D 常用通用属性:
-
viewControl 用于鼠标的旋转,缩放等视角控制,是个对象
viewControl 的属性:
-
distance :默认视角距离主体的距离,值越大主体显示越小
-
alpha :视角绕 x 轴,即上下旋转的角度
-
beta :视角绕 y 轴,即左右旋转的角度
-
zoomSensitivity 缩放操作的灵敏度,值越大越灵敏,设置0可禁用缩放功能
-
rotateSensitivity 旋转操作的灵敏度,值越大越灵敏 ,设置0可禁用旋转功能
-
panSensitivity 平移操作的灵敏度,值越大越灵敏,设置0可禁用平移功能
-
autoRotate 是否开启视角绕物体的自动旋转查看
series 常用通用属性:
- type :图表类型,支持surface、bar3D、line3D等值
- itemStyle :样式包括颜色和透明度。
- name:系列名称,用于 tooltip 的显示,legend 的图例筛选
- data:图表项数据
surface(曲面图)常用专属属性:
series :
- wireframe:曲面图的网格线设置
- equation:曲面的函数表达式。如果需要展示的是函数曲面,可以不设置 data,通过 equation 去声明函数表达式
- parametric:是否为参数曲面。
- parametricEquation:曲面的参数方程。在data没被设置的时候,可以通过 parametricEquation 去声明参数参数方程。在 parametric 为true时有效。
其中parametricEquation属性是实现3D饼图关键,通过曲面的参数方程(parametricEquation)可以绘制复杂的 3D 曲面和曲线比如球体、圆柱体等 ,通过它 我们可以自定义绘制3D扇形图,最终由多个3D扇形组合成3D饼图。
参数方程(parametricEquation是个对象)通常使用两个参数( u 和 v)在特定区间内生成点的坐标 (x, y, z)。
参数方程形式:
parametricEquation:{u: { min: 0, max: 2 * Math.PI, step: 0.1 }, // u 参数范围v: { min: 0, max: Math.PI, step: 0.1 }, // v 参数范围x: function(u, v) { /* 返回 x 坐标 */ },y: function(u, v) { /* 返回 y 坐标 */ },z: function(u, v) { /* 返回 z 坐标 */ }
}
(1)参数范围 (u 和 v):
min 和 max 定义参数的取值范围。
step 控制参数步长,影响曲面的细分精度(值越小,曲面越精细,但性能开销越大)。
(2)坐标方程 (x, y, z)
定义曲面形状的核心部分。例如:
- 圆柱体:
x: function(u, v) { return Math.cos(u); },
y: function(u, v) { return Math.sin(u); },
z: function(u, v) { return v; }
- 环面
x: function(u, v) {return (2 + Math.cos(v)) * Math.cos(u);
},
y: function(u, v) {return (2 + Math.cos(v)) * Math.sin(u);
},
z: function(u, v) {return Math.sin(v);
}
快速示例:绘制球体
option = {xAxis3D: { type: 'value' },yAxis3D: { type: 'value' },zAxis3D: { type: 'value' },grid3D: {},series: [{type: 'surface',parametric: true, // 启用参数方程模式parametricEquation: {u: { min: 0, max: 2 * Math.PI, step: 0.1 }, // u 参数范围v: { min: 0, max: Math.PI, step: 0.1 }, // v 参数范围x: function(u, v) {return Math.sin(v) * Math.cos(u); // x 坐标方程},y: function(u, v) {return Math.sin(v) * Math.sin(u); // y 坐标方程},z: function(u, v) {return Math.cos(v); // z 坐标方程}},itemStyle: {color: '#2290ff', // 曲面颜色opacity: 0.7}}]
};
更多属性请查看官方文档
四、代码实战
以vue3为代码为示例,实现3D饼图、环形图
4.1 封装一个echarts通用组件
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%),所以父元素需要设置尺寸。
4.2 实现3D饼图主体
3D饼图主体不包含指示线和标签
demo.vue
<template><div class="container"><div class="echarts-view"><Echarts :data="options" /></div></div>
</template>
<script setup>
import { ref, computed } from "vue";
import Echarts from "./echarts.vue";//数据
const data = ref([{ name: "已完成", value: 25, color: "#D13DF2" },{ name: "申请中", value: 45, color: "#6442ee" },{ name: "已撤销", value: 12, color: "#6DCDE6" },{ name: "审核中", value: 7, color: "#2F54F3" },{ name: "已驳回", value: 3, color: "#8E56E0" },
]);// 图表配置
const options = computed(() => {//总数let total = data.value.reduce((a, b) => a + b.value, 0);//当前累加值let sumValue = 0;//辅助参数,控制饼图半径,(0-1)范围内控制环形大小,值越小环形内半径越大let k = 1;//series配置(每个扇形)let series = data.value.map((item) => {//当前扇形起始位置占饼图比例let startRatio = sumValue / total;//值累加sumValue += item.value;//当前扇形结束位置占饼图比例let endRatio = sumValue / total;return {name: item.name ?? null,type: "surface", //曲面图itemStyle: {color: item.color ?? null, //颜色},wireframe: {show: false, //不显示网格线},pieData: item, //数据//饼图状态pieStatus: {k, //辅助参数startRatio, //起始位置比例endRatio, //结束位置比例value: item.value, //数值},parametric: true, //参数曲面//曲面的参数方程parametricEquation: getParametricEquation(startRatio,endRatio,k,item.value),};});//返回配置return {//提示框tooltip: {formatter: (params) => {if (params.seriesName !== "mouseoutSeries" &¶ms.seriesName !== "pie2d") {return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${series[params.seriesIndex].pieData.value}`;}return "";},},xAxis3D: {min: -1,max: 1,},yAxis3D: {min: -1,max: 1,},zAxis3D: {min: -1,max: 1,},//grid3D: {show: false, //不显示坐标系boxHeight: 15, //饼图高度// 用于鼠标的旋转,缩放等视角控制viewControl: {alpha: 30, //视角distance: 300, //距离,值越大饼图越小rotateSensitivity: 0, //禁止旋转zoomSensitivity: 0, //禁止缩放panSensitivity: 0, //禁止平移autoRotate: false, //禁止自动旋转},},series,};
});/*** 获取面的参数方程* @param {*} startRatio 扇形起始位置比例* @param {*} endRatio 扇形结束位置比例* @param {*} k 辅助参数,控制饼图半径* @param {*} value 数值*/
const getParametricEquation = (startRatio, endRatio, k, value) => {const startRadian = startRatio * Math.PI * 2;const endRadian = endRatio * Math.PI * 2;k = typeof k === "number" && !isNaN(k) ? k : 1 / 3; //默认1/3// 返回曲面参数方程return {u: {min: -Math.PI,max: Math.PI * 3,step: Math.PI / 32,},v: {min: 0,max: Math.PI * 2,step: Math.PI / 20,},x(u, v) {if (u < startRadian) {return Math.cos(startRadian) * (1 + Math.cos(v) * k);}if (u > endRadian) {return Math.cos(endRadian) * (1 + Math.cos(v) * k);}return Math.cos(u) * (1 + Math.cos(v) * k);},y(u, v) {if (u < startRadian) {return Math.sin(startRadian) * (1 + Math.cos(v) * k);}if (u > endRadian) {return Math.sin(endRadian) * (1 + Math.cos(v) * k);}return Math.sin(u) * (1 + Math.cos(v) * k);},z(u, v) {if (u < -Math.PI * 0.5) {return Math.sin(u);}if (u > Math.PI * 2.5) {return Math.sin(u) * value * 0.1;}// 扇形高度根据value值计算return Math.sin(v) > 0 ? value * 0.1 : -1;},};
};</script>
<style scoped>
.container {width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;background-color: #203598;align-items: center;
}
.echarts-view {height: 700px;width: 1200px;
}
</style>
运行效果:
4.2 添加指示线和标签
....
....
....
// 图表配置
const options = computed(() => {//总数let total = data.value.reduce((a, b) => a + b.value, 0);//当前累加值let sumValue = 0;//辅助参数,控制饼图半径,(0-1)范围内控制环形大小,值越小环形内半径越大let k = 1;//series配置(每个扇形)
let series =............./新增///添加指示线series.forEach((item, index) => {let {itemStyle: { color },pieStatus: { startRatio, endRatio, value },} = item;addLabelLine(series, startRatio, endRatio, value, k, index, color);});
///return {............}
})///新增//
//添加label指示线
/*** @param {*} series 配置* @param {*} startRatio 扇形起始位置比例* @param {*} endRatio 扇形结束位置比例* @param {*} value 数值* @param {*} k 辅助参数,饼图半径相关* @param {*} i 在series中索引* @param {*} color 指示线颜色*/
const addLabelLine = (series,startRatio,endRatio,value,k,i,color = "#fff"
) => {//计算扇形中心弧度const midRadian = (startRatio + endRatio) * Math.PI;// 计算起点位置坐标(扇形边缘中心)const radius = 1 + k; // 外径const posX = Math.cos(midRadian) * radius; //x坐标const posY = Math.sin(midRadian) * radius; //y坐标const posZ = 0.1 * value; //z坐标let flag =(midRadian >= 0 && midRadian <= Math.PI / 2) ||(midRadian >= (3 * Math.PI) / 2 && midRadian <= Math.PI * 2)? 1: -1;//计算拐点坐标let turningPosArr = [posX * 1.1 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posY * 1.1 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posZ,];//计算结束位置坐标let endPosArr = [posX * 1.2 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posY * 1.2 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posZ * 3,];//添加label+指示线series.push(// 指示线{type: "line3D",lineStyle: {color: "#fff",//线颜色width:2,//线宽},data: [[posX, posY, posZ], turningPosArr, endPosArr],},//label{type: "scatter3D",label: {show: true,distance: 0,position: "center",textStyle: {color: "#fff",//文字颜色backgroundColor: "rgba(0,0,0,0)", //透明背景fontSize: 18,//文字尺寸fontWeight :'bold', //文字加粗padding: 5,},formatter: "{b}",},symbolSize: 0,data: [{name: series[i].name + ":" + value+'个',value: endPosArr,},],});
};
//
运行效果:
完整代码:
demo.vue
<template><div class="container"><div class="echarts-view"><Echarts :data="options" /></div></div>
</template>
<script setup>
import { ref, computed } from "vue";
import Echarts from "./echarts.vue";//数据
const data = ref([{ name: "已完成", value: 25, color: "#D13DF2" },{ name: "申请中", value: 45, color: "#6442ee" },{ name: "已撤销", value: 12, color: "#6DCDE6" },{ name: "审核中", value: 7, color: "#2F54F3" },{ name: "已驳回", value: 3, color: "#8E56E0" },
]);// 图表配置
const options = computed(() => {//总数let total = data.value.reduce((a, b) => a + b.value, 0);//当前累加值let sumValue = 0;//辅助参数,控制饼图半径,(0-1)范围内控制环形大小,值越小环形内半径越大let k = 1;//series配置(每个扇形)let series = data.value.map((item) => {//当前扇形起始位置占饼图比例let startRatio = sumValue / total;//值累加sumValue += item.value;//当前扇形结束位置占饼图比例let endRatio = sumValue / total;return {name: item.name ?? null,type: "surface", //曲面图itemStyle: {color: item.color ?? null, //颜色},wireframe: {show: false, //不显示网格线},pieData: item, //数据//饼图状态pieStatus: {k, //辅助参数startRatio, //起始位置比例endRatio, //结束位置比例value: item.value, //数值},parametric: true, //参数曲面//曲面的参数方程parametricEquation: getParametricEquation(startRatio,endRatio,k,item.value),};});//添加指示线series.forEach((item, index) => {let {itemStyle: { color },pieStatus: { startRatio, endRatio, value },} = item;addLabelLine(series, startRatio, endRatio, value, k, index, color);});//返回配置return {//提示框tooltip: {formatter: (params) => {if (params.seriesName !== "mouseoutSeries" &¶ms.seriesName !== "pie2d") {return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${series[params.seriesIndex].pieData.value}`;}return "";},},xAxis3D: {min: -1,max: 1,},yAxis3D: {min: -1,max: 1,},zAxis3D: {min: -1,max: 1,},//grid3D: {show: false, //不显示坐标系boxHeight: 15, //饼图高度// 用于鼠标的旋转,缩放等视角控制viewControl: {alpha: 30, //视角distance: 300, //距离,值越大饼图越小rotateSensitivity: 0, //禁止旋转zoomSensitivity: 0, //禁止缩放panSensitivity: 0, //禁止平移autoRotate: false, //禁止自动旋转},},series,};
});/*** 获取面的参数方程* @param {*} startRatio 扇形起始位置比例* @param {*} endRatio 扇形结束位置比例* @param {*} k 辅助参数,控制饼图半径* @param {*} value 数值*/
const getParametricEquation = (startRatio, endRatio, k, value) => {const startRadian = startRatio * Math.PI * 2;const endRadian = endRatio * Math.PI * 2;k = typeof k === "number" && !isNaN(k) ? k : 1 / 3; //默认1/3// 返回曲面参数方程return {u: {min: -Math.PI,max: Math.PI * 3,step: Math.PI / 32,},v: {min: 0,max: Math.PI * 2,step: Math.PI / 20,},x(u, v) {if (u < startRadian) {return Math.cos(startRadian) * (1 + Math.cos(v) * k);}if (u > endRadian) {return Math.cos(endRadian) * (1 + Math.cos(v) * k);}return Math.cos(u) * (1 + Math.cos(v) * k);},y(u, v) {if (u < startRadian) {return Math.sin(startRadian) * (1 + Math.cos(v) * k);}if (u > endRadian) {return Math.sin(endRadian) * (1 + Math.cos(v) * k);}return Math.sin(u) * (1 + Math.cos(v) * k);},z(u, v) {if (u < -Math.PI * 0.5) {return Math.sin(u);}if (u > Math.PI * 2.5) {return Math.sin(u) * value * 0.1;}// 扇形高度根据value值计算return Math.sin(v) > 0 ? value * 0.1 : -1;},};
};//添加label指示线
/*** @param {*} series 配置* @param {*} startRatio 扇形起始位置比例* @param {*} endRatio 扇形结束位置比例* @param {*} value 数值* @param {*} k 辅助参数,饼图半径相关* @param {*} i 在series中索引* @param {*} color 指示线颜色*/
const addLabelLine = (series,startRatio,endRatio,value,k,i,color = "#fff"
) => {//计算扇形中心弧度const midRadian = (startRatio + endRatio) * Math.PI;// 计算起点位置坐标(扇形边缘中心)const radius = 1 + k; // 外径const posX = Math.cos(midRadian) * radius; //x坐标const posY = Math.sin(midRadian) * radius; //y坐标const posZ = 0.1 * value; //z坐标let flag =(midRadian >= 0 && midRadian <= Math.PI / 2) ||(midRadian >= (3 * Math.PI) / 2 && midRadian <= Math.PI * 2)? 1: -1;//计算拐点坐标let turningPosArr = [posX * 1.1 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posY * 1.1 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posZ,];//计算结束位置坐标let endPosArr = [posX * 1.2 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posY * 1.2 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posZ * 3,];//添加label+指示线series.push(// 指示线{type: "line3D",lineStyle: {color: "#fff",//线颜色width:2,//线宽},data: [[posX, posY, posZ], turningPosArr, endPosArr],},//label{type: "scatter3D",label: {show: true,distance: 0,position: "center",textStyle: {color: "#fff",//文字颜色backgroundColor: "rgba(0,0,0,0)", //透明背景fontSize: 18,//文字尺寸fontWeight :'bold', //文字加粗padding: 5,},formatter: "{b}",},symbolSize: 0,data: [{name: series[i].name + ":" + value+'个',value: endPosArr,},],});
};
</script>
<style scoped>
.container {width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;background-color: #203598;align-items: center;
}
.echarts-view {height: 700px;width: 1200px;
}
</style>
控制参数说明:
代码中几个比较重要的控制参数需要注意,根据实际需求修改:
- k(第28行):辅助参数,用来控制饼图大小,一般设置(0-1)范围,当值为小于1时会出现环形,值越小环形内半径越大
- boxHeight(第113行):饼图高度主要控制参数(不是唯一影响参数,高度视觉效果还受到距离影响)
- viewControl (第115行):用于鼠标的旋转,缩放等视角控制,其中 alpha控制3D视角角度,distance控制眼睛与3D饼图距离,值越大饼图越显小。rotateSensitivity开启鼠标旋转功能,zoomSensitivity开启鼠标缩放功能。
通过调整参数实现3D环形图:
k=0.2 //半径相关
boxHeight:8 //高度
distance: 180, //距离
rotateSensitivity: 1, //开启鼠标控制旋转
autoRotate: true, //开启自动旋转
运行效果:
3D环形图完整代码:
demo.vue
<template><div class="container"><div class="echarts-view"><Echarts :data="options" /></div></div>
</template>
<script setup>
import { ref, computed } from "vue";
import Echarts from "./echarts.vue";//数据
const data = ref([{ name: "已完成", value: 25, color: "#D13DF2" },{ name: "申请中", value: 45, color: "#6442ee" },{ name: "已撤销", value: 12, color: "#6DCDE6" },{ name: "审核中", value: 7, color: "#2F54F3" },{ name: "已驳回", value: 3, color: "#8E56E0" },
]);// 图表配置
const options = computed(() => {//总数let total = data.value.reduce((a, b) => a + b.value, 0);//当前累加值let sumValue = 0;//辅助参数,控制饼图半径,(0-1)范围内控制环形大小,值越小环形内半径越大let k = 0.2;//series配置(每个扇形)let series = data.value.map((item) => {//当前扇形起始位置占饼图比例let startRatio = sumValue / total;//值累加sumValue += item.value;//当前扇形结束位置占饼图比例let endRatio = sumValue / total;return {name: item.name ?? null,type: "surface", //曲面图itemStyle: {color: item.color ?? null, //颜色},wireframe: {show: false, //不显示网格线},pieData: item, //数据//饼图状态pieStatus: {k, //辅助参数startRatio, //起始位置比例endRatio, //结束位置比例value: item.value, //数值},parametric: true, //参数曲面//曲面的参数方程parametricEquation: getParametricEquation(startRatio,endRatio,k,item.value),};});//添加指示线series.forEach((item, index) => {let {itemStyle: { color },pieStatus: { startRatio, endRatio, value },} = item;addLabelLine(series, startRatio, endRatio, value, k, index, color);});//返回配置return {//提示框tooltip: {formatter: (params) => {if (params.seriesName !== "mouseoutSeries" &¶ms.seriesName !== "pie2d") {return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${series[params.seriesIndex].pieData.value}`;}return "";},},xAxis3D: {min: -1,max: 1,},yAxis3D: {min: -1,max: 1,},zAxis3D: {min: -1,max: 1,},//grid3D: {show: false, //不显示坐标系boxHeight: 8, //饼图高度// 用于鼠标的旋转,缩放等视角控制viewControl: {alpha: 30, //视角distance: 180, //距离,值越大饼图越小rotateSensitivity: 1, //禁止旋转zoomSensitivity: 0, //禁止缩放panSensitivity: 0, //禁止平移autoRotate: true, //禁止自动旋转},},series,};
});/*** 获取面的参数方程* @param {*} startRatio 扇形起始位置比例* @param {*} endRatio 扇形结束位置比例* @param {*} k 辅助参数,控制饼图半径* @param {*} value 数值*/
const getParametricEquation = (startRatio, endRatio, k, value) => {const startRadian = startRatio * Math.PI * 2;const endRadian = endRatio * Math.PI * 2;k = typeof k === "number" && !isNaN(k) ? k : 1 / 3; //默认1/3// 返回曲面参数方程return {u: {min: -Math.PI,max: Math.PI * 3,step: Math.PI / 32,},v: {min: 0,max: Math.PI * 2,step: Math.PI / 20,},x(u, v) {if (u < startRadian) {return Math.cos(startRadian) * (1 + Math.cos(v) * k);}if (u > endRadian) {return Math.cos(endRadian) * (1 + Math.cos(v) * k);}return Math.cos(u) * (1 + Math.cos(v) * k);},y(u, v) {if (u < startRadian) {return Math.sin(startRadian) * (1 + Math.cos(v) * k);}if (u > endRadian) {return Math.sin(endRadian) * (1 + Math.cos(v) * k);}return Math.sin(u) * (1 + Math.cos(v) * k);},z(u, v) {if (u < -Math.PI * 0.5) {return Math.sin(u);}if (u > Math.PI * 2.5) {return Math.sin(u) * value * 0.1;}// 扇形高度根据value值计算return Math.sin(v) > 0 ? value * 0.1 : -1;},};
};//添加label指示线
/*** @param {*} series 配置* @param {*} startRatio 扇形起始位置比例* @param {*} endRatio 扇形结束位置比例* @param {*} value 数值* @param {*} k 辅助参数,饼图半径相关* @param {*} i 在series中索引* @param {*} color 指示线颜色*/
const addLabelLine = (series,startRatio,endRatio,value,k,i,color = "#fff"
) => {//计算扇形中心弧度const midRadian = (startRatio + endRatio) * Math.PI;// 计算起点位置坐标(扇形边缘中心)const radius = 1 + k; // 外径const posX = Math.cos(midRadian) * radius; //x坐标const posY = Math.sin(midRadian) * radius; //y坐标const posZ = 0.1 * value; //z坐标let flag =(midRadian >= 0 && midRadian <= Math.PI / 2) ||(midRadian >= (3 * Math.PI) / 2 && midRadian <= Math.PI * 2)? 1: -1;//计算拐点坐标let turningPosArr = [posX * 1.1 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posY * 1.1 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posZ,];//计算结束位置坐标let endPosArr = [posX * 1.2 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posY * 1.2 + i * 0.1 * flag + (flag < 0 ? -0.2 : 0),posZ * 3,];//添加label+指示线series.push(// 指示线{type: "line3D",lineStyle: {color: "#fff",//线颜色width:2,//线宽},data: [[posX, posY, posZ], turningPosArr, endPosArr],},//label{type: "scatter3D",label: {show: true,distance: 0,position: "center",textStyle: {color: "#fff",//文字颜色backgroundColor: "rgba(0,0,0,0)", //透明背景fontSize: 18,//文字尺寸fontWeight :'bold', //文字加粗padding: 5,},formatter: "{b}",},symbolSize: 0,data: [{name: series[i].name + ":" + value+'个',value: endPosArr,},],});
};
</script>
<style scoped>
.container {width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;background-color: #203598;align-items: center;
}
.echarts-view {height: 700px;width: 1200px;
}
</style>
总结
通过echarts-gl 3D插件灵活应用我们成功实现了 基础版3D 饼图、3D环形图。你可以根据实际需求,进一步调整图表的样式和参数,创造出更加美观、实用、符合实际需求的可视化效果。
相关文章:

Vue3 Echarts 3D饼图(3D环形图)实现讲解附带源码
文章目录 前言一、准备工作1. 所需工具2. 引入依赖方式一:CDN 快速引入方式二:npm 本地安装(推荐) 二、实现原理解析三、echarts-gl 3D插件 使用回顾grid3D 常用通用属性:series 常用通用属性:surface&…...

Kafka快速安装与使用
引言 这篇文章是一篇Ubuntu(Linux)环境下的Kafka安装与使用教程,通过本文,你可以非常快速搭建一个kafka的小单元进行日常开发与调测。 安装步骤 下载与解压安装 首先我们需要下载一下Kafka,这里笔者采用wget指令: wget https:…...

Java EE初阶——wait 和 notify
1. 线程饥饿 线程饥饿是指一个或多个线程因长期无法获取所需资源(如锁,CPU时间等)而持续处于等待状态,导致其任务无法推进的现象。 典型场景 优先级抢占: 在支持线程优先级的系统中,高优先级线程可能持续…...

RPA vs. 传统浏览器自动化:效率与灵活性的终极较量
1. 引言 在数字化转型的大潮下,企业和开发者对浏览器自动化的需求日益增长。无论是网页数据抓取、自动化测试,还是用户行为模拟,浏览器自动化已经成为提升效率的关键工具。然而,面对越来越严格的反自动化检测、复杂的 Web 结构和…...
Flask框架深度解析:蓝图、上下文机制与Jinja2模板引擎实战
Flask作为Python最流行的轻量级Web框架之一,以其简洁、灵活和高度可扩展的特性赢得了广大开发者的青睐。本文将深入探讨Flask框架的三大核心特性:蓝图(Blueprint)模块化开发、上下文(Context)管理机制以及Jinja2模板引擎的高级用法。无论你是Flask初学者…...

docker 快速部署若依项目
1、首先创建一个自定义网络,作用是使连接到该网络的容器能够通过容器名称进行通信,无需使用复杂的IP地址配置,方便了容器化应用中各个服务之间的交互。 sudo docker network create ruoyi 2、创建一个文件夹,创建compose.yml文件…...

polarctf-web-[rce1]
考点: (1)RCE(exec函数) (2)空格绕过 (3)执行函数(exec函数) (4)闭合(ping命令闭合) 题目来源:Polarctf-web-[rce1] 解题: 这段代码实现了一个简单的 Ping 测试工具,用户可以通过表单提交一个 IP 地址,服务器会执…...
数据备份与恢复方案
数据备份与恢复方案 一.背景 为确保公司信息安全,防止关键数据丢失,应对突发事件,特制定全面的数据备份与恢复方案。该方案将对公司的各类文件资料进行分级管理,并针对不同级别的数据设定相应的备份策略和恢复流程。 二…...

Redis+Caffeine构造多级缓存
一、背景 项目中对性能要求极高,因此使用多级缓存,最终方案决定是RedisCaffeine。其中Redis作为二级缓存,Caffeine作为一级本地缓存。 二、Caffeine简单介绍 Caffeine是一款基于Java 8的高性能、灵活的本地缓存库。它提供了近乎最佳的命中…...

docker(四)使用篇二:docker 镜像
在上一章中,我们介绍了 docker 镜像仓库,本文就来介绍 docker 镜像。 一、什么是镜像 docker 镜像本质上是一个 read-only 只读文件, 这个文件包含了文件系统、源码、库文件、依赖、工具等一些运行 application 所必须的文件。 我们可以把…...
ms-swift 代码推理数据集
目前想要对SFT微调后的模型进行测试,看官方文档ms-swift中有eval的教程,但是从介绍来看,eval使用的是modelscope的评测内容。 评测 SWIFT支持了eval(评测)能力,用于对原始模型和训练后的模型给出标准化…...

AXI4总线协议 ------ AXI_LITE协议
一、AXI 相关知识介绍 https://download.csdn.net/download/mvpkuku/90841873 AXI_LITE 选出部分重点,详细文档见上面链接。 1.AXI4 协议类型 2.握手机制 二、AXI_LITE 协议的实现 1. AXI_LITE 通道及各通道端口功能介绍 2.实现思路及框架 2.1 总体框架 2.2 …...
DATE_FORMAT可以接收date类型,也可以接收String类型!
DATE_FORMAT 是 SQL 函数,主要用于将日期/时间类型的字段按照指定格式转换成字符串。在 MyBatis 的 XML 动态 SQL 中,你看到的这段代码是为了比较数据库中的日期字段和传入参数的日期值,但会忽略时间部分,只比较年月日。 代码解释…...

Ubuntu24.04 安装 5080显卡驱动以及cuda
前言 之前使用Ubuntu22.04版本一直报错,然后换了24.04版本才能正常安装 一. 配置基础环境 Linux系统进行环境开发环境配置-CSDN博客 二. 安装显卡驱动 1.安装驱动 按以下步骤来: sudo apt update && sudo apt upgrade -y#下载最新内核并安装 sudo add…...
华三H3C交换机配置NTP时钟步骤 示例
现场1台H3C 5110交换机 版本:Comware Software, Version 5.20.99, Release 1105 当前没有指定NTP, <H3C-5110>dis ntp-service status Clock status: unsynchronizedClock stratum: 16Reference clock ID: noneNominal frequency: 100.0000 HzAc…...
RKNN开发环境搭建(ubuntu22.04)
以下情况在RV1106G3的平台上验证正常。 1、conda安装 1)conda --version//确认是否安装 2)创建一个安装目录,进行下一步 3)wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-4.6.14-Linux-x…...
matlab多项式
1. 多项式表示 多项式用行向量表示,按降幂排列系数。例如,多项式 3x22x1 表示为 [3 2 1]。 2. 创建多项式 直接输入系数:如 p [1 -3 3 -1] 表示 x3−3x23x−1。由根创建:使用 poly 函数。例如,根为 [1, 1, 1]&…...
Sprnig MVC 如何统一异常处理 (Exception Handling)?
主要有以下几种方式来实现统一异常处理,其中 ControllerAdvice (或 RestControllerAdvice) 结合 ExceptionHandler 是最常用的方式。 1. ExceptionHandler 注解 作用: 用于标记一个方法,该方法将处理在同一个 Controller 类中抛出的特定类型…...

SpringAI-RC1正式发布:移除千帆大模型!
续 Spring AI M8 版本之后(5.1 发布),前几日 Spring AI 悄悄的发布了最新版 Spring AI 1.0.0 RC1(5.13 发布),此版本也将是 GA(Generally Available,正式版)发布前的最后…...

操作系统之进程和线程听课笔记
计算机的上电运行就是构建进程树,进程调度就是在进程树节点进程进行切换 进程间通信的好处 经典模型 生产者和消费者 进程和线程的区别 线程引入带来的问题线程的优势 由于unix70年代产生,90年代有线程,当时数据库系统操作需要线程,操作系统没有来得及重造,出现了用户态线…...
【vue】封装接口,全局字典,表格表头及使用
一、封装接口(API请求) 1. 创建axios实例 // src/utils/request.js import axios from axiosconst service axios.create({baseURL: process.env.VUE_APP_BASE_API,timeout: 10000 })// 请求拦截器 service.interceptors.request.use(config > {co…...
深入解析ZAB协议:ZooKeeper的分布式一致性核心
引言 在分布式系统中,如何高效、可靠地实现多节点间的数据一致性是核心挑战之一。ZAB协议(ZooKeeper Atomic Broadcast)作为 ZooKeeper的核心算法,被广泛应用于分布式协调服务(如Kafka、HBase、Dubbo等)。…...

COMSOL随机参数化表面流体流动模拟
基于粗糙度表面的裂隙流研究对于理解地下水的流动、污染物传输以及与之相关的地质灾害(如滑坡)等方面具有重要意义。本研究通过蒙特卡洛方法生成随机表面形貌,并利用COMSOL Multiphysics对随机参数化表面的微尺度流体流动进行模拟。 参数化…...
大模型笔记-“训练”和“推理”概念
在大模型(如Transformer类模型、LLM)的资源管理和开发流程中,“训练”和“推理”是两个核心概念,分别对应模型的构建和实际应用阶段: 训练是模型的“学习过程”,需要大量资源和时间。推理是模型的“应用过…...

JavaSwing中的容器之--JScrollPane
JavaSwing中的容器之–JScrollPane 在Java Swing中,容器是用于容纳其他组件(如按钮、标签等)的组件。Swing提供了多种容器,它们可以嵌套使用以创建复杂的用户界面。 JScrollPane是一个轻量级组件,提供可滚动视图。JSc…...

使用 Cookie 实现认证跳转功能
使用 Cookie 实现认证跳转功能的实践与解析 在 Web 开发中,用户身份认证是一个基础而关键的功能点。本文将通过一个简单的前后端示例系统,介绍如何基于 Cookie 实现 Token 保存与自动跳转认证的功能,并结合 Cookie 与 Header 的区别、使用场…...
Reth(冗余以太网接口) 和Bridge-Aggregation(链路聚合接口)区别
Reth(Redundant Ethernet)与Bridge-Aggregation是H3C设备中两种不同的接口技术,主要区别体现在工作原理、应用场景及配置特性上。以下是详细对比分析: 定义与类型 Reth(冗余以太网接口) 类型:…...
(面试)Android各版本新特性
Android 6.0 (Marshmallow, API 23) 运行时权限管理:用户可在应用运行时动态授予或拒绝权限,取代安装时统一授权4。Doze模式与应用待机:优化后台耗电,延长设备续航5。指纹识别支持:原生API支持指纹身份验证。 Android…...
算法基础 -- 小根堆构建的两种方式:上浮法与下沉法
小根堆构建的两种方式:上浮法与下沉法 在构建小根堆(Min-Heap)时,通常有两种常见的构建方式: 上浮建堆(逐个插入,上浮调整)下沉建堆(Heapify 自底向上,下沉…...

LED接口设计
一个LED灯有3种控制状态,常亮、常灭和闪烁,要做到这种控制最简单的一种方法是使用任何一款处理器的普通IO去控制。 用IO控制方式有两种,一种是高有效,如下图1所示IO口为高电平时LED亮,IO为低电平时LED不亮。IO口出一个…...