当前位置:   article > 正文

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

echarts 3d饼图

一、实现的效果

二、具体步骤

1.安装依赖

npm install echarts 

2.引入echarts

import * as echarts from 'echarts';

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

import 'echarts-gl';

3.echarts部分代码

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

  1. // 颜色列表
  2. const colorList = [
  3. 'rgba(76, 139, 241, 0.9)',
  4. 'rgba(101, 193, 241, 0.9)',
  5. 'rgba(249, 215, 114, 0.9)',
  6. 'rgba(179, 186, 195, 0.9)',
  7. 'rgba(255, 255, 255, 0.9)',
  8. 'rgba(145, 186, 217, 0.9)',
  9. ];
  10. // 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
  11. function getParametricEquation(startRatio: any, endRatio: any, isSelected: any, isHovered: any, k: any, h: any) {
  12. // 计算
  13. let midRatio = (startRatio + endRatio) / 2;
  14. let startRadian = startRatio * Math.PI * 2;
  15. let endRadian = endRatio * Math.PI * 2;
  16. let midRadian = midRatio * Math.PI * 2;
  17. // 如果只有一个扇形,则不实现选中效果。
  18. // if (startRatio === 0 && endRatio === 1) {
  19. // isSelected = false;
  20. // }
  21. isSelected = false;
  22. // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
  23. k = typeof k !== 'undefined' ? k : 1 / 3;
  24. // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
  25. let offsetX = isSelected ? Math.sin(midRadian) * 0.1 : 0;
  26. let offsetY = isSelected ? Math.cos(midRadian) * 0.1 : 0;
  27. // 计算高亮效果的放大比例(未高亮,则比例为 1)
  28. let hoverRate = isHovered ? 1.05 : 1;
  29. // 返回曲面参数方程
  30. return {
  31. u: {
  32. min: -Math.PI,
  33. max: Math.PI * 3,
  34. step: Math.PI / 32,
  35. },
  36. v: {
  37. min: 0,
  38. max: Math.PI * 2,
  39. step: Math.PI / 20,
  40. },
  41. x: function (u: any, v: any) {
  42. if (u < startRadian) {
  43. return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
  44. }
  45. if (u > endRadian) {
  46. return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
  47. }
  48. return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
  49. },
  50. y: function (u: any, v: any) {
  51. if (u < startRadian) {
  52. return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
  53. }
  54. if (u > endRadian) {
  55. return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
  56. }
  57. return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
  58. },
  59. z: function (u: any, v: any) {
  60. if (u < -Math.PI * 0.5) {
  61. return Math.sin(u);
  62. }
  63. if (u > Math.PI * 2.5) {
  64. return Math.sin(u) * h * 0.1;
  65. }
  66. return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
  67. },
  68. };
  69. }
  70. // 生成模拟 3D 饼图的配置项
  71. function getPie3D(pieData: any, internalDiameterRatio: any) {
  72. let series = [];
  73. let sumValue = 0;
  74. let startValue = 0;
  75. let endValue = 0;
  76. let legendData = [];
  77. let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;
  78. // 为每一个饼图数据,生成一个 series-surface 配置
  79. for (let i = 0; i < pieData.length; i++) {
  80. sumValue += pieData[i].value;
  81. let seriesItem: any = {
  82. name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
  83. type: 'surface',
  84. parametric: true,
  85. wireframe: {
  86. show: false,
  87. },
  88. pieData: pieData[i],
  89. pieStatus: {
  90. selected: false,
  91. hovered: false,
  92. k: 1 / 10,
  93. },
  94. };
  95. if (typeof pieData[i].itemStyle != 'undefined') {
  96. let itemStyle: any = {};
  97. typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
  98. typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
  99. seriesItem.itemStyle = itemStyle;
  100. }
  101. series.push(seriesItem);
  102. }
  103. // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
  104. // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
  105. for (let i = 0; i < series.length; i++) {
  106. endValue = startValue + series[i].pieData.value;
  107. series[i].pieData.startRatio = startValue / sumValue;
  108. series[i].pieData.endRatio = endValue / sumValue;
  109. series[i].parametricEquation = getParametricEquation(
  110. series[i].pieData.startRatio,
  111. series[i].pieData.endRatio,
  112. false,
  113. false,
  114. k,
  115. series[i].pieData.value
  116. );
  117. startValue = endValue;
  118. legendData.push(series[i].name);
  119. }
  120. series.push({
  121. name: 'mouseoutSeries',
  122. type: 'surface',
  123. parametric: true,
  124. wireframe: {
  125. show: false,
  126. },
  127. itemStyle: {
  128. opacity: 0.2,
  129. color: 'rgba(165, 247, 253, 1)',
  130. },
  131. parametricEquation: {
  132. u: {
  133. min: 0,
  134. max: Math.PI * 2,
  135. step: Math.PI / 20,
  136. },
  137. v: {
  138. min: 0,
  139. max: Math.PI / 4,
  140. step: Math.PI / 20,
  141. },
  142. x: function (u: any, v: any) {
  143. return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2.5;
  144. },
  145. y: function (u: any, v: any) {
  146. return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2.5;
  147. },
  148. z: function (u: any, v: any) {
  149. return Math.cos(v) > 0 ? -3 : -3;
  150. },
  151. },
  152. });
  153. // 准备待返回的配置项,把准备好的 legendData、series 传入。
  154. let option = {
  155. legend: {
  156. icon: 'circle',
  157. orient: 'vertical',
  158. data: pieData.map((dItem: any, dIndex: any) => {
  159. return {
  160. ...dItem,
  161. textStyle: {
  162. rich: {
  163. percent: {
  164. color: colorList[dIndex],
  165. },
  166. },
  167. },
  168. };
  169. }),
  170. right: '5%',
  171. top: '20%',
  172. itemGap: 10,
  173. itemWidth: 12,
  174. itemHeight: 12,
  175. selectedMode: false, // 关闭图例选择
  176. textStyle: {
  177. color: '#fff',
  178. fontSize: 14,
  179. fontFamily: 'Source Han Sans CN',
  180. rich: {
  181. name: {
  182. color: '#FFF',
  183. fontSize: 18,
  184. width: 50,
  185. padding: [0, 0, 0, 10],
  186. },
  187. value: {
  188. color: '#2BDFD4',
  189. fontSize: 20,
  190. width: 50,
  191. padding: [0, 0, 0, 20],
  192. },
  193. percent: {
  194. color: '#2BDFD4',
  195. fontSize: 24,
  196. padding: [0, 0, 0, 20],
  197. },
  198. unit: {
  199. color: '#ACDCE4',
  200. fontSize: 24,
  201. padding: [0, 0, 0, 5],
  202. },
  203. },
  204. },
  205. formatter: (name: any) => {
  206. let obj = pieData.find((item: any) => item.name === name);
  207. let datas = pieData;
  208. let total = 0;
  209. let target = obj.value;
  210. for (let i = 0; i < datas.length; i++) {
  211. total += Number(datas[i].value);
  212. }
  213. const arr = [`{name|${name}}{value|${obj.value}次}{percent|${((target / total) * 100).toFixed(0)}}{unit|%}`];
  214. return arr.join('');
  215. },
  216. },
  217. xAxis3D: {},
  218. yAxis3D: {},
  219. zAxis3D: {},
  220. grid3D: {
  221. viewControl: {
  222. autoRotate: true, // 自动旋转
  223. },
  224. left: '4%',
  225. width: '45%',
  226. show: false,
  227. boxHeight: 30,
  228. // boxWidth和boxDepth这两个属性值保持一致,才可以在调整饼图宽度的时候保持水平,不然就会歪歪扭扭
  229. boxWidth: 130,
  230. boxDepth: 130,
  231. },
  232. series: series,
  233. };
  234. return option;
  235. }
  236. const data = [
  237. {
  238. name: 'PM2.5',
  239. value: 134,
  240. },
  241. {
  242. name: 'VOC',
  243. value: 56,
  244. },
  245. {
  246. name: 'T',
  247. value: 57,
  248. },
  249. {
  250. name: 'CH2O',
  251. value: 36,
  252. },
  253. {
  254. name: 'CO2',
  255. value: 51,
  256. },
  257. {
  258. name: 'RH',
  259. value: 51,
  260. },
  261. ];
  262. const serData = data.map((dItem, index) => {
  263. return {
  264. ...dItem,
  265. value: Number(dItem.value),
  266. itemStyle: {
  267. color: colorList[index],
  268. },
  269. };
  270. });
  271. // 传入数据生成 option
  272. let option = getPie3D(serData, 0.7);

 

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号