翼度科技»论坛 编程开发 JavaScript 查看内容

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

3

主题

3

帖子

9

积分

新手上路

Rank: 1

积分
9
效果



技术
  1.  React + TypeScript + Less + Echarts
复制代码
代码块
  1. import * as echarts from "echarts";
  2. import React, { useEffect, useRef } from "react";
  3. import "echarts-gl";
  4. import "./index.less";

  5. const LeftEcharts = () => {
  6.     const chartDom = useRef(null);

  7.     useEffect(() => {
  8.         const myChart = echarts.init(chartDom.current);
  9.         // 数据源
  10.         const optionsData: any = [
  11.             {
  12.                 name: "IT运营管控团队",
  13.                 value: 1000,
  14.                 itemStyle: {
  15.                     color: "#dd4b3d",
  16.                 },
  17.             },
  18.             {
  19.                 name: "业务支撑团队",
  20.                 value: 600,
  21.                 itemStyle: {
  22.                     color: "#dd9c3c",
  23.                 },
  24.             },
  25.             {
  26.                 name: "计费结算团队",
  27.                 value: 900,
  28.                 itemStyle: {
  29.                     color: "#f6bb50",
  30.                 },
  31.             },
  32.             {
  33.                 name: "数据应用运营团队",
  34.                 value: 800,
  35.                 itemStyle: {
  36.                     color: "#5ec7f8",
  37.                 },
  38.             },
  39.             {
  40.                 name: "Paas组件运营团队",
  41.                 value: 400,
  42.                 itemStyle: {
  43.                     color: "#31dda1",
  44.                 },
  45.             },
  46.             {
  47.                 name: "云数安全团队",
  48.                 value: 300,
  49.                 itemStyle: {
  50.                     color: "#637aff",
  51.                 },
  52.             },
  53.         ];

  54.         // 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
  55.         function getParametricEquation(
  56.             startRatio,
  57.             endRatio,
  58.             isSelected,
  59.             isHovered,
  60.             k,
  61.             h
  62.         ) {
  63.             // 计算
  64.             let midRatio = (startRatio + endRatio) / 2;

  65.             let startRadian = startRatio * Math.PI * 2;
  66.             let endRadian = endRatio * Math.PI * 2;
  67.             let midRadian = midRatio * Math.PI * 2;

  68.             // 如果只有一个扇形,则不实现选中效果。
  69.             // if (startRatio === 0 && endRatio === 1) {
  70.             //     isSelected = false;
  71.             // }
  72.             isSelected = false;
  73.             // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
  74.             k = typeof k !== "undefined" ? k : 1 / 3;

  75.             // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
  76.             let offsetX = isSelected ? Math.sin(midRadian) * 0.1 : 0;
  77.             let offsetY = isSelected ? Math.cos(midRadian) * 0.1 : 0;

  78.             // 计算高亮效果的放大比例(未高亮,则比例为 1)
  79.             let hoverRate = isHovered ? 1.05 : 1;

  80.             // 返回曲面参数方程
  81.             return {
  82.                 u: {
  83.                     min: -Math.PI,
  84.                     max: Math.PI * 3,
  85.                     step: Math.PI / 32,
  86.                 },

  87.                 v: {
  88.                     min: 0,
  89.                     max: Math.PI * 2,
  90.                     step: Math.PI / 20,
  91.                 },

  92.                 x: function (u, v) {
  93.                     if (u < startRadian) {
  94.                         return (
  95.                             offsetX +
  96.                             Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
  97.                         );
  98.                     }
  99.                     if (u > endRadian) {
  100.                         return (
  101.                             offsetX +
  102.                             Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
  103.                         );
  104.                     }
  105.                     return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
  106.                 },

  107.                 y: function (u, v) {
  108.                     if (u < startRadian) {
  109.                         return (
  110.                             offsetY +
  111.                             Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
  112.                         );
  113.                     }
  114.                     if (u > endRadian) {
  115.                         return (
  116.                             offsetY +
  117.                             Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
  118.                         );
  119.                     }
  120.                     return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
  121.                 },

  122.                 z: function (u, v) {
  123.                     if (u < -Math.PI * 0.5) {
  124.                         return Math.sin(u);
  125.                     }
  126.                     if (u > Math.PI * 2.5) {
  127.                         return Math.sin(u) * h * 0.1;
  128.                     }
  129.                     return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
  130.                 },
  131.             };
  132.         }

  133.         // 生成模拟 3D 饼图的配置项
  134.         function getPie3D(pieData: any, internalDiameterRatio) {
  135.             let series: any = [];
  136.             let sumValue = 0;
  137.             let startValue = 0;
  138.             let endValue = 0;
  139.             let legendData: any = [];
  140.             let k =
  141.                 typeof internalDiameterRatio !== "undefined"
  142.                     ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio)
  143.                     : 1 / 3;
  144.             // 为每一个饼图数据,生成一个 series-surface 配置
  145.             for (let i = 0; i < pieData.length; i++) {
  146.                 sumValue += pieData[i].value;
  147.                 let seriesItem: any = {
  148.                     name:
  149.                         typeof pieData[i].name === "undefined"
  150.                             ? `series${i}`
  151.                             : pieData[i].name,
  152.                     type: "surface",
  153.                     parametric: true,
  154.                     wireframe: {
  155.                         show: false,
  156.                     },
  157.                     pieData: pieData[i],
  158.                     pieStatus: {
  159.                         selected: false,
  160.                         hovered: false,
  161.                         k: 1 / 10,
  162.                     },
  163.                 };
  164.                 if (typeof pieData[i].itemStyle != "undefined") {
  165.                     let itemStyle: any = {};
  166.                     typeof pieData[i].itemStyle.color != "undefined" ? (itemStyle.color = pieData[i].itemStyle.color) : null;
  167.                     typeof pieData[i].itemStyle.opacity != "undefined" ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
  168.                     seriesItem.itemStyle = itemStyle;
  169.                 }
  170.                 series.push(seriesItem);
  171.             }
  172.             for (let i = 0; i < series.length; i++) {
  173.                 endValue = startValue + series[i].pieData.value;
  174.                 series[i].pieData.startRatio = startValue / sumValue;
  175.                 series[i].pieData.endRatio = endValue / sumValue;
  176.                 series[i].parametricEquation = getParametricEquation(
  177.                     series[i].pieData.startRatio,
  178.                     series[i].pieData.endRatio,
  179.                     false,
  180.                     false,
  181.                     k,
  182.                     series[i].pieData.value
  183.                 );
  184.                 startValue = endValue;
  185.                 legendData.push(series[i].name);
  186.             }
  187.             return series;
  188.         }

  189.         const series: any = getPie3D(optionsData, 0.6);
  190.         series.push({
  191.             name: "pie2d",
  192.             type: "pie",
  193.             label: {
  194.                 opacity: 1,
  195.                 fontSize: 14,
  196.                 lineHeight: 20,
  197.                 textStyle: {
  198.                     fontSize: 14,
  199.                     color: "#fff",
  200.                 },
  201.                 show: false,
  202.                 position: "center",
  203.             },
  204.             labelLine: {
  205.                 length: 10,
  206.                 length2: 10,
  207.                 show: false,
  208.             },
  209.             startAngle: 2, //起始角度,支持范围[0, 360]。
  210.             clockwise: false, //饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式
  211.             radius: ["50%", "60%"],
  212.             center: ["62%", "50%"],
  213.             data: optionsData,
  214.             itemStyle: {
  215.                 opacity: 0,
  216.             },
  217.         });
  218.         // 准备待返回的配置项,把准备好的 legendData、series 传入。
  219.         const option = {
  220.             legend: {
  221.                 show: true, // 显示图例
  222.                 tooltip: {
  223.                     show: true, // 显示图例的提示信息
  224.                 },
  225.                 orient: "vertical", // 图例的排列方向
  226.                 data: ["IT运营管控团队", "业务支撑团队", "计费结算团队", "数据应用运营团队", "Paas组件运营团队", '云数安全团队'], // 图例的内容
  227.                 top: 20, // 图例距离顶部的距离
  228.                 itemGap: 10, // 图例项之间的间距
  229.                 itemHeight: 20, // 图例项的高度
  230.                 itemWidth: 24, // 图例项的宽度
  231.                 right: "5%", // 图例距离右边的距离
  232.                 textStyle: { // 图例的文本样式
  233.                     color: "#fff", // 文本颜色
  234.                     fontSize: 10, // 文本字体大小
  235.                     rich: {
  236.                         name: {
  237.                             width: 60, // 名称部分的宽度
  238.                             fontSize: 14, // 名称部分字体大小
  239.                             color: "#B0D8DF", // 名称部分颜色
  240.                             fontFamily: "Source Han Sans CN", // 名称部分字体
  241.                         },
  242.                         value: {
  243.                             width: 50, // 数值部分的宽度
  244.                             fontSize: 4, // 数值部分字体大小
  245.                             padding: [0, 5, 0, 5], // 数值部分的内边距
  246.                             color: "#fff", // 数值部分颜色
  247.                             fontFamily: "Source Han Sans CN", // 数值部分字体
  248.                         },
  249.                         A: {
  250.                             fontSize: 20, // A部分的字体大小
  251.                             color: "#B0D8DF", // A部分颜色
  252.                             fontFamily: "Source Han Sans CN", // A部分字体
  253.                         },
  254.                         rate: {
  255.                             width: 60, // 比率部分的宽度
  256.                             fontSize: 14, // 比率部分字体大小
  257.                             padding: [0, 5, 0, 10], // 比率部分的内边距
  258.                             color: "#579ed2", // 比率部分颜色
  259.                             fontFamily: "Source Han Sans CN", // 比率部分字体
  260.                         },
  261.                     },
  262.                 },
  263.                 formatter: function (name) { // 格式化图例项的显示内容
  264.                     let total = 0; // 总值
  265.                     let target; // 目标值
  266.                     for (let i = 0; i < optionsData.length; i++) {
  267.                         total += optionsData[i].value; // 计算总值
  268.                         if (optionsData[i].name === name) { // 查找目标值
  269.                             target = optionsData[i].value;
  270.                         }
  271.                     }
  272.                     let arr = [
  273.                         "{name|" + name + "}", // 名称
  274.                         "{value|" + "}", // 数值(未赋值需补充)
  275.                         "{rate|" + ((target / total) * 100).toFixed(1) + "%}", // 比率
  276.                     ];
  277.                     return arr.join(""); // 返回格式化后的字符串
  278.                 },
  279.             },
  280.             animation: true, // 开启动画效果
  281.             tooltip: {
  282.                 backgroundColor: "rgba(64, 180, 176, 0.6)", // 提示框的背景颜色
  283.                 borderColor: "rgba(64, 180, 176, 0.6)", // 提示框的边框颜色
  284.                 textStyle: {
  285.                     color: "#fff", // 提示文本颜色
  286.                     fontSize: 24, // 提示文本字体大小
  287.                 },
  288.                 formatter: (params) => { // 格式化提示框内容
  289.                     if (
  290.                         params.seriesName !== "mouseoutSeries" &&
  291.                         params.seriesName !== "pie2d" // 排除特定系列
  292.                     ) {
  293.                         return `${params.seriesName
  294.                             }<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color
  295.                             };"></span>${option.series[params.seriesIndex].pieData.value + "万人"
  296.                             }`; // 返回系列名称和数值
  297.                     }
  298.                 },
  299.             },
  300.             labelLine: {
  301.                 show: true, // 显示标签连接线
  302.                 lineStyle: {
  303.                     color: "#7BC0CB", // 标签连接线颜色
  304.                 },
  305.                 normal: {
  306.                     show: true, // 正常状态显示
  307.                     length: 10, // 连接线长度
  308.                     length2: 10, // 连接线第二段长度
  309.                 },
  310.             },
  311.             label: {
  312.                 show: true, // 显示标签
  313.                 position: "outside", // 标签位置
  314.                 formatter: "{b} \n{c}\n{d}%", // 标签格式
  315.                 textStyle: {
  316.                     color: "rgba(176, 216, 223, 1)", // 标签文本颜色
  317.                     fontSize: 24, // 标签字体大小
  318.                 },
  319.             },
  320.             xAxis3D: {
  321.                 min: -1, // x轴最小值
  322.                 max: 1, // x轴最大值
  323.             },
  324.             yAxis3D: {
  325.                 min: -1, // y轴最小值
  326.                 max: 1, // y轴最大值
  327.             },
  328.             zAxis3D: {
  329.                 min: -1, // z轴最小值
  330.                 max: 1, // z轴最大值
  331.             },
  332.             grid3D: {
  333.                 show: false, // 是否显示3D网格
  334.                 boxHeight: 1, // 3D盒子的高度
  335.                 left: -40, // 3D图形左边距
  336.                 top: -10, // 3D图形顶部边距
  337.                 width: "50%", // 3D图形宽度
  338.                 viewControl: {
  339.                     distance: 280, // 视距
  340.                     alpha: 20, // 视角的俯仰角
  341.                     beta: 15, // 视角的旋转角
  342.                     autoRotate: true, // 是否自动旋转
  343.                     rotateSensitivity: 1, // 旋转灵敏度
  344.                     zoomSensitivity: 0, // 缩放灵敏度
  345.                     panSensitivity: 0, // 平移灵敏度
  346.                 },
  347.             },
  348.             series: series, // 数据系列
  349.         };
  350.         

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

  353.     return (
  354.         <div className='left-echarts'>
  355.             <div className='left-top-nav'>
  356.                 团队概况
  357.             </div>
  358.             <div style={{ width: "496px", height: "270px", position: "relative" }}>
  359.                 <div ref={chartDom} style={{ width: "100%", height: "100%", zIndex: "5" }}></div>
  360.                 <div className="bg"></div>
  361.             </div>
  362.         </div>
  363.     );
  364. };

  365. export default LeftEcharts;
复制代码
总结

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

来源:https://www.jb51.net/javascript/331242dwf.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具