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>

 

posted @ 2024-06-27 16:11  一文搞懂  阅读(577)  评论(0编辑  收藏  举报