echarts 饼图嵌套 二级饼图 子饼图 复合饼图
总体思路:先绘制两个饼图,在画两根线(设置第一个饼图起始角度方便计算,计算线条坐标的时候考虑饼图的位置、半径)
<template> <div class="box" ref="chartRef"></div> </template> <script setup> import * as echarts from "echarts"; import { watch, onMounted, ref, defineProps } from "vue"; let myChart; let chartRef = ref(null); const props = defineProps({ title: { type: String, default: "标题" } }); const data1 = [ { value: 33, name: 'rose 1' }, { value: 33, name: 'rose 2' }, { value: 30, name: 'rose 3' } ] const data2 = [ { value: 30, name: 'ddd 1' }, { value: 28, name: 'ddd 2' }, { value: 26, name: 'ddd 3' }, { value: 24, name: 'ddd 4' }, { value: 22, name: 'ddd 5' }, { value: 20, name: 'ddd 6' }, { value: 18, name: 'ddd 7' }, { value: 16, name: 'ddd 8' } ] let textColor = "white" let markLineData = [] const option = { legend: {}, tooltip: {}, //图例 legend: { show: false, right: '5%',//靠右 orient: 'vertical',//垂直排列 top: 'center', // 上下居中 align: 'left',//文字在图例左侧 itemGap: 20, //图例间距 textStyle: { color: textColor } }, series: [ { type: 'pie', radius: "70%", center: ['30%', '50%'], label: { show: true, position: "inside", }, startAngle: 45, // 起始角度 45 clockwise: false, // 逆时针 markLine: { lineStyle: { type: 'solid', color: "#BFBFBF" }, symbol: 'none', data: markLineData }, data: data1 }, { type: 'pie', radius: "40%", center: ['65%', '30%'], encode: { itemName: 'product', value: '2016', }, label: { show: true, position: "inside" }, data: data2 } ] }; // 获取表标线 对应点坐标 function getMarkLineData(percent) { // 1.获取画布 width,height let height = myChart.getHeight() let width = myChart.getWidth() //2.根据 series[0].center,获取配置 转 数值 let pie1CenterXpercent = convertPercentageStringToDecimal(option.series[0].center[0]) // 圆心1轴坐标X百分比 let pie1CenterYpercent = convertPercentageStringToDecimal(option.series[0].center[1]) // 圆心1轴坐标Y百分比 let pie2CenterXpercent = convertPercentageStringToDecimal(option.series[1].center[0]) // 圆心2轴坐标X百分比 let pie2CenterYpercent = convertPercentageStringToDecimal(option.series[1].center[1]) // 圆心2轴坐标Y百分比 let pie1RadiusPercent = convertPercentageStringToDecimal(option.series[0].radius) //圆心1半径百分比 let pie2RadiusPercent = convertPercentageStringToDecimal(option.series[1].radius) //圆心2半径百分比 let pie1Radius = height / 2 * pie1RadiusPercent // 圆心半径 let pie2Radius = height / 2 * pie2RadiusPercent // 圆心半径 let pie1CenterX = width * pie1CenterXpercent // 圆心1轴坐标X let pie1CenterY = height * pie1CenterYpercent // 圆心1轴坐标Y let pie2CenterX = width * pie2CenterXpercent // 圆心2轴坐标X let pie2CenterY = height * pie2CenterYpercent // 圆心2轴坐标Y //3.圆边上点坐标 let line1StartX = pie1CenterX + pie1Radius * Math.cos(45 * 3.14 / 180) // let line1StartX = pie1CenterX + r * cos(ao * 3.14 /180 ) let line1StartY = pie1CenterY - pie1Radius * Math.sin(45 * 3.14 / 180) // let line1StartY = pie1CenterY - r * sin(ao * 3.14 /180 ) let line1EndX = pie2CenterX let line1EndY = pie2CenterY - pie2Radius let ao = 360 * (percent / 100) // 扇形角度 let ao1 = 0 // 用来计算的坐标角度 ao1 = (ao <= 45) ? (45 - ao) : (360 - (ao - 45)) if (ao1 < 270 && ao1 > 45) ao1 = 270 // 角度当270用,要不样式不好看 let line2StartX = pie1CenterX + pie1Radius * Math.cos(ao1 * 3.14 / 180) let line2StartY = pie1CenterY - pie1Radius * Math.sin(ao1 * 3.14 / 180) let line2EndX = pie2CenterX let line2EndY = pie2CenterY + pie2Radius return [ [{ x: line1StartX, y: line1StartY }, { x: line1EndX, // 线1,终点x值 y: line1EndY // 线1,终点y值 }], [{ x: line2StartX, y: line2StartY }, { x: line2EndX,// 线2,终点x值 y: line2EndY // 线2,终点y值 }] ] } // 带百分号字符串转小数 function convertPercentageStringToDecimal(percentageString) { const percentageValue = parseFloat(percentageString.replace("%", "")) / 100; return percentageValue; } //通过区块的占比(最后一个区块),计算线条起点和终点 const addOtherData = (arrData) => { let end = arrData.length - 1 let sum = 0; for (let i = 0; i < arrData.length; i++) { let value = arrData[i] ? 0 : Number(arrData[i].value) sum += value } let percent = sum ? ((arrData[end].value / sum) * 100).toFixed(2) : 100 option.series[0].markLine.data = getMarkLineData(percent) } const initChart = () => { addOtherData(data1); myChart.setOption(option); }; watch(props, () => { console.log("表格数据变动"); initChart(); }); onMounted(() => { myChart = echarts.init(chartRef.value); initChart(); }); </script> <style scoped> .box { width: 100%; height: 100%; /* width: 100px; height: 100px; */ } </style>