vue3.0 使用echarts与echarts-gl 实现3D饼图

效果

安装echarts

npm install echarts

npm install echarts-gl

3d饼图组件:

<template>
    <div style="width: 100%; height: 100%" ref="echart"></div>
</template>

<script setup>
import { reactive, ref, onMounted, watch } from 'vue'
import * as echarts from 'echarts'
import 'echarts-gl'

const boxHeight = ref([])
const legendData = ref([])
const echart = ref()

const props = defineProps({
    optionsData: []
})

const echartInit = () => {
    var myChart = echarts.init(echart.value)
    const series = getPie3D(props.optionsData, 0.7)
    series.push({
        center: ['10%', '90%'],
        name: 'pie2d',
        type: 'pie',
        label: {
        show: false,
        opacity: 1,
        fontSize: 12,
        lineHeight: 10,
        textStyle: {
            fontSize: 12,
            color: '#fff',
        },
        },
        labelLine: {
        length: 30,
        length2: 30,
        },
        startAngle: -30, //起始角度,支持范围[0, 360]。
        clockwise: false, //饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式
        radius: ['40%', '60%'],
        //data: props.optionsData,
        data: props.optionsData.map(item => {
          item.itemStyle.opacity = 0
          return item
        }),

        itemStyle: {
        opacity: 0,
        
        }
    })
    // 准备待返回的配置项,把准备好的 legendData、series 传入。
    let option = {
        legend: {
        show: true,
        tooltip: {
            show: true,
        },
        orient: 'vertical',
        //data: ['待办', '已办', '未处理'],
        data: legendData.value,
        top: 'center',
        // 设置图例为矩形
        itemWidth: 14,   // 图例标记的宽度,默认为14
        itemHeight: 14,  // 图例标记的高度,默认为12
        itemStyle: {
            // 设置边框圆角,可以设置为 0 实现正方形
            borderRadius: 5
        },
        itemGap: 12,
        right: '2%',
        textStyle: {
            color: '#fff',
            fontSize: 12,
        },
        formatter: (param)=> {
            let item = legendData.value.filter(item => item.name == param)[0];
            let bfs = fomatFloat(item.value * 100, 2) + "%";
            let v = item.value2;
            //return `${item.name}  ${bfs}`;
            return `${item.name}  ${v}`;
        }
        },
        animation: true,
        tooltip: {
        formatter: (params) => {
            if (
            params.seriesName !== 'mouseoutSeries' &&
            params.seriesName !== 'pie2d'
            ) {
            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 + '台'
            }`
            }
        },
        textStyle: {
            fontSize: 12,
        },
        },
        title: {
        x: 'center',
        top: '20',
        textStyle: {
            color: '#fff',
            fontSize: 12,
        },
        },
        // backgroundColor: '#FFF',
        labelLine: {
        show: false,
        lineStyle: {
            color: '#7BC0CB',
        },
        normal: {
            show: false,
            length: 10,
            length2: 10,
        },
        },
        label: {
        show: false,
        position: 'outside',
        formatter: '{b} \n{d}%',
        textStyle: {
            color: '#fff',
            fontSize: '14px',
        },
        },
        xAxis3D: {
        min: -1,
        max: 1,
        },
        yAxis3D: {
        min: -1,
        max: 1,
        },
        zAxis3D: {
        min: -1,
        max: 1,
        },
        grid3D: {
        show: false,
        //boxHeight: 0.01,
        boxHeight: boxHeight.value,
        //top: '30%',
        bottom: '50%',
        left: '-18%',
        // environment: "rgba(255,255,255,0)",
        viewControl: {
            distance: 180,//这个数值越大图就越小
            alpha: 25,//倾斜角度
            beta: 60,//起始渲染角度
            autoRotate: false, // 自动旋转
            rotateSensitivity: 1,//旋转灵敏度,鼠标按住不放可进行角度偏移
            zoomSensitivity: 1,//缩放灵敏度,鼠标滚轮可修改大小
            panSensitivity: 0,// 平移操作的灵敏度,值越大越灵敏。默认为1,设置为0后无法平移。支持使用数组分别设置横向和纵向的平移灵敏度
        },
        },
        series: series,
    }
    // 使用刚指定的配置项和数据显示图表。
    myChart.setOption(option)
    }
    
    function fomatFloat(num, n) {
    var f = parseFloat(num);
    if (isNaN(f)) {
        return false;
    }
    f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n); // n 幂   
    var s = f.toString();
    var rs = s.indexOf('.');
    //判定如果是整数,增加小数点再补0
    if (rs < 0) {
        rs = s.length;
        s += '.';
    }
    while (s.length <= rs + n) {
        s += '0';
    }
    return s;
}

// 获取3d饼图的最高扇区的高度
function getHeight3D(series, height) {
    series.sort((a, b) => {
        return (b.pieData.value - a.pieData.value);
    })
    return height * 25 / series[0].pieData.value;
}
function getParametricEquation(
    startRatio,
    endRatio,
    isSelected,
    isHovered,
    k,
    height,
) {

    // 计算
    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 * height : -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

    pieData.sort((a, b) => {
        return (b.value - a.value);
    });

    // 为每一个饼图数据,生成一个 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,
        series[i].pieData.value,
        )
        boxHeight.value = getHeight3D(series, 2);//通过传参设定3d饼/环的高度,26代表26px

        startValue = endValue

        //legendData.value.push(series[i].name)
        let bfb = fomatFloat(series[i].pieData.value / sumValue, 4);
        legendData.value.push({
        name: series[i].name,
        value: bfb,
        value2: series[i].pieData.value
    });
    }
    return series
    }

watch(
    () => [props.optionsData],
    () => {
        echartInit()
    }
)

onMounted(() => {
    echartInit()
})


</script>

vue中的使用:

<My3DPie :optionsData="optionsData" />

vue的js引入组件

// 传入数据生成 option
         const optionsData = ref([
            {
              name: '饮料',
              value: 36,
              itemStyle: {
                opacity: 0.8,
                color: '#14EC92',
              },
            },
            {
              name: '小食品',
              value: 29,
              itemStyle: {
                opacity: 0.8,
                color: '#42A2D8',
              },
            }
            
          ])
相关推荐
web Rookie3 分钟前
React 中 createContext 和 useContext 的深度应用与优化实战
前端·javascript·react.js
男孩127 分钟前
react高阶组件及hooks
前端·javascript·react.js
程序员-小李18 分钟前
餐厅下单助手系统(Java+MySQL)
java·开发语言·mysql
开心工作室_kaic22 分钟前
springboot496基于java手机销售网站设计和实现(论文+源码)_kaic
java·开发语言·智能手机
像少年啦飞驰点、24 分钟前
SpringBoot + HttpSession 自定义生成sessionId
java·开发语言
m0_7482517227 分钟前
DataOps驱动数据集成创新:Apache DolphinScheduler & SeaTunnel on Amazon Web Services
前端·apache
珊珊来吃28 分钟前
EXCEL中给某一列数据加上双引号
java·前端·excel
raysync88839 分钟前
替代传统FTP传输,镭速大数据传输系统实现安全高效数据流转!
开发语言·安全·php
mask哥40 分钟前
算法:LeetCode470_用Rand7()实现Rand10()_java实现
java·开发语言
cr.sheeper1 小时前
CTFHUB-web进阶-php
开发语言·php