let color = ['#0061EB', '#FF8B1F', '#5F2AD7']; let valObj = [{name:'1',val:5},{name:'2',val:2},{name:'3',val:3}]; // 传入数据生成 option valObj.forEach((ele, i) => { ele.itemStyle = { // 透明度 opacity: ele.val == 0 ? 0 : 0.7, // 扇形颜色 color: color[i], }; ele.label = { show: true, } ele.value = ele.val }) var data = valObj; let total = data.reduce((prev, curr) => prev + curr.value, 0); function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k) { // 计算 let midRatio = (startRatio + endRatio) / 2; let startRadian = startRatio * Math.PI * 2; let endRadian = endRatio * Math.PI * 2; let midRadian = midRatio * Math.PI * 2; // 如果只有一个扇形,则不实现选中效果。 if (startRatio === 0 && endRatio === 1) { isSelected = false; } // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3) k = typeof k !== 'undefined' ? k : 1 / 3; // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0) let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0; let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0; // 计算高亮效果的放大比例(未高亮,则比例为 1) let hoverRate = isHovered ? 1.05 : 1; // 返回曲面参数方程 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 offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate; } if (u > endRadian) { return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate; } return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate; }, y: function (u, v) { if (u < startRadian) { return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate; } if (u > endRadian) { return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate; } return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate; }, z: function (u, v) { if (u < -Math.PI * 0.5) { return Math.sin(u); } if (u > Math.PI * 2.5) { return Math.sin(u); } return Math.sin(v) > 0 ? 1 : -1; }, }; } // 生成模拟 3D 饼图的配置项 function getPie3D(pieData, internalDiameterRatio) { let series = []; let sumValue = 0; let startValue = 0; let endValue = 0; let legendData = []; let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3; // 新增标签 series @20210613 let labelSeries = { id: 'labelSeries', type: 'bar3D', //zlevel:-9, barSize: [0.1, 0.1], data: [], label: { show: true, formatter: function (params) { let rate = (params.value[3] / total * 100).toFixed(0); return `${rate}%`; }, textStyle: { fontSize: 25, color: "#fff", backgroundColor: 'transparent', }, }, }; // 为每一个饼图数据,生成一个 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], pieStatus: { selected: false, hovered: false, k: k, }, }; 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, false, false, k ); startValue = endValue; legendData.push(series[i].name); // 判断增加 label 效果 @20210613 if (pieData[i].label && pieData[i].label.show) { let labelRadian = (series[i].pieData.startRatio + series[i].pieData.endRatio) * Math.PI; labelSeries.data.push({ name: series[i].name, value: [Math.cos(labelRadian), Math.sin(labelRadian), 1.2, series[i].pieData.value], itemStyle: { opacity: 1, }, }); } } // 将 labelSeries 添加进去 @20210613 series.push(labelSeries); // 准备待返回的配置项,把准备好的 legendData、series 传入。 let option = { legend: { orient: "vertical", icon: "circle", right: "0", align: "left", top: "center", itemGap: 15, formatter: function (name) { let index = 0; data.forEach(function (value, i) { if (value.name == name) { index = i; } }); return [`{name|${name}}{value|${data[index].value}}`].join('\n') }, textStyle: { verticalAlign: 'middle', rich: { name: { fontSize: '24', color: '#fff', fontWeght: 'bold', width: 200, }, value: { fontSize: 20, color: '#fff', fontFamily: "YouSheBiaoTiHei", textShadowColor: "red" } } }, }, tooltip: { formatter: (params) => { if (params.seriesName !== 'mouseoutSeries') { return `${params.seriesName }<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color };"></span>${option.series[params.seriesIndex].pieData.value}`; } }, }, backgroundColor: '#333', xAxis3D: { min: -1, max: 1, }, yAxis3D: { min: -1, max: 1, }, zAxis3D: { min: -1, max: 1, }, grid3D: { left: '-21%', top: -20, show: false, boxHeight: 10, //top: '30%', bottom: '20%', }, series: series, }; return option; } let option = getPie3D(data, 0.71);