因为项目里同时有echarts的地图,地图需要弹跳动画,还有2d饼图和3d饼图.这里有一个坑,动画必须要ECharts 5.3.0,而地图弹跳动画 → 需要 ECharts 5.3.0 → 但 5.3.0 又和 echarts-gl 不兼容 → 3D 饼图出不来。所以这里用的是threejs,效果如下先需要下载threejsnpm install threetemplatediv classchart_3dPie_boxdiv refchartContainerclasschart-container/div classtotal_numdiv classnum{{chartData}}/divdiv classtext告警总数/div/divdiv classflex-space-between chart-legenddiv v-for(item, index) in lableData:keyindexclasschart-itemdiv classchart_item_label flex_centerdiv:style{ backgroundColor: item.color }classchart_item_color/{{item.label}}/divdiv:style{ color: item.color }classchart_item_value{{item.value}}/div/div/div/div/templatescriptimport*as THREE fromthree;import{OrbitControls}fromthree/examples/jsm/controls/OrbitControls;import fontData from../../../../assets/font/DINPro-Regular.otf;exportdefault{name:ThreeDPieChart,computed:{chartData(){// 总和returnthis.config.data.reduce((sum,item)sumitem.value,0);},},data(){return{lableData:[{label:设备异常,value:70,color:#FFCC26},{label:水质异常,value:45,color:#00FFFF},{label:电气异常,value:20,color:#FF4747},{label:采集异常,value:56,color:#4BFF64},{label:工艺异常,value:13,color:#FFFC19},],config:{data:[{label:电气异常,value:100},{label:水质异常,value:45},{label:电气异常,value:20},{label:采集异常,value:56},{label:工艺异常,value:13},],colors:[#FFCC26,#00FFFF,#FF4747,#4BFF64,#FFFC19],height:10,heightFactor:4,},renderer:null,scene:null,camera:null,controls:null,font:null,animationId:null,};},mounted(){this.initChart();window.addEventListener(resize,this.handleResize);},beforeDestroy(){this.cleanup();},methods:{initChart(){if(!this.$refs.chartContainer)return;constcontainerWidththis.$refs.chartContainer.clientWidth;constcontainerHeightthis.$refs.chartContainer.clientHeight;constoutRMath.min(containerWidth,containerHeight)*0.58;// 调整饼图的大小constinnerRoutR*0.7;// 内圈大小// 1. 初始化渲染器启用抗锯齿和更高的阴影质量this.renderernew THREE.WebGLRenderer({antialias:true,alpha:true,// 允许透明背景});this.renderer.setSize(containerWidth,containerHeight);this.renderer.shadowMap.enabledtrue;this.renderer.shadowMap.typeTHREE.PCFSoftShadowMap;// 更柔和的阴影this.renderer.outputEncodingTHREE.sRGBEncoding;// 更好的颜色渲染this.$refs.chartContainer.appendChild(this.renderer.domElement);// 2. 创建场景设置适当的背景色this.scenenew THREE.Scene();// 添加光源constlight1new THREE.PointLight(0xfff3e0,0.5);light1.position.set(0,1200,2160);this.scene.add(light1);// 环境光调整强度解决颜色变暗constambientLightnew THREE.AmbientLight(0xffffff,0.6);this.scene.add(ambientLight);// 4. 创建相机this.cameranew THREE.OrthographicCamera(containerWidth/-2,containerWidth/2,containerHeight/2,containerHeight/-2,1,2000);// 特写镜头相机距离拉近this.camera.position.set(0,1000,1200);this.camera.lookAt(0,0,0);// 5. 控制器设置this.controlsnewOrbitControls(this.camera,this.renderer.domElement);// 6. 加载字体添加加载状态提示this.loadFont(outR,innerR);},loadFont(outR,innerR){constfontLoadernew THREE.FontLoader();this.fontfontLoader.parse(fontData);this.createPieChart(outR,innerR);},createPieChart(outR,innerR){constgroupnew THREE.Group();group.rotation.x-Math.PI/2;// 更精确的旋转group.position.y10;// 移动饼图的位置,向下移动 30 个单位this.scene.add(group);consttotalValuethis.config.data.reduce((sum,item)sumitem.value,0);let startAngle0;this.config.data.forEach((item,index){constangleLength(item.value/totalValue)*Math.PI*2;// 使用弧度制更精确constheightthis.config.height(item.value/totalValue)*this.config.height*this.config.heightFactor;// 使用更鲜艳的颜色constcolornew THREE.Color(this.config.colors[index]);color.convertSRGBToLinear();// 确保颜色正确渲染this.createPieSegment(group,outR,innerR,height,startAngle,angleLength,color,item.value,item.label// 添加标签显示);startAngleangleLength;});this.animate();},createPieSegment(group,outR,innerR,height,startAngle,angleLength,color,text,label){// 1. 创建形状constshapenew THREE.Shape();shape.absarc(0,0,outR,startAngle,startAngleangleLength,false);shape.lineTo(Math.cos(startAngleangleLength)*innerR,Math.sin(startAngleangleLength)*innerR);shape.absarc(0,0,innerR,startAngleangleLength,startAngle,true);// 2. 挤出设置constextrudeSettings{curveSegments:100,steps:2,depth:height,bevelEnabled:true,bevelThickness:1,bevelSize:0,bevelOffset:0,bevelSegments:1,};// 3. 创建网格使用更亮的材质constgeometrynew THREE.ExtrudeGeometry(shape,extrudeSettings);constmaterialnew THREE.MeshPhongMaterial({color:color,shininess:20,roughness:0.6,});constmeshnew THREE.Mesh(geometry,material);group.add(mesh);// 4. 添加文本如果字体已加载if(this.font){this.addTextToSegment(mesh,outR,innerR,height,startAngle,angleLength,text);}// 5. 添加标签可选this.addLabelToSegment(group,outR,startAngle,angleLength,label);},addTextToSegment(mesh,outR,innerR,height,startAngle,angleLength,text){try{// 计算文本位置和角度constmidAnglestartAngleangleLength/2;constradius(outRinnerR)/2;// 创建文本几何体consttextGeometrynew THREE.TextGeometry(text,{font:this.font,size:11,height:2,curveSegments:12,bevelEnabled:false,});// 计算文本居中textGeometry.computeBoundingBox();consttextWidthtextGeometry.boundingBox.max.x-textGeometry.boundingBox.min.x;// 创建文本材质更醒目的颜色consttextMaterialnew THREE.MeshPhongMaterial({color:0xffffff,});consttextMeshnew THREE.Mesh(textGeometry,textMaterial);// 定位和旋转文本textMesh.position.set(Math.cos(midAngle)*radius-textWidth/2,Math.sin(midAngle)*radius-10,height0);textMesh.rotation.set(120,// X轴旋转90度使文字立起来0,// Y轴不需要旋转0// Z轴旋转使文字朝向圆心);// textMesh.rotation.z midAngle Math.PI / 2;// textMesh.rotation.x Math.PI / 2;mesh.add(textMesh);}catch(error){console.error(创建文本失败:,error);}},addLabelToSegment(group,radius,startAngle,angleLength,label){// 创建简单的标签使用CSS2DRenderer或Three.js精灵// 这里简化为控制台输出console.log(Segment Label:${label});},animate(){this.animationIdrequestAnimationFrame(this.animate);this.controls.update();this.renderer.render(this.scene,this.camera);},handleResize(){if(!this.renderer||!this.camera||!this.$refs.chartContainer)return;constwidththis.$refs.chartContainer.clientWidth;constheightthis.$refs.chartContainer.clientHeight;this.camera.leftwidth/-2;this.camera.rightwidth/2;this.camera.topheight/2;this.camera.bottomheight/-2;this.camera.updateProjectionMatrix();this.renderer.setSize(width,height);},cleanup(){window.removeEventListener(resize,this.handleResize);if(this.animationId){cancelAnimationFrame(this.animationId);}if(this.rendererthis.$refs.chartContainerthis.$refs.chartContainer.contains(this.renderer.domElement)){this.$refs.chartContainer.removeChild(this.renderer.domElement);}// 释放资源if(this.scene){while(this.scene.children.length0){this.scene.remove(this.scene.children[0]);}}},},};/scriptstyle langscssscoped.chart_3dPie_box{position:relative;.chart-container{position:absolute;top:0;left:0;width:507px;height:343px;padding:0;overflow:hidden;// background: url(~/assets/images/dz_img.png) center bottom 40%/200px 87px no-repeat;background:url(~/assets/images/dz_img.png)no-repeat center center;background-size:100%/200px100%;}.total_num{width:507px;height:343px;position:absolute;left:0;top:0;display:flex;flex-direction:column;align-items:center;justify-content:center;.num{position:absolute;top:88px;font-family:SourceHanSansCN-Bold;font-weight:bold;font-size:54px;color:#ffffff;}.text{position:absolute;bottom:142px;font-family:Source Han Sans CN;font-weight:400;font-size:32px;color:#b0e8ff;}}.chart-legend{position:absolute;top:0;right:0;width:calc(50%-121px);padding:36px0;display:flex;flex-direction:column;// gap: 14px;.chart-item{width:100%;padding-right:94px;display:flex;align-items:center;justify-content:space-between;.chart_item_color{width:24px;height:24px;margin-right:19px;}.chart_item_label{font-family:Source Han Sans CN;font-weight:400;font-size:32px;color:#d1deee;}.chart_item_value{font-family:SourceHanSansCN-Bold;font-weight:bold;font-size:34px;color:#ffcc26;}}}}/style