<template> <div class="water-eval-container"> <div class="cityGreenLand-charts" id="cityGreenLand-charts"></div> <!-- Base background --> <div class="bg"></div> </div> </template> <script lang="ts"> import {<!-- --> getCurrentInstance } from 'vue' import {<!-- --> inject, onMounted, Ref, ref, markRaw, watch, computed, nextTick } from 'vue' import 'echarts-gl' export default {<!-- --> name: 'echartsCom', setup() {<!-- --> const {<!-- --> proxy } = getCurrentInstance() as any const optionData = ref([ {<!-- --> name: 'aaa', value: 14000, itemStyle: {<!-- --> color: 'rgba(27,242,214,0.6)', }, }, {<!-- --> name: 'bbb', value: 10000, itemStyle: {<!-- --> color: 'rgba(83,224,168,0.6)', }, }, {<!-- --> name: 'ccc', value: 9000, itemStyle: {<!-- --> color: 'rgba(69,104,226,0.6)', }, }, ]) let option: any onMounted(() => { nextTick(() => { init() }) }) const init = () => { //Build a 3D pie chart let myChart = proxy.$echarts.init(document.getElementById('cityGreenLand-charts')) // Pass in data to generate option option = getPie3D(optionData.value, 0.9) myChart.setOption(option) //Do you need a label guide line? If so, add a transparent 2D pie chart and adjust the angle so that the labelLine is aligned with the 3D pie chart, and setOption again // option.series.push({<!-- --> // name: 'pie2d', // type: 'pie', // labelLine: {<!-- --> // length: 10, // length2: 10 // }, // startAngle: -20, //Start angle, support range [0, 360]. // clockwise: false, // Whether the sectors of the pie chart are arranged clockwise. The above two configurations are mainly to align the 3D style // radius: ['20%', '50%'], // center: ['50%', '50%'], // data: optionData.value, // itemStyle: {<!-- --> // opacity: 0 // } // }); myChart.setOption(option) bindListen(myChart) } const getPie3D = (pieData: any, internalDiameterRatio: any) => {<!-- --> //internalDiameterRatio: transparent hollow ratio let series = [] let sumValue = 0 let startValue = 0 let endValue = 0 let legendData = [] let legendBfb = [] let k = 1 - internalDiameterRatio pieData.sort((a: any, b: any) => { return b.value - a.value }) // For each pie chart data, generate a series-surface configuration for (let i = 0; i < pieData.length; i + + ) { sumValue + = pieData[i].value let seriesItem: any = { 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, }, center: ['10%', '50%'], } if (typeof pieData[i].itemStyle != 'undefined') { let itemStyle: any = {} 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) } //Use the calculated data and sumValue during the last traversal to call the getParametricEquation function, // Pass different parametric equations series-surface.parametricEquation to each series-surface, that is, realize each sector. legendData = [] legendBfb = [] 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, series[i].pieData.value, ) startValue = endValue let bfb = fomatFloat(series[i].pieData.value / sumValue, 4) legendData.push({ name: series[i].name, value: bfb, }) legendBfb.push({ name: series[i].name, value: bfb, }) } let boxHeight = getHeight3D(series, 23) //Set the height of the 3D cake/ring by passing parameters, 26 represents 26px // Prepare the configuration items to be returned, and pass in the prepared legendData and series. let option = { //Legend component legend: { data: legendData, //The layout orientation of the legend list. icon: 'square', orient: 'horizontal', left: 'center', bottom: 50, //The space between each item of legend text itemGap: 15, textStyle: { color: '#fff', }, show: true, //Format the legend text (what value is displayed) formatter: function (name: any) { var target for (var i = 0, l = pieData.length; i < l; i + + ) { if (pieData[i].name == name) { target = pieData[i].value } } return `${name}: ${target}` }, // This can display the percentage (can be configured according to what you want) // formatter: function(param) { // let item = legendBfb.filter(item => item.name == param)[0]; // let bfs = that.fomatFloat(item.value * 100, 2) + "%"; // console.log(item.name) // return `${item.name} :${bfs}`; // } }, //Move the text content of the prompt (I didn't have time to change it, you can change it according to your needs) tooltip: { formatter: (params: any) => { if (params.seriesName !== 'mouseoutSeries' & amp; & amp; params.seriesName !== 'pie2d') { let bfb = ( (option.series[params.seriesIndex].pieData.endRatio - option.series[params.seriesIndex].pieData.startRatio) * 100 ).toFixed(2) return ( `${params.seriesName}<br/>` + `<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span >` + `${bfb}` ) } }, }, //This can be transformed xAxis3D: { min: -1, max: 1, }, yAxis3D: { min: -1, max: 1, }, zAxis3D: { min: -1, max: 1, }, //Here is the focus of modifying the style grid3D: { show: false, boxHeight: boxHeight, //The height of the ring //This is the position of the pie chart top: '5%', left: '0%', viewControl: { //3D effects can be enlarged, rotated, etc. Please check the official configuration yourself. alpha: 30, //Angle (this is very important to adjust the angle) distance: 200, //Adjust the distance from the perspective to the subject, similar to adjusting zoom (this is the overall size) rotateSensitivity: 0, //Set to 0 and cannot rotate zoomSensitivity: 0, //Set to 0 and cannot zoom panSensitivity: 0, //Set to 0 and cannot pan autoRotate: false, //Automatic rotation }, }, series: series, } return option } //Get the height of the highest sector of the 3D C image const getHeight3D = (series: any, height: any) => { series.sort((a: any, b: any) => { return b.pieData.value - a.pieData.value }) return (height * 18) / series[0].pieData.value } // Generate sector-shaped surface parametric equations for series-surface.parametricEquation const getParametricEquation = ( startRatio: any, endRatio: any, isSelected: any, isHovered: any, k: any, h: any, ) => { // calculate let midRatio = (startRatio + endRatio) / 2 let startRadian = startRatio * Math.PI * 2 let endRadian = endRatio * Math.PI * 2 let midRadian = midRatio * Math.PI * 2 // If there is only one sector, the selection effect will not be achieved. if (startRatio === 0 & amp; & amp; endRatio === 1) { isSelected = false } // Convert the auxiliary parameter k (default value 1/3) through the value of the sector inner diameter/outer diameter k = typeof k !== 'undefined' ? k : 1 / 3 // Calculate the displacement of the selected effect in the x-axis and y-axis directions respectively (if it is not selected, the displacement is 0) let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0 let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0 // Calculate the magnification ratio of the highlight effect (if not highlighted, the ratio is 1) let hoverRate = isHovered ? 1.05 : 1 // Return the surface parametric equation 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: any, v: any) { 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: any, v: any) { 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: any, v: any) { 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 }, } } const fomatFloat = (num: any, n: any) => { var f = parseFloat(num) if (isNaN(f)) {<!-- --> return false } f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n) // n power var s = f.toString() var rs = s.indexOf('.') //Determine if it is an integer, add the decimal point and add 0 if (rs < 0) {<!-- --> rs = s.length s + = '.' } while (s.length <= rs + n) {<!-- --> s + = '0' } return s } const bindListen = (myChart: any) => {<!-- --> // Listen to mouse events to achieve pie chart selection effect (single selection) and approximate highlighting (enlarging) effect. let selectedIndex = '' let hoveredIndex = '' // Monitor click events to achieve selection effect (single selection) myChart.on('click', function (params: any) {<!-- --> // Read the parameters required to re-render the sector from option.series, and invert whether it is selected. let isSelected = !option.series[params.seriesIndex].pieStatus.selected let isHovered = option.series[params.seriesIndex].pieStatus.hovered let k = option.series[params.seriesIndex].pieStatus.k let startRatio = option.series[params.seriesIndex].pieData.startRatio let endRatio = option.series[params.seriesIndex].pieData.endRatio // If other sectors have been selected before, unselect them (update option) if (selectedIndex !== '' & amp; & amp; selectedIndex !== params.seriesIndex) {<!-- --> option.series[selectedIndex].parametricEquation = getParametricEquation( option.series[selectedIndex].pieData.startRatio, option.series[selectedIndex].pieData.endRatio, false, false, k, option.series[selectedIndex].pieData.value, ) option.series[selectedIndex].pieStatus.selected = false } // Perform the selection/unselection operation on the currently clicked sector (update option) option.series[params.seriesIndex].parametricEquation = getParametricEquation( startRatio, endRatio, isSelected, isHovered, k, option.series[params.seriesIndex].pieData.value, ) option.series[params.seriesIndex].pieStatus.selected = isSelected // If this is a selection operation, record the series number corresponding to the last selected sector seriesIndex isSelected ? (selectedIndex = params.seriesIndex) : null //Use the updated option to render the chart myChart.setOption(option) }) // Monitor mouseover to approximate the highlighting (amplification) effect myChart.on('mouseover', function (params: any) {<!-- --> // Prepare the parameters required to re-render the sector let isSelected let isHovered let startRatio let endRatio let k // If the sector that triggered mouseover is currently highlighted, no operation will be performed. if (hoveredIndex === params.seriesIndex) {<!-- --> return // Otherwise, perform highlighting and necessary unhighlighting operations } else {<!-- --> // If there is currently a highlighted sector, cancel its highlighted state (update option) if (hoveredIndex !== '') {<!-- --> // Read the parameters required to re-render the sector from option.series, and set whether to highlight or not to false. isSelected = option.series[hoveredIndex].pieStatus.selected isHovered = false startRatio = option.series[hoveredIndex].pieData.startRatio endRatio = option.series[hoveredIndex].pieData.endRatio k = option.series[hoveredIndex].pieStatus.k // Perform the unhighlighting operation on the currently clicked sector (update option) option.series[hoveredIndex].parametricEquation = getParametricEquation( startRatio, endRatio, isSelected, isHovered, k, option.series[hoveredIndex].pieData.value, ) option.series[hoveredIndex].pieStatus.hovered = isHovered // Clear the seriesIndex of the previously recorded series number corresponding to the last selected sector. hoveredIndex = '' } // If the sector that triggers mouseover is not a transparent ring, highlight it (update option) if (params.seriesName !== 'mouseoutSeries' & amp; & amp; params.seriesName !== 'pie2d') {<!-- --> // Read the parameters required to re-render the sector from option.series, and set whether to highlight or not to true. isSelected = option.series[params.seriesIndex].pieStatus.selected isHovered = true startRatio = option.series[params.seriesIndex].pieData.startRatio endRatio = option.series[params.seriesIndex].pieData.endRatio k = option.series[params.seriesIndex].pieStatus.k // Perform highlighting operation on the currently clicked sector (update option) option.series[params.seriesIndex].parametricEquation = getParametricEquation( startRatio, endRatio, isSelected, isHovered, k, option.series[params.seriesIndex].pieData.value + 5, ) option.series[params.seriesIndex].pieStatus.hovered = isHovered //Record the series number corresponding to the last highlighted sector seriesIndex hoveredIndex = params.seriesIndex } //Use the updated option to render the chart myChart.setOption(option) } }) //Fix the bug of failure to cancel highlighting myChart.on('globalout', function () {<!-- --> // Prepare the parameters required to re-render the sector let isSelected let isHovered let startRatio let endRatio let k if (hoveredIndex !== '') {<!-- --> // Read the parameters required to re-render the sector from option.series, and set whether to highlight or not to true. isSelected = option.series[hoveredIndex].pieStatus.selected isHovered = false k = option.series[hoveredIndex].pieStatus.k startRatio = option.series[hoveredIndex].pieData.startRatio endRatio = option.series[hoveredIndex].pieData.endRatio // Perform the unhighlighting operation on the currently clicked sector (update option) option.series[hoveredIndex].parametricEquation = getParametricEquation( startRatio, endRatio, isSelected, isHovered, k, option.series[hoveredIndex].pieData.value, ) option.series[hoveredIndex].pieStatus.hovered = isHovered // Clear the previously recorded series number seriesIndex corresponding to the last selected sector. hoveredIndex = '' } //Use the updated option to render the chart myChart.setOption(option) }) } }, } </script> <style lang="less" scoped> .water-eval-container {<!-- --> width: 100%; height: 100%; position: relative; } .cityGreenLand-charts {<!-- --> height: 100%; width: 100%; position: relative; } .bg {<!-- --> // position: absolute; // bottom: 90px; // left: 50%; // z-index: 2; // width: 250px; // height: 150px; // background: no-repeat center; // background-image: url('https://ks3-cn-beijing.ksyun.com/sxjg-elevator/datav-platform-2.0/images/chart_opacity_bg.png'); // background-size: 100% 100%; // transform: translateX(-50%); // opacity: .5; } </style>