首页 > 其他分享 >立体饼图

立体饼图

时间:2024-04-10 15:55:06浏览次数:20  
标签:const option series 立体 state pieData Math

<template>
	<div>
		<div style="width: 100%; height: 200px" ref="CapacityPie"></div>
	</div>
</template>
<script lang='ts'>
import { toRefs, reactive, defineComponent, getCurrentInstance, onMounted, ref, Ref } from 'vue';
import * as echarts from 'echarts';
import 'echarts-gl';
import { getPie3D, getParametricEquation } from '../assets/charts';
export default defineComponent({
	name: 'ScreenCapacityPie',
	setup() {
		const { proxy } = getCurrentInstance() as any;
		const state = reactive({
			myCharts: {
				CapacityPie: null,
			},
			option: {
			},
		});
		// 页面加载时
		onMounted(async () => {
			initCapacityPie();
		});
		// 初始化容量检测图表
		const initCapacityPie = () => {
			const myChart = echarts.init(proxy.$refs.CapacityPie);
			const optionData = [
				{
					name: '水田',
					value: 60,
					itemStyle: {
						color: '#37FFC9',
					},
				},
				{
					name: '水浇地',
					value: 44,
					itemStyle: {
						color: '#31A1FF',
					},
				},
				{
					name: '旱地',
					value: 32,
					itemStyle: {
						color: '#FFF777',
					},
				},
			];
			state.option = getPie3D(optionData, 0.5);
			myChart.setOption(state.option, true);
			state.option.series.push({
				name: '工单数量占比', //自己根据场景修改
				backgroundColor: 'transparent',
				type: 'pie',
				label: {
					show: false,
					opacity: 1,
					fontSize: 13,
					lineHeight: 20,
				},
				startAngle: -40, // 起始角度,支持范围[0, 360]。
				clockwise: false, // 饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式
				data: optionData,
				itemStyle: {
					opacity: 0, //这里必须是0,不然2d的图会覆盖在表面
				},
			});
			myChart.setOption(state.option, true);
			state.myCharts.CapacityPie = myChart;
			// bindListen(myChart);
		};
		const bindListen = (myChart: any, optionName = 'option') => {
			let selectedIndex = '';
			let hoveredIndex = '';
			// 监听点击事件,实现选中效果(单选)
			myChart.on('click', (params: any) => {
				// 从 option.series 中读取重新渲染扇形所需的参数,将是否选中取反。
				const isSelected = !state.option.series[params.seriesIndex].pieStatus.selected;
				const isHovered = state.option.series[params.seriesIndex].pieStatus.hovered;
				const k = state.option.series[params.seriesIndex].pieStatus.k;
				const startRatio = state.option.series[params.seriesIndex].pieData.startRatio;
				const endRatio = state.option.series[params.seriesIndex].pieData.endRatio;
				// 如果之前选中过其他扇形,将其取消选中(对 option 更新)
				if (selectedIndex !== '' && selectedIndex !== params.seriesIndex) {
					state.option.series[selectedIndex].parametricEquation = getParametricEquation(
						state.option.series[selectedIndex].pieData.startRatio,
						state.option.series[selectedIndex].pieData.endRatio,
						false,
						false,
						k,
						state.option.series[selectedIndex].pieData.value
					);
					state.option.series[selectedIndex].pieStatus.selected = false;
				}
				// 对当前点击的扇形,执行选中/取消选中操作(对 option 更新)
				state.option.series[params.seriesIndex].parametricEquation = getParametricEquation(
					startRatio,
					endRatio,
					isSelected,
					isHovered,
					k,
					state.option.series[params.seriesIndex].pieData.value
				);
				state.option.series[params.seriesIndex].pieStatus.selected = isSelected;
				// 如果本次是选中操作,记录上次选中的扇形对应的系列号 seriesIndex
				selectedIndex = isSelected ? params.seriesIndex : null;
				// 使用更新后的 option,渲染图表
				myChart.setOption(state.option);
			});
			// 监听 mouseover,近似实现高亮(放大)效果
			myChart.on('mouseover', (params: any) => {
				// 准备重新渲染扇形所需的参数
				let isSelected: any;
				let isHovered: any;
				let startRatio: any;
				let endRatio: any;
				let k: any;
				// 如果触发 mouseover 的扇形当前已高亮,则不做操作
				if (hoveredIndex === params.seriesIndex) {
					return;
					// 否则进行高亮及必要的取消高亮操作
				} else {
					// 如果当前有高亮的扇形,取消其高亮状态(对 option 更新)
					if (hoveredIndex !== '' && state.option.series[hoveredIndex].pieStatus) {
						// 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 false。
						isSelected = state.option.series[hoveredIndex].pieStatus.selected;
						isHovered = false;
						startRatio = state.option.series[hoveredIndex].pieData.startRatio;
						endRatio = state.option.series[hoveredIndex].pieData.endRatio;
						k = state.option.series[hoveredIndex].pieStatus.k;
						// 对当前点击的扇形,执行取消高亮操作(对 option 更新)
						state.option.series[hoveredIndex].parametricEquation = getParametricEquation(
							startRatio,
							endRatio,
							isSelected,
							isHovered,
							k,
							state.option.series[hoveredIndex].pieData.value
						);
						state.option.series[hoveredIndex].pieStatus.hovered = isHovered;
						// 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空
						hoveredIndex = '';
					}
					// 如果触发 mouseover 的扇形不是透明圆环,将其高亮(对 option 更新)
					if (params.seriesName !== 'mouseoutSeries' && state.option.series[params.seriesIndex].pieStatus) {
						let seriesData = state.option.series[params.seriesIndex];
						// 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。
						isSelected = seriesData.pieStatus.selected;
						isHovered = true;
						startRatio = seriesData.pieData.startRatio;
						endRatio = seriesData.pieData.endRatio;
						k = seriesData.pieStatus.k;
						// 对当前点击的扇形,执行高亮操作(对 option 更新)
						state.option.series[params.seriesIndex].parametricEquation = getParametricEquation(
							startRatio,
							endRatio,
							isSelected,
							isHovered,
							k,
							seriesData.pieData.value + 30
						);
						state.option.series[params.seriesIndex].pieStatus.hovered = isHovered;
						// // 记录上次高亮的扇形对应的系列号 seriesIndex
						hoveredIndex = params.seriesIndex;
					}
					// 使用更新后的 option,渲染图表
					myChart.setOption(state.option);
				}
			});
			// 修正取消高亮失败的 bug
			myChart.on('globalout', () => {
				// 准备重新渲染扇形所需的参数
				let isSelected: any;
				let isHovered: any;
				let startRatio: any;
				let endRatio: any;
				let k: any;
				if (hoveredIndex !== '' && state.option.series[hoveredIndex].pieStatus) {
					// 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。
					isSelected = state.option.series[hoveredIndex].pieStatus.selected;
					isHovered = false;
					k = state.option.series[hoveredIndex].pieStatus.k;
					startRatio = state.option.series[hoveredIndex].pieData.startRatio;
					endRatio = state.option.series[hoveredIndex].pieData.endRatio;
					// 对当前点击的扇形,执行取消高亮操作(对 option 更新)
					state.option.series[hoveredIndex].parametricEquation = getParametricEquation(
						startRatio,
						endRatio,
						isSelected,
						isHovered,
						k,
						state.option.series[hoveredIndex].pieData.value
					);
					state.option.series[hoveredIndex].pieStatus.hovered = isHovered;
					// 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空
					hoveredIndex = '';
				}
				// 使用更新后的 option,渲染图表
				myChart.setOption(state.option);
			});
		};
	},
});
</script>
ts文件
/**
 * 绘制3d图
 * @param pieData 总数据
 * @param internalDiameterRatio:透明的空心占比
 * @param distance 视角到主体的距离
 * @param alpha 旋转角度
 * @param pieHeight 立体的高度
 * @param opacity 饼或者环的透明度
 */
// 生成扇形的曲面参数方程
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) {
	// 计算
	const midRatio = (startRatio + endRatio) / 2;

	const startRadian = startRatio * Math.PI * 2;
	const endRadian = endRatio * Math.PI * 2;
	const midRadian = midRatio * Math.PI * 2;

	// 如果只有一个扇形,则不实现选中效果。
	if (startRatio === 0 && endRatio === 1) {
		// eslint-disable-next-line no-param-reassign
		isSelected = false;
	}

	// 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
	// eslint-disable-next-line no-param-reassign
	k = typeof k !== 'undefined' ? k : 1 / 3;

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

	// 计算高亮效果的放大比例(未高亮,则比例为 1)
	const 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(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(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(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;
			}
			// 当前图形的高度是Z根据h(每个value的值决定的)
			return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
		},
	};
}
// 生成模拟 3D 饼图的配置项
function getPie3D(pieData, internalDiameterRatio) {
	const series = [];
	// 总和
	let sumValue = 0;
	let startValue = 0;
	let endValue = 0;
	const legendData = [];
	let legendBfb = [];
	const k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;

	// 为每一个饼图数据,生成一个 series-surface 配置
	for (let i = 0; i < pieData.length; i += 1) {
		sumValue += pieData[i].value;

		const 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,
			},
		};

		if (typeof pieData[i].itemStyle !== 'undefined') {
			const { itemStyle } = pieData[i];

			// eslint-disable-next-line no-unused-expressions
			typeof pieData[i].itemStyle.color !== 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
			// eslint-disable-next-line no-unused-expressions
			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 += 1) {
		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,
			// 我这里做了一个处理,使除了第一个之外的值都是10
			series[i].pieData.value === series[0].pieData.value ? 35 : 10
		);

		startValue = endValue;
		const bfb = fomatFloat(series[i].pieData.value / 100, 4);
		legendData.push({
			name: series[i].name,
			value: bfb,
		});
		legendBfb.push({
			name: series[i].name,
			value: bfb,
		});
	}

	// 准备待返回的配置项,把准备好的 legendData、series 传入。
	const option = {
		// animation: false,
		tooltip: {
			formatter: (params) => {
				if (params.seriesName !== 'mouseoutSeries') {
					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}%`;
				}
				return '';
			},
		},
		xAxis3D: {
			min: -1,
			max: 1,
		},
		yAxis3D: {
			min: -1,
			max: 1,
		},
		zAxis3D: {
			min: -1,
			max: 1,
		},
		legend: {
			show: true,
			data: legendData,
            right:"right",
			itemGap: 10,
			itemWidth: 12,
			itemHeight: 12,
			textStyle: {
				color: '#fff',
				rich: {
					title: {
						fontSize: 14
						
					},
					value: {
						fontSize: 14,
						padding: [0, 20, 0, 20],
					},
				},
			},
			formatter: function (param) {
				const item = legendBfb.filter((item) => item.name === param)[0];
				const bfs = fomatFloat(item.value * 100, 2) + '%';
				return '{title|' + `${item.name}` + '}{value|' + `${bfs}` + '}';
			},
		},
		grid3D: {
			show: false,
			boxHeight: 5,
			top: '-20%',
			viewControl: {
				// 3d效果可以放大、旋转等,请自己去查看官方配置
				alpha: 35,
				// beta: 30,
				rotateSensitivity: 1,
				zoomSensitivity: 0,
				panSensitivity: 0,
				// autoRotate: true,
				distance: 150,
			},
			// 后处理特效可以为画面添加高光、景深、环境光遮蔽(SSAO)、调色等效果。可以让整个画面更富有质感。
			postEffect: {
				// 配置这项会出现锯齿,请自己去查看官方配置有办法解决
				enable: false,
				bloom: {
					enable: true,
					bloomIntensity: 0.1,
				},
				SSAO: {
					enable: true,
					quality: 'medium',
					radius: 2,
				},
				// temporalSuperSampling: {
				//   enable: true,
				// },
			},
		},
		series,
	};
	return option;
}
/**
 * 格式化浮点数
 */
const fomatFloat = (num, n) => {
	let f = parseFloat(num);
	if (isNaN(f)) {
		return false;
	}
	f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n); // n 幂
	let s = f.toString();
	let rs = s.indexOf('.');
	// 判定如果是整数,增加小数点再补0
	if (rs < 0) {
		rs = s.length;
		s += '.';
	}
	while (s.length <= rs + n) {
		s += '0';
	}
	return s;
};

export { getPie3D, getParametricEquation };

标签:const,option,series,立体,state,pieData,Math
From: https://www.cnblogs.com/JaneLifeBlog/p/18126211

相关文章

  • 立体几何初步 | 思维导图
    前言使用方法:如果想得到更好的显示效果,可以点击全屏按钮,已经实现电脑端、手机端的适配,效果很好;电视端没有实现适配,Ipad端的适配没有测试;思维导图全屏......
  • 提高三维模型数据的立体裁剪技术
    提高三维模型数据的立体裁剪技术 立体裁剪是三维模型处理中的重要步骤,可以用于去除模型中不需要的部分,提高模型的质量和准确性。本文将介绍几种常见的立体裁剪技术,包括边界裁剪、体素裁剪和几何裁剪,并分析它们的优缺点和适用场景。 引言三维模型在设计、建模和动画......
  • 解决三维模型的立体裁剪的主要技术方法
    解决三维模型的立体裁剪的主要技术方法 三维模型的立体裁剪是指在三维空间中对物体进行裁剪,以便在渲染、动画和虚拟现实等应用中获得所需的效果。立体裁剪技术在计算机图形学和计算机辅助设计领域有着广泛的应用。本文将介绍三维模型立体裁剪的主要技术方法。 体素化......
  • WM8978 —— 带扬声器驱动程序的立体声编解码器(2)
    接前一篇文章:WM8978——带扬声器驱动程序的立体声编解码器(1)六、引脚详细说明引脚(PIN)名称(NAME)类型(TYPE)描述(DESCRIPTION)1LIP模拟输入左麦克风前置放大器正极输入2LIN模拟输入左麦克风前置放大器负极输入3L2/GPIO2模拟输入左声道线路输入/辅助麦克风前置放大器正输入/......
  • 立体呈现航空风采,3D漫游可视化探索企业文化新境界
    在科技的浪潮中,3D技术日益成熟,为人们带来了前所未有的视觉体验。而今天,我们将借助这一神奇的技术,开启一场别开生面的航空企业文化漫游之旅,全方位地感受航空企业的魅力与风采。 置身3D模型中,眼前的景象变得栩栩如生,仿佛触手可及。我们可以自由地穿梭于各个展区,欣赏到不同区域的......
  • 光度立体法的简化求解(已知特殊光源方向)
     原理这个博主写的很好如何获取物体表面的法向量?好好谈谈光度立体法-CSDN博客PhotometricStereo光度立体三维重建(一)——介绍-CSDN博客​不过当图片较大的时候,比如4048*4000这种量级的,矩阵很大,速度要10秒,加了openmp也需要2s;如果我们知道一些先验知识,在特定的slant......
  • 倾斜摄影三维模型的立体裁剪的问题分析
    倾斜摄影三维模型的立体裁剪的问题分析   倾斜摄影是一种利用斜视角进行拍摄和测量的技术,可以获得具有高精度的三维模型。然而,在生成三维模型的过程中,立体裁剪是一个重要的问题。立体裁剪是指将倾斜摄影的影像数据与地面数据进行匹配和融合,以生成真实而准确的三维模型。本......
  • PHP立体安全攻击向量:保护应用程序的关键挑战
    PHP立体安全攻击向量:保护应用程序的关键挑战PHP作为一种广泛使用的服务器端脚本语言,拥有庞大的用户群体和丰富的生态系统。然而,随着互联网的发展,网络安全问题也变得愈发严重。本文将深入探讨PHP的立体安全攻击向量,分析其原理和可能的应对策略,帮助开发者了解并应对这些潜在威......
  • 通达信波段趋势强弱主图,立体K线 红绿多空变色彩带源码
    {通达信波段趋势强弱主图,立体K线红绿多空变色彩带源码}X_1:=(3*CLOSE+LOW+OPEN+HIGH)/6;X_2:=EMA(X_1,13);X_3:=EMA(X_2,3);X_4:=EMA(X_3,3);X_5:=EMA(X_4,3);X_6:=EMA(X_5,3);X_7:=EMA(X_6,3);DRAWBAND(X_2,RGB(150,50,20),X_7,RGB(36,150,40));NOTEXTMB101:IF(X_......
  • 立体之路:解锁3D可视化模型下的交通新世界
    在科技的浪潮中,每一个革新都是对人类未来生活的深度洞察。而今,当可视化这一技术走进我们的视野,它不仅是一场视觉盛宴,更是一次对未来出行方式的全新探索。 一、从平面到立体,解锁道路新视角你是否曾站在十字路口,对着复杂的交通网络感到迷茫?传统的道路地图,虽然详尽,但总是缺乏直观......