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

devtools/2024/11/20 2:41:45/

效果

技术

 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.parametricEquationfunction 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;


http://www.ppmy.cn/devtools/135359.html

相关文章

WordCloudStudio:全面支持Web端,随时随地创建精彩文字云

在当今视觉化表达日益重要的时代&#xff0c;文字云成为了一种流行的内容呈现形式。无论是用于展示数据、分享创意&#xff0c;还是提高内容吸引力&#xff0c;文字云都能带来意想不到的效果。https://wordcloudstudio.com/ 作为文字云制作的领先工具&#xff0c;现在全面支持W…

Redis自学之路—基础数据结构具体方法解析(五)

目录 简介 数据结果具体方法解析 字符串(String) 操作命令 set设置值 setex setnx get获取值 del删除key mset批量设置值 incr数字运算 append追加指令 strlen字符串长度 getset设置并返回原值 setrange设置指定位置的字符 getrange截取字符串 命令的时间复杂…

Python学习26天

集合 # 定义集合 num {1, 2, 3, 4, 5} print(f"num&#xff1a;{num}\nnum数据类型为&#xff1a;{type(num)}") # 求集合中元素个数 print(f"num中元素个数为&#xff1a;{len(num)}") # 增加集合中的元素 num.add(6) print(num) # {1,2,3,4,5,6} # 删除…

基于微信小程序的河池旅游设计与实现

一、前言 随着移动互联网的快速发展&#xff0c;微信小程序以其便捷性、无需安装等优势受到广泛关注。河池拥有丰富的旅游资源&#xff0c;包括独特的自然风光&#xff08;如巴马长寿之乡的山水、宜州下枧河风光等&#xff09;、多彩的民族文化&#xff08;如壮族铜鼓文化、仫佬…

RTSP播放器EasyPlayer.js播放器UniApp或者内嵌其他App里面webview需要截图下载

EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS、WEBRTC、FMP4视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC、G711A、Mp3等多种音视频编码格式&#xff0c;支持MSE、WASM、WebCodec等多种解码方…

千帆启航,人才先行 | 讯方技术HarmonyOS人才训练营

HarmonyOS NEXT鸿蒙星河版已于1月份面向开发者开放申请&#xff0c;鸿蒙原生应用厂商正在加速开发&#xff0c;预计2024年中旬&#xff0c;将覆盖5000个头部App&#xff0c;鸿蒙原生应用版图基本成型&#xff0c;未来HarmonyOS将继续赋能千行百业。 为响应鸿蒙生态发展对人才需…

设备如何连接到 MySQL Server

通常,设备或其他系统(如你的监控设备)会通过 TCP/IP 协议 连接到 MySQL 服务器。这通常包括以下步骤: 配置 MySQL 允许远程连接 MySQL 默认仅允许本地连接(即只能从 MySQL 服务器所在的机器访问)。要允许远程连接,需要修改 MySQL 配置文件 my.cnf,通常位于 /etc/mysql/…

Pytest-Bdd-Playwright 系列教程(11):场景快捷方式

Pytest-Bdd-Playwright 系列教程&#xff08;11&#xff09;&#xff1a;场景快捷方式 前言1. 手动绑定场景的传统方法2. 场景快捷方式的自动绑定方法2.1 绑定所有场景2.2 绑定多个路径2.3 自动与手动绑定的结合 3. 示例&#xff1a;结合 Playwright 的实际应用3.1 项目目录结构…