javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > React echarts写3d旋转扇形图

React中如何使用echarts写出3d旋转扇形图

作者:苦逼的猿宝

这篇文章主要给大家介绍了关于React中如何使用echarts写出3d旋转扇形图,介绍了如何使用React、TypeScript、Less和Echarts来构建高效、可维护的前端应用代码结构,并提供了相关代码示例,需要的朋友可以参考下

效果

技术

 React + TypeScript + Less + Echarts

代码块

import * as echarts from "echarts";
import React, { useEffect, useRef } from "react";
import "echarts-gl";
import "./index.less";

const LeftEcharts = () => {
    const chartDom = useRef(null);

    useEffect(() => {
        const myChart = echarts.init(chartDom.current);
        // 数据源
        const optionsData: any = [
            {
                name: "IT运营管控团队",
                value: 1000,
                itemStyle: {
                    color: "#dd4b3d",
                },
            },
            {
                name: "业务支撑团队",
                value: 600,
                itemStyle: {
                    color: "#dd9c3c",
                },
            },
            {
                name: "计费结算团队",
                value: 900,
                itemStyle: {
                    color: "#f6bb50",
                },
            },
            {
                name: "数据应用运营团队",
                value: 800,
                itemStyle: {
                    color: "#5ec7f8",
                },
            },
            {
                name: "Paas组件运营团队",
                value: 400,
                itemStyle: {
                    color: "#31dda1",
                },
            },
            {
                name: "云数安全团队",
                value: 300,
                itemStyle: {
                    color: "#637aff",
                },
            },
        ];

        // 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
        function 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;
            // }
            isSelected = false;
            // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
            k = typeof k !== "undefined" ? k : 1 / 3;

            // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
            let offsetX = isSelected ? Math.sin(midRadian) * 0.1 : 0;
            let offsetY = isSelected ? Math.cos(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;
                },
            };
        }

        // 生成模拟 3D 饼图的配置项
        function getPie3D(pieData: any, internalDiameterRatio) {
            let series: any = [];
            let sumValue = 0;
            let startValue = 0;
            let endValue = 0;
            let legendData: any = [];
            let k =
                typeof internalDiameterRatio !== "undefined"
                    ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio)
                    : 1 / 3;
            // 为每一个饼图数据,生成一个 series-surface 配置
            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: 1 / 10,
                    },
                };
                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);
            }
            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;
                legendData.push(series[i].name);
            }
            return series;
        }

        const series: any = getPie3D(optionsData, 0.6);
        series.push({
            name: "pie2d",
            type: "pie",
            label: {
                opacity: 1,
                fontSize: 14,
                lineHeight: 20,
                textStyle: {
                    fontSize: 14,
                    color: "#fff",
                },
                show: false,
                position: "center",
            },
            labelLine: {
                length: 10,
                length2: 10,
                show: false,
            },
            startAngle: 2, //起始角度,支持范围[0, 360]。
            clockwise: false, //饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式
            radius: ["50%", "60%"],
            center: ["62%", "50%"],
            data: optionsData,
            itemStyle: {
                opacity: 0,
            },
        });
        // 准备待返回的配置项,把准备好的 legendData、series 传入。
        const option = {
            legend: {
                show: true, // 显示图例
                tooltip: {
                    show: true, // 显示图例的提示信息
                },
                orient: "vertical", // 图例的排列方向
                data: ["IT运营管控团队", "业务支撑团队", "计费结算团队", "数据应用运营团队", "Paas组件运营团队", '云数安全团队'], // 图例的内容
                top: 20, // 图例距离顶部的距离
                itemGap: 10, // 图例项之间的间距
                itemHeight: 20, // 图例项的高度
                itemWidth: 24, // 图例项的宽度
                right: "5%", // 图例距离右边的距离
                textStyle: { // 图例的文本样式
                    color: "#fff", // 文本颜色
                    fontSize: 10, // 文本字体大小
                    rich: {
                        name: {
                            width: 60, // 名称部分的宽度
                            fontSize: 14, // 名称部分字体大小
                            color: "#B0D8DF", // 名称部分颜色
                            fontFamily: "Source Han Sans CN", // 名称部分字体
                        },
                        value: {
                            width: 50, // 数值部分的宽度
                            fontSize: 4, // 数值部分字体大小
                            padding: [0, 5, 0, 5], // 数值部分的内边距
                            color: "#fff", // 数值部分颜色
                            fontFamily: "Source Han Sans CN", // 数值部分字体
                        },
                        A: {
                            fontSize: 20, // A部分的字体大小
                            color: "#B0D8DF", // A部分颜色
                            fontFamily: "Source Han Sans CN", // A部分字体
                        },
                        rate: {
                            width: 60, // 比率部分的宽度
                            fontSize: 14, // 比率部分字体大小
                            padding: [0, 5, 0, 10], // 比率部分的内边距
                            color: "#579ed2", // 比率部分颜色
                            fontFamily: "Source Han Sans CN", // 比率部分字体
                        },
                    },
                },
                formatter: function (name) { // 格式化图例项的显示内容
                    let total = 0; // 总值
                    let target; // 目标值
                    for (let i = 0; i < optionsData.length; i++) {
                        total += optionsData[i].value; // 计算总值
                        if (optionsData[i].name === name) { // 查找目标值
                            target = optionsData[i].value;
                        }
                    }
                    let arr = [
                        "{name|" + name + "}", // 名称
                        "{value|" + "}", // 数值(未赋值需补充)
                        "{rate|" + ((target / total) * 100).toFixed(1) + "%}", // 比率
                    ];
                    return arr.join(""); // 返回格式化后的字符串
                },
            },
            animation: true, // 开启动画效果
            tooltip: {
                backgroundColor: "rgba(64, 180, 176, 0.6)", // 提示框的背景颜色
                borderColor: "rgba(64, 180, 176, 0.6)", // 提示框的边框颜色
                textStyle: {
                    color: "#fff", // 提示文本颜色
                    fontSize: 24, // 提示文本字体大小
                },
                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 + "万人"
                            }`; // 返回系列名称和数值
                    }
                },
            },
            labelLine: {
                show: true, // 显示标签连接线
                lineStyle: {
                    color: "#7BC0CB", // 标签连接线颜色
                },
                normal: {
                    show: true, // 正常状态显示
                    length: 10, // 连接线长度
                    length2: 10, // 连接线第二段长度
                },
            },
            label: {
                show: true, // 显示标签
                position: "outside", // 标签位置
                formatter: "{b} \n{c}\n{d}%", // 标签格式
                textStyle: {
                    color: "rgba(176, 216, 223, 1)", // 标签文本颜色
                    fontSize: 24, // 标签字体大小
                },
            },
            xAxis3D: {
                min: -1, // x轴最小值
                max: 1, // x轴最大值
            },
            yAxis3D: {
                min: -1, // y轴最小值
                max: 1, // y轴最大值
            },
            zAxis3D: {
                min: -1, // z轴最小值
                max: 1, // z轴最大值
            },
            grid3D: {
                show: false, // 是否显示3D网格
                boxHeight: 1, // 3D盒子的高度
                left: -40, // 3D图形左边距
                top: -10, // 3D图形顶部边距
                width: "50%", // 3D图形宽度
                viewControl: {
                    distance: 280, // 视距
                    alpha: 20, // 视角的俯仰角
                    beta: 15, // 视角的旋转角
                    autoRotate: true, // 是否自动旋转
                    rotateSensitivity: 1, // 旋转灵敏度
                    zoomSensitivity: 0, // 缩放灵敏度
                    panSensitivity: 0, // 平移灵敏度
                },
            },
            series: series, // 数据系列
        };
        

        myChart.setOption(option);
    }, []);

    return (
        <div className='left-echarts'>
            <div className='left-top-nav'>
                团队概况
            </div>
            <div style={{ width: "496px", height: "270px", position: "relative" }}>
                <div ref={chartDom} style={{ width: "100%", height: "100%", zIndex: "5" }}></div>
                <div className="bg"></div>
            </div>
        </div>
    );
};

export default LeftEcharts;

总结 

到此这篇关于React中如何使用echarts写出3d旋转扇形图的文章就介绍到这了,更多相关React echarts写3d旋转扇形图内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文