先看下最終效果
實現思路
使用echarts-gl的曲面圖(surface)類型
通過parametric繪制曲面參數實現3D效果
代碼實現
<template><div id="surfacePie"></div>
</template>
<script setup>import {onMounted} from 'vue'import * as echarts from "echarts";import "echarts-gl";// 圖表數據const chartData = [{ name: '性能測試',value: 134},{ name: '安全',value: 56},{ name: '功能',value: 57},{ name: '代碼',value: 11},{ name: '易用性',value: 51}]// 自定義顏色const colorList = ['rgba(23,122,219)','rgba(219,129,2)','rgba(113,74,219)','rgba(12,255,246)','rgba(37,26,231)','rgba(219,91,23)',]// 生成扇形的曲面參數方程,用于 series-surface.parametricEquationfunction getParametricEquation(startRatio, endRatio, k, h) {// 計算let startRadian = startRatio * Math.PI * 2;let endRadian = endRatio * Math.PI * 2;// 通過扇形內徑/外徑的值,換算出輔助參數 k(默認值 1/3)k = typeof k !== 'undefined' ? k : 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: function (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: function (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: function (u, v) {if (u < -Math.PI * 0.5) {return Math.sin(u);}if (u > Math.PI * 2.5) {return Math.sin(u) * h * 0.1;}return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;},};}// 生成模擬 3D 餅圖的配置項function getPie3D(pieData, internalDiameterRatio) {let series = [];let sumValue = 0; // 總值 計算比例用let startValue = 0;let endValue = 0;// 根據傳入的內外徑比例,計算餅圖空心大小let k =typeof internalDiameterRatio !== 'undefined'? (1 - internalDiameterRatio) / (1 + internalDiameterRatio): 1 / 3;// 為每一個餅圖數據,生成一個 series-surface 配置for (let i = 0; i < pieData.length; i++) {sumValue += pieData[i].value;let seriesItem = {name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,type: 'surface', // 曲面圖parametric: true, //是否為參數曲面wireframe: {show: false, // 曲面網格線 不顯示},pieData: pieData[i]};// 為每一項設置顏色和透明度if (typeof pieData[i].itemStyle != 'undefined') {let itemStyle = {}typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;typeof pieData[i].itemStyle.opacity != 'undefined'? (itemStyle.opacity = pieData[i].itemStyle.opacity): null;seriesItem['itemStyle'] = itemStyle;}series.push(seriesItem);}// 使用上一次遍歷時,計算出的數據和 sumValue,調用 getParametricEquation 函數,// 向每個 series-surface 傳入不同的參數方程 series-surface.parametricEquation,也就是實現每一個扇形。for (let i = 0; i < series.length; i++) {endValue = startValue + series[i].pieData.value;series[i].pieData.startRatio = startValue / sumValue; // 曲面開始位置series[i].pieData.endRatio = endValue / sumValue; // 當前曲面結束位置series[i].parametricEquation = getParametricEquation(series[i].pieData.startRatio,series[i].pieData.endRatio,k,series[i].pieData.value);startValue = endValue;}// 準備待返回的配置項,把準備好的 legendData、series 傳入。let option = {// 圖例legend: {type:'scroll',pageIconSize: 12,icon: 'none', // 需求要顯示實心點 提供類型沒有 所以這個設置不顯示data: pieData.map((dItem,dIndex) => {return {...dItem,// 設置單個圖例項的文本樣式textStyle: {rich: {// 用富文本為每一個圖例項畫一個對應顏色的實心點iconName: {width: 4,height: 4,borderRadius: 5,backgroundColor: colorList[dIndex],},}},}}),top: 'bottom',left: 'center',itemGap: 5,selectedMode: false, // 關閉圖例選擇width: '100%',// 統一設置圖例的文本樣式textStyle: {color: '#fff',fontSize: 12,fontFamily: 'Source Han Sans CN',rich: {// 富文本統一設置 圖例的樣式name: {fontSize: 12,padding: [0, 0, 0, 10],width: 50,},percent: {fontSize: 12,padding: [0, 0, 0, 5],width: 15,},unit: {fontSize: 12,padding: [0, 0, 0, 1]}}},// 格式化圖例formatter: name => {let obj = pieData.find(item => item.name === name);let total = 0;let target = obj.valuefor (let i = 0; i < pieData.length; i++) {total += Number(pieData[i].value);}const legendStyle = `{iconName|}{name|${name}}{percent|${((target / total) * 100).toFixed(0)}}{unit|%}`return legendStyle}},xAxis3D: {},yAxis3D: {},zAxis3D: {},// 設置三維笛卡爾坐標系grid3D: {viewControl: {autoRotate: true, // 自動旋轉},left: 'center',top: '-8%',show: false, // 坐標系設置不顯示boxHeight: 100,boxWidth: 150,boxDepth: 150,},series: series,};return option;}</script>
補充說明
- echarts版本5.51
- echarts-gl版本2.0.9
- vue版本3.4.29
- vite版本5.3.1