echarts如何实现3D饼图(环形图)?

news2024/12/23 16:25:28

一、实现的效果

二、具体步骤

1.安装依赖

npm install echarts 

2.引入echarts

import * as echarts from 'echarts';

 注意:这里需要用到echarts-gl,必须单独引入才可以

import 'echarts-gl';

3.echarts部分代码

我知道这部分内容很多,但只要cv去用就可以了,getParametricEquation这个函数不用改(我也不知道咋改。。。反正我没动过);getPie3D函数根据自己的需求稍微改一下option配置就好,其余的可以不用管

 // 颜色列表
	const colorList = [
		'rgba(76, 139, 241, 0.9)',
		'rgba(101, 193, 241, 0.9)',
		'rgba(249, 215, 114, 0.9)',
		'rgba(179, 186, 195, 0.9)',
		'rgba(255, 255, 255,  0.9)',
		'rgba(145, 186, 217, 0.9)',
	];

	// 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
	function getParametricEquation(startRatio: any, endRatio: any, isSelected: any, isHovered: any, k: any, h: any) {
		// 计算
		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: any, v: any) {
				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: any, v: any) {
				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: any, v: any) {
				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: any) {
		let series = [];
		let sumValue = 0;
		let startValue = 0;
		let endValue = 0;
		let legendData = [];
		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);
		}

		// 使用上一次遍历时,计算出的数据和 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
			);
			startValue = endValue;
			legendData.push(series[i].name);
		}
		series.push({
			name: 'mouseoutSeries',
			type: 'surface',

			parametric: true,
			wireframe: {
				show: false,
			},
			itemStyle: {
				opacity: 0.2,
				color: 'rgba(165, 247, 253, 1)',
			},
			parametricEquation: {
				u: {
					min: 0,
					max: Math.PI * 2,
					step: Math.PI / 20,
				},
				v: {
					min: 0,
					max: Math.PI / 4,
					step: Math.PI / 20,
				},
				x: function (u: any, v: any) {
					return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2.5;
				},
				y: function (u: any, v: any) {
					return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2.5;
				},
				z: function (u: any, v: any) {
					return Math.cos(v) > 0 ? -3 : -3;
				},
			},
		});

		// 准备待返回的配置项,把准备好的 legendData、series 传入。
		let option = {
			legend: {
				icon: 'circle',
				orient: 'vertical',
				data: pieData.map((dItem: any, dIndex: any) => {
					return {
						...dItem,
						textStyle: {
							rich: {
								percent: {
									color: colorList[dIndex],
								},
							},
						},
					};
				}),
				right: '5%',
				top: '20%',
				itemGap: 10,
				itemWidth: 12,
				itemHeight: 12,
				selectedMode: false, // 关闭图例选择
				textStyle: {
					color: '#fff',
					fontSize: 14,
					fontFamily: 'Source Han Sans CN',
					rich: {
						name: {
							color: '#FFF',
							fontSize: 18,
							width: 50,
							padding: [0, 0, 0, 10],
						},
						value: {
							color: '#2BDFD4',
							fontSize: 20,
							width: 50,
							padding: [0, 0, 0, 20],
						},
						percent: {
							color: '#2BDFD4',
							fontSize: 24,
							padding: [0, 0, 0, 20],
						},
						unit: {
							color: '#ACDCE4',
							fontSize: 24,
							padding: [0, 0, 0, 5],
						},
					},
				},
				formatter: (name: any) => {
					let obj = pieData.find((item: any) => item.name === name);
					let datas = pieData;
					let total = 0;
					let target = obj.value;
					for (let i = 0; i < datas.length; i++) {
						total += Number(datas[i].value);
					}
					const arr = [`{name|${name}}{value|${obj.value}次}{percent|${((target / total) * 100).toFixed(0)}}{unit|%}`];
					return arr.join('');
				},
			},
			xAxis3D: {},
			yAxis3D: {},
			zAxis3D: {},
			grid3D: {
				viewControl: {
					autoRotate: true, // 自动旋转
				},
				left: '4%',
				width: '45%',
				show: false,
				boxHeight: 30,
        // boxWidth和boxDepth这两个属性值保持一致,才可以在调整饼图宽度的时候保持水平,不然就会歪歪扭扭
				boxWidth: 130,
				boxDepth: 130,
			},
			series: series,
		};
		return option;
	}

	const data = [
		{
			name: 'PM2.5',
			value: 134,
		},
		{
			name: 'VOC',
			value: 56,
		},
		{
			name: 'T',
			value: 57,
		},
		{
			name: 'CH2O',
			value: 36,
		},
		{
			name: 'CO2',
			value: 51,
		},
		{
			name: 'RH',
			value: 51,
		},
	];

	const serData = data.map((dItem, index) => {
		return {
			...dItem,
			value: Number(dItem.value),
			itemStyle: {
				color: colorList[index],
			},
		};
	});
  
	// 传入数据生成 option
	let option = getPie3D(serData, 0.7);

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1489487.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【ES入门一:基础概念】

集群层面上的基础概念 集群 由多个es实例组成的叫做集群 节点 单个ES的服务实例叫做节点。每个实例都有自己的名字&#xff0c;就是在配置文件中配置的‘node.name’中的内容。为了标识每个节点&#xff0c;每个节点启动后都会分配一个UID&#xff0c;存储在data目录。每个…

第七十四天漏洞发现-Web框架中间件插件BurpSuite浏览器被动主动探针

第74天 漏洞发现-Web框架中间件插件&BurpSuite&浏览器&被动&主动探针 最近几天都是演示工具如何使用如&#xff1a;AWVS、Nessus、nexpose等综合性利用工具。 Burp插件和漏扫工具的区别 知识点&#xff1a; 1、浏览器插件&BurpSuite插件 2、Hack-Tools&…

基于协同过滤的旅游推荐系统设计与实现

基于协同过滤的旅游推荐系统设计与实现 在当今旅游业蓬勃发展的背景下&#xff0c;人们对于旅游体验的需求日益增加&#xff0c;如何为用户提供更加个性化、精准的旅游推荐成为了旅游行业的一个重要课题。为解决这一问题&#xff0c;我们设计并实现了一个基于协同过滤的旅游推…

“互动+消费”时代,借助华为云GaussDB重构新零售中消费逻辑

场与人的关系 “人—货—场”是零售中重要的三要素&#xff0c;我们一直在追求&#xff0c;将零售中的人、货、场进行数字化并在云端进行整合&#xff0c;形成属于我们自己的云平台。 随着互联网技术为信息提供的便利&#xff0c;消费者的集体力量正在逐渐形成一股强大的反向…

RabbitMQ的整体架构是怎么样的?

RabbitMQ是一个开源的消息中间件&#xff0c;用于在应用程序之间传递消息。它实现了AMQP(高级消息队列协议)并支持其他消息传递协议&#xff0c;例如STOMP(简单文本定向消息协议)和MQTT&#xff08;物联网协议&#xff09; 他的整体架构大致如下&#xff1a; Producer&#xf…

【NVCC,CUDA,NVIDIA驱动】装了pytorch,nvcc -V不能用,但能正常使用gpu

这里写目录标题 问题描述问题原理为什么anaconda安装的Pytorch&#xff0c;其能够直接在gpu上运行NVCC是什么&#xff0c;怎么查看装没装 如果没有NVCC文件夹&#xff0c;应该如何安装NVCC&#xff1f;CUDNN&#xff1a;Local Installer for Linux x86_64和Local Installer for…

小程序开通流量主

开发小程序有一段时间了&#xff0c;误打误撞开通了流量主。到现在有2400人访问了&#xff0c;当然这是累计的&#xff0c;每天访问人数也就是平均七八十左右。 当然&#xff0c;每日还是有一些收入的&#xff0c;虽然比较低&#xff0c;一块钱上下&#xff1a; 感觉做小程序&…

hnust 湖南科技大学 2022 数据挖掘课设 完整代码+报告+图源文件+指导书

hnust 湖南科技大学 2022 数据挖掘课设 完整代码报告图源文件指导书 目录 实验一 Apriori算法设计与应用 - 1 - 一、 背景介绍 - 1 - 二、 实验内容 - 1 - 三、 实验结果与分析 - 2 - 四、 小结与心得体会 - 3 - 实验二 KNN算法设计与应用 - 4 - 一、 背景介绍 - 4 - 二、 实…

NLP评价指标

一、分类任务常见评估&#xff1a; 准确度(Accuracy) 评估预测正确的比例&#xff0c;精确率(Precision) 评估预测正例的查准率&#xff0c;召回率(Recall) 评估真实正例的查全率。如果是多分类&#xff0c;则每个类别各自求P、R最终求平均值。 TP&#xff08;True Positives…

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:分布式迁移标识)

组件的分布式迁移标识&#xff0c;指明了该组件在分布式迁移场景下可以将特定状态恢复到对端设备。 说明&#xff1a; 从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 restoreId restoreId(value: number) 标记支持分布式…

Java_二叉树

文章目录 一、二叉树1.树型结构2.概念&#xff08;重要&#xff09;3.树的表示形式&#xff08;了解&#xff09;4.树的应用5.二叉树的概念6.两种特殊的二叉树7.二叉树的性质练习 8.二叉树的存储9.二叉树的遍历手动创建二叉树1、前中后遍历2、层序遍历3、二叉树的基本操作获取树…

什么是Vue的过渡效果?如何使用Vue的过渡效果?

Vue的过渡效果是Vue.js框架中提供的一种动画效果&#xff0c;可以让元素在插入、更新或移除时拥有更流畅的视觉切换效果。使用Vue的过渡效果可以为页面增添动感和交互性&#xff0c;让用户体验更加友好。 下面我们来看一下如何使用Vue的过渡效果。首先&#xff0c;我们需要在V…

什么是SpringCloud,有哪些组件?

spring Cloud 是基于spring boot的分布式系统开发工具,它提供了一系列开箱即用的,针对分布式系统开发的特性和组件。用于帮助开发人员快速构建和管理云原生应用程序。 Spring Cloud 的主要目标是解决分布式系统中的常见问题,例如服务发现,负载均衡,配置管理,断路器,消息总…

【鸿蒙 HarmonyOS 4.0】应用状态:LocalStorage/AppStorage/PersistentStorage

一、介绍 如果要实现应用级的&#xff0c;或者多个页面的状态数据共享&#xff0c;就需要用到应用级别的状态管理的概念。 LocalStorage&#xff1a;页面级UI状态存储&#xff0c;通常用于UIAbility内、页面间的状态共享。AppStorage&#xff1a;特殊的单例LocalStorage对象&…

ElasticSearch之数据分片和故障转移

写在前面 基础环境可以参考ElasticSearch之分布式模型介绍&#xff0c;选主&#xff0c;脑裂 。 本文看下es的数据分片和故障转移相关内容。 1&#xff1a;数据分片 分片&#xff0c;英文是shard&#xff0c;存储在data node &#xff0c;分为主分片和副本分片&#xff0c;英…

数据治理实战——翼支付金融板块业务数仓建设和数据治理之路

目录 一、数据治理背景 二、数据治理建设内容 2.1 组织协同 2.2 平台建设 2.3 数据应用治理 2.4 数据规范 2.5 数据安全 三、企业级数仓建设 3.1 调研阶段 2.2 平台护航 2.3 数仓分层 2.4 维度建模 2.4.1 维度建模四步曲 2.4.2 命名规范 2.4.3 资产沉淀 2.4.4 …

【JS】WebSocket:实现实时通信功能。

【JS】WebSocket:实现实时通信功能。 一、WebSocket是什么&#xff1f;二、为什么需要WebSocket?三、WebSocket的优势四、WebSocket实现方式五、示例1&#xff1a;实时数据展示六、示例2&#xff1a;实现简易聊天室 如果想需要websocket实现功能&#xff0c;后端也要进行对应的…

前端食堂技术周刊第 114 期:Interop 2024、TS 5.4 RC、2 月登陆浏览器的新功能、JSR、AI SDK 3.0

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;凉拌鸡架 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看下…

tsc : 无法加载文件 C:\Users\Administrat\AppData\Roaming\npm\tsc.ps 1,因为在此系统上禁止运行脚本

报错&#xff1a;tsc : 无法加载文件 C:\Users\Administrat\AppData\Roaming\npm\tsc.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。 解决 使用命令行时出现ab…

Excel中筛选合并单元格后,只显示第一行怎么办?

Excel中筛选合并单元格后,只显示第一行怎么办? 我们日常的Excel数据在展示的时候为了数据的清晰和美观往往部分相同的单元格进行合并,但是合并之后在筛选时会发现结果会显示异常。 现在我们筛选下国籍为中国的员工信息,发现只显示了一条数据,解决这个异常只需要五Excel步:…