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&…...
Java大师成长计划之第20天:Spring Framework基础
📢 友情提示: 本文由银河易创AI(https://ai.eaigx.com)平台gpt-4o-mini模型辅助创作完成,旨在提供灵感参考与技术分享,文中关键数据、代码与结论建议通过官方渠道验证。 在Java开发领域,Spring …...
WebSocket集成方案对比
WebSocket集成方案对比与实战 架构选型全景图 #mermaid-svg-BEuyOkkoP6cFygI0 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-BEuyOkkoP6cFygI0 .error-icon{fill:#552222;}#mermaid-svg-BEuyOkkoP6cFygI0 .er…...
新能源汽车电池加热技术:传统膜加热 vs. 脉冲自加热
在新能源汽车的普及过程中,低温环境下的电池性能一直是影响用户体验的关键问题。当温度低于0C时,锂电池的内阻增大,充放电效率下降,续航缩短,甚至可能因低温充电导致电池损坏。 引言:电池低温性能衰减机理 …...
C++ 状态模式详解
状态模式(State Pattern)是一种行为设计模式,它允许一个对象在内部状态改变时改变其行为,使对象看起来像是改变了其类。 核心概念 设计原则 状态模式遵循以下设计原则: 单一职责原则:将状态相关行为分离…...
1. 使用 IntelliJ IDEA 创建 React 项目:创建 React 项目界面详解;配置 Yarn 为包管理器
1. 使用 IntelliJ IDEA 创建 React 项目:创建 React 项目界面详解;配置 Yarn 为包管理器 🧩 使用 IntelliJ IDEA 创建 React 项目(附 Yarn 配置与 Vite 建议)📷 创建 React 项目界面详解1️⃣ Name…...
【深度学习】目标检测算法大全
目录 一、R-CNN 1、R-CNN概述 2、R-CNN 模型总体流程 3、核心模块详解 (1)候选框生成(Selective Search) (2)深度特征提取与微调 2.1 特征提取 2.2 网络微调(Fine-tuning) …...
【node】6 包与npm
前言 目标 1 了解什么是包 2 怎么使用npm下载包 #mermaid-svg-Ur0d2uCdQeAQOJjW {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Ur0d2uCdQeAQOJjW .error-icon{fill:#552222;}#mermaid-svg-Ur0d2uCdQeAQOJjW .erro…...
【C++进阶篇】多态
深入探索C多态:静态与动态绑定的奥秘 一. 多态1.1 定义1.2 多态定义及实现1.2.1 多态构成条件1.2.1.1 实现多态两个必要条件1.2.1.2 虚函数1.2.1.3 虚函数的重写/覆盖1.2.1.4 协变1.2.1.5 析构函数重写1.2.1.6 override和final关键字1.2.1.7 重载/重写/隐藏的对⽐ 1…...
计算机网络|| 路由器和交换机的配置
一、实验目的 1. 了解路由器和交换机的工作模式和使用方法; 2. 熟悉 Cisco 网络设备的基本配置命令; 3. 掌握 Cisco 路由器的基本配置方式及配置命令; 4. 掌握路由器和交换机的基本配置与管理方法。 二、实验环境 1. 运行 Windows 操作…...
图形化编程如何从工具迭代到生态重构?
一、技术架构的范式突破 在图形化编程领域,技术架构的创新正在重塑行业格局。iVX 作为开源领域的领军者该平台通过图形化逻辑设计,将传统文本编程需 30 行 Python 代码实现的 "按钮点击→条件判断→调用接口→弹窗反馈" 流程,简化…...
歌曲《忘尘谷》基于C语言的歌曲调性检测技术解析
引言 在音乐分析与数字信号处理领域,自动检测歌曲调性是一项基础且关键的任务。本文以C语言为核心,结合音频处理库(libsndfile)和快速傅里叶变换库(FFTW),探讨如何实现调性检测,并通…...
Spring Boot 使用Itext绘制并导出PDF
最终效果 其实可以加分页,但是没有那么精细的需求,所以我最后就没有加,有兴趣的可以尝试下。 项目依赖 <!-- Spring Boot 版本有点老 --> <spring-boot.version>2.3.12.RELEASE</spring-boot.version><!-- 依…...
医学影像处理与可视化:从预处理到 3D 重建的技术实践
🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用…...
用 openssl 测试 tls 连接
以 baidu 为例,命令行为: openssl s_client -tlsextdebug -connect baidu.com:443 得到的输出为: CONNECTED(00000003) TLS server extension "renegotiation info" (id65281), len1 0000 - 00 …...
Matlab 汽车制动纵向动力学模型和PID控制
1、内容简介 Matlab 228-汽车制动纵向动力学模型和PID控制 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略...
重塑JavaScript原生功底=>【构造函数篇】
概念:用于创建对象的函数称之为构造函数 作用:构造函数在 JavaScript 中是用来创建对象的最根本操作。 语法:当一个函数通过 new 关键字 来调用的话,那么这个函数就是一个构造函数。 场景:构造函数是专门用来创建对象…...
从0到1:Python机器学习实战全攻略(8/10)
摘要:通过本文的学习,我们深入探索了 Python 机器学习从入门到实战的精彩世界。从 Python 在机器学习领域的独特优势,到机器学习的核心概念,再到各种强大工具库的应用,以及实战项目的完整演练,我们逐步揭开…...
[计算机网络]网络层
文章目录 408考研大纲IPV4数据报格式协议: IPv4 地址DHCP协议IP组播 408考研大纲 IPV4数据报格式 协议: 1:ICMP IPv4 地址 特殊IP 网络号全1又称直接广播地址,32位全1又称受限广播地址 因为255.255.255.255只能在本网络内广播,路由器不许通过它&…...
华为行业认证是什么?如何考取华为行业认证?
据IDC预测,2027年全球数字化转型市场规模将突破3.4万亿美元,而中国将成为增长最快的市场之一。然而,85%的企业在转型中面临核心人才短缺的困境,尤其缺乏兼具技术能力与行业洞察的复合型人才! 讯方技术作为华为授权培训…...
Kotlin与Qt跨平台框架深度解析:业务逻辑共享与多语言集成
简介 Kotlin Multiplatform和Qt作为两大主流跨平台开发框架,各自在技术生态和应用场景上展现出独特优势。Kotlin Multiplatform专注于业务逻辑的跨平台共享,通过Kotlin语言的统一特性实现高达80%的代码复用率,特别适合移动应用和Web服务的业务逻辑开发。而Qt则凭借其强大的…...
基于LNMP架构的个人博客系统部署
一、项目概述 本项目旨在通过两台服务器(Server-Web和Server-NFS-DNS)搭建一个基于LNMP(Linux、Nginx、MySQL、PHP)架构的个人博客系统。通过域名访问自建网站,同时实现资源文件的共享和DNS解析功能。 二、服务器配置…...
Python训练打卡Day21
常见的降维算法: # 先运行预处理阶段的代码 import pandas as pd import pandas as pd #用于数据处理和分析,可处理表格数据。 import numpy as np #用于数值计算,提供了高效的数组操作。 import matplotlib.pyplot as plt #用于绘…...
PostgreSQL 序列(Sequence) 与 Oracle 序列对比
PostgreSQL 序列(Sequence) 与 Oracle 序列对比 PostgreSQL 和 Oracle 都提供了序列(Sequence)功能,但在实现细节和使用方式上存在一些重要差异。以下是两者的详细对比: 一 基本语法对比 1.1 创建序列 PostgreSQL: CREATE [ { TEMPORARY | TEMP } |…...
直播:怎样用Agentic AI搭建企业AI应用?5.24日,拆解新一代“智能客服系统”案例
2025 DeepSeek掀起了中国企业的AI落地浪潮! 随着应用的深入,AI的落地技术正在快速演化。 3月,Manus一夜爆火,让AI Agent更加引人关注。 从生成式AI,到Agentic AI(代理式AI)。 AI正在从只能生…...
《Asp.net Mvc 网站开发》复习试题
一.选择题(注:每题2分,共 54分,只能在下列表格中,填写每个题目相应的正确字母选项) 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: :27: 1. Mvc让软件…...
算法题(145):货仓选址
审题: 本题需要我们找出距离之和的最小值 思路: 方法一:贪心 贪心策略:将货仓建立在所有商店的中间可以达到距离之和最小 因为每家商店都需要接收一车商品,所以这里的距离之和指的是从货仓到每一家商店的路线的距离之和…...
✅ TensorRT Python 安装精简流程(适用于 Ubuntu 20.04+)
安装 TensorRT Python 轮子的步骤 确保 pip 和 wheel 模块已更新并安装: 参考链接 python3 -m pip install --upgrade pip python3 -m pip install wheel 1. 确认环境要求 Python:版本 3.8 - 3.13 OS:Ubuntu 20.04 或 Windows 10 CPU&a…...
MYSQL 全量,增量备份与恢复
目录 一 数据备份的重要性 1 数据备份的重要性 2 数据库备份类型 2.1 从物理与逻辑的角度分类 2.2. 从数据库的备份策略角度分类从数据库的备份策略角度,数据库的备份可分为完全备份、差异备份和增量备份。 3 常见的备份方法 3.1 物理冷备份 物理冷备份时需要在数据库处…...
10. Spring AI PromptTemplate:从模板到高级技巧
1、前言 如果学到了这里,相信大部分人对Prompt并不陌生了。 在 Spring AI 的世界里,与强大的语言模型进行交互的基石便是 Prompt(提示语)。它不仅仅是你输入给 AI 的一段文本,更是你与智能对话的桥梁,是你唤醒模型潜能的关键指令。理解 Prompt 的本质、构建原则以及在 …...
基于OpenCV的人脸识别:Haar级联分类器
文章目录 引言一、环境准备二、代码实现1. 图像加载与预处理2. 加载Haar级联分类器3. 人脸检测核心参数详解4. 结果显示与标注 三、效果优化建议四、完整代码五、总结 引言 本文将带你一步步实现一个简单但实用的人脸检测程序,使用Python和OpenCV库。 一、环境准备…...
Git安装教程及常用命令
1. 安装 Git Bash 下载 Git 安装包 首先,访问 Git 官方网站 下载适用于 Windows 的 Git 安装包。 安装步骤 启动安装程序:双击下载的 .exe 文件,启动安装程序。选择安装选项: 安装路径:可以选择默认路径࿰…...
【PmHub后端篇】Skywalking:性能监控与分布式追踪的利器
在微服务架构日益普及的当下,对系统的性能监控和分布式追踪显得尤为重要。本文将详细介绍在 PmHub 项目中,如何使用 Skywalking 实现对系统的性能监控和分布式追踪,以及在这过程中的一些关键技术点和实践经验。 1 分布式链路追踪概述 在微服…...
ChromeDriver 技术生态与应用场景深度解析
ChromeDriver 技术生态与应用场景深度解析 随着 Web 自动化测试、运维和数据采集需求的不断增长,ChromeDriver 及其相关技术栈在各行业中扮演着举足轻重的角色。本文将从技术选型、语言适配、典型场景、技术延伸等维度,结合最新行业趋势与实践经验&…...
链表面试题6之回文结构
经过前几道题的铺垫,那么我们也是来到了链表的第六关。这也是一道非常经典的题目。 目录 逆置法 数组法 那么对于这道题目,我们要判断回文结构,实际上就是判断链表对不对称。这种类型的题目我们好像在哪里见过,对了,…...
ASP.NET Core Identity框架使用指南
文章目录 前言一、核心功能二、核心组件三、使用1)创建项目2)安装必要 NuGet包3)配置数据库连接字符串4)用户与角色实体定义4)配置数据库上下文5) 注册服务6)数据库迁移与初始化7)用…...
Hugging Face推出了一款免费AI代理工具,它能像人类一样使用电脑
Hugging Face推出了一款免费AI代理工具,它能像人类一样使用电脑。 这款工具名为Open Computer Agent(开放计算机代理),可模拟真实的电脑操作。 无需安装,在浏览器中即可运行。 以下是一些信息: - Open C…...
一.Gitee基本操作
一.初始化 1.git init初始化仓库 git init 用于在当前目录下初始化一个本地 Git 仓库,让这个目录开始被 Git 跟踪和管理。 生成 .git 元数据目录,从而可以开始进行提交、回退、分支管理等操作。 2.git config user.name/user.email配置本地仓库 # 设置…...
24、DeepSeek-V3论文笔记
DeepSeek-V3论文笔记 **一、概述****二、核心架构与创新技术**0.汇总:1. **基础架构**2. **创新策略** 1.DeepSeekMoE无辅助损失负载均衡DeepSeekMoE基础架构无辅助损失负载均衡互补序列级辅助损失 2.多令牌预测(MTP)1.概念2、原理2.1BPD2.2M…...
神经网络初步学习——感知机
一、前言 神经网络,顾名思义,它与我们大脑生物学里面讲到的神经元有关联。前辈们在研究早期人工智能的时候,就开始过我们的“交叉融合”,他们思考能不能把我们的人工智能的学习模式改造成我们人脑中神经元之间的学习方式——于是乎…...
在Text-to-SQL任务中应用过程奖励模型
论文标题 Reward-SQL: Boosting Text-to-SQL via Stepwise Reasoning and Process-Supervised Rewards 论文地址 https://arxiv.org/pdf/2505.04671 代码地址 https://github.com/ruc-datalab/RewardSQL 作者背景 中国人民大学,香港科技大学广州,阿…...
Python的安装使用
一、下载Python安装包 下载python安装包,可以直接访问官网地址:https://www.python.org/downloads/ 通过页面咱们直接下载最新版本的python安装包即可,python3.13.3。在页面的下方也可下载安装之前的版本,目前咱们按最新版本安装…...
mapreduce-wordcount程序2
WordCount案例分析 给定一个路径,统计这个路径下所有的文件中的每一个单词的出现次数。 其中,需要我们去实现代码的部分是:map函数和reduce函数。它们各自的作用是: map函数的入参是kv结构,k是偏移量,v是一…...
Java 内存模型(JMM)与内存屏障:原理、实践与性能权衡
Java 内存模型(JMM)与内存屏障:原理、实践与性能权衡 在多线程高并发时代,Java 内存模型(JMM) 及其背后的内存屏障机制,是保障并发程序正确性与性能的基石。本文将系统梳理 JMM 的核心原理、内…...
1.6 偏导数
(铺垫)全导数与偏导数看似相似,实则对应不同维度的变化观察。理解它们的差异需要从"变量自由度"切入: (核心差异解剖) 维度偏导数全导数变量关系其他变量被强制锁定所有变量都通过中间变量关联…...
网络爬虫学习之正则表达式
开篇 本文整理自《python3 网络爬虫开发实战》的学习笔记。 笔记整理 match match是一种常用的匹配方法,向它传入要匹配的字符串以及正则表达式,就可以检测这个正则表达式是否和字符串相匹配。 match会尝试从字符串的起始位置开始匹配正则表达式&#x…...
Pytorch常用统计和矩阵运算
文章目录 常用统计函数torch.prod()求积torch.sum()求和torch.mean()求均值torch.max()求最值torch.var() 方差torch.std()标准差 常见矩阵运算矩阵乘法点积 (torch.dot)批量矩阵乘法 (torch.bmm)奇异值分解 (SVD)特征分解 (torch.eig)矩阵求逆 (torch.inverse)伪逆 (torch.pin…...
PyTorch Lightning实战 - 训练 MNIST 数据集
MNIST with PyTorch Lightning 利用 PyTorch Lightning 训练 MNIST 数据。验证梯度范数、学习率、优化器对训练的影响。 pip show lightning Version: 2.5.1.post0Fast dev run DATASET_DIR"/repos/datasets" python mnist_pl.py --output_grad_norm --fast_dev_run…...
内存泄漏系列专题分析之十一:高通相机CamX ION/dmabuf内存管理机制Camx ImageBuffer原理
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:内存泄漏系列专题分析之八:高通相机CamX内存泄漏&内存占用分析--通用ION(dmabuf)内存拆解 这一篇我们开始讲: 内存泄漏系列专题分析之十一:高通相机CamX ION/dmabuf内存管理机制Camx ImageBuf…...
MySQL-逻辑架构
MySQL服务器逻辑架构图 主要分层结构 1.连接层 功能:处理连接、安全认证、线程管理等 核心模块:连接器:支持不同语言(JDBC)与MySQL交互;线程连接池:管理线程连接,减少线程频繁创建…...