vue3之echarts3D环柱图-间隔版

vue3之echarts3D环柱图-间隔版

效果:
vue3之echarts3D环柱图-间隔版_第1张图片

版本
"echarts": "^5.4.1", "echarts-gl": "^2.0.9"

核心代码:

<template>
    <div class="content">
        <div ref="eCharts" class="chart"></div>
        <div ref="eCharts2" class="chart"></div>
    </div>
</template>

<script setup>
import { onMounted, ref } from "vue";
import * as echarts from "echarts";
import "echarts-gl";

const eCharts = ref(null);
let myChart = null;
const eCharts2 = ref(null);
let myChart2 = null;

let boxHeight;
let optionData = ref([
    {
        name: "已核销",
        value: 10000,
        itemStyle: {
            color: "rgba(91, 149, 255, 1)",
        },
        isShow: true
    },
    {
        name: "未核销",
        value: 12116,
        itemStyle: {
            color: "transportant",
        },
        isShow: false
    },
]);
let optionData2 = ref([
    {
        name: "已核销",
        value: 10000,
        itemStyle: {
            color: "transportant",
        },
        isShow: false
    },
    {
        name: "未核销",
        value: 12116,
        itemStyle: {
            color: "rgba(66, 250, 251, 1)",
        },
        isShow: true
    },
]);

onMounted(() => {
    initCharts(myChart, eCharts.value, optionData.value, 0);
    initCharts(myChart2, eCharts2.value, optionData2.value, '-4%');
});

const getParametricEquation = (
    startRatio,
    endRatio,
    isSelected,
    isHovered,
    k,
    h
) => {
    // 计算
    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) * h * 0.1;
            }
            return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
        },
    };
};

const getPie3D = (pieData, internalDiameterRatio) => {
    // internalDiameterRatio:透明的空心占比
    let series = [];
    let sumValue = 0;
    let startValue = 0;
    let endValue = 0;

    let k = 1 - internalDiameterRatio;
    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,
            },
            center: ["10%", "50%"],
        };

        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 // 控制各模块高度一致100   控制各模块高度根据value改变
        );
        startValue = endValue;
    }
    boxHeight = getHeight3D(series, 15); //通过传参设定3d饼/环的高度,26代表26PX
    return series;
};

const getHeight3D = (series, height) => {
    series.sort((a, b) => {
        return b.pieData.value - a.pieData.value;
    });
    return (height * 8) / series[0].pieData.value;
};

const initCharts = (chart, chartContainer, optionData, top) => {
    chart = echarts.init(chartContainer);
    const series = getPie3D(optionData, 0.8);
    series.push({
        name: "pie2d",
        type: "pie",
        label: {
            opacity: 1,
            fontSize: 12,
            lineHeight: 12,
        },
        labelLine: {
            length: 10,
            length2: 40,
        },
        startAngle: -40, // 起始角度,支持范围[0, 360]。
        clockwise: false, // 饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式
        radius: ["10%", "52%"],
        center: ["50%", "50%"],
        data: optionData,
        itemStyle: {
            opacity: 0,
        },
    });
    optionData.forEach((item, index) => {
        item.label = {
            color: item.itemStyle.color,
            show: item.isShow,
            formatter: (item) => {
                return `{b|${item.name} }`;
            },
            rich: {
                b: {
                    fontSize: 12,
                    lineHeight: 20,
                },
                c: {
                    color: "#fff",
                    fontSize: 12,
                },
            },
        };
    });

    let option = {
        xAxis3D: {
            min: -1,
            max: 1,
        },
        yAxis3D: {
            min: -1,
            max: 1,
        },
        zAxis3D: {
            min: -1,
            max: 1,
        },
        grid3D: {
            show: false,
            top, //距离上边的间距
            boxHeight, //圆环的高度
            viewControl: {
                //3d效果可以放大、旋转等,请自己去查看官方配置
                alpha: 32, //角度
                distance: 220, //调整视角到主体的距离,类似调整zoom
                rotateSensitivity: 0, //设置为0无法旋转
                zoomSensitivity: 0, //设置为0无法缩放
                panSensitivity: 0, //设置为0无法平移
                autoRotate: false, //自动旋转
            },
        },
        series,
    };
    chart?.setOption(option);
};

</script>
<style lang="scss" scoped>
.content {
    position: relative;

    .chart {
        position: absolute;
        top: -28px;
        left: -15px;
        width: 434px;
        height: 300px;
    }
}
</style>

你可能感兴趣的:(echarts,echarts,3d,vue.js)