Newer
Older
KaiFengPC / src / views / sponeScreen / Echarts / tslyqk_constituent_3d.vue
@zhangdeliang zhangdeliang on 3 Sep 9 KB update
  1. <template>
  2. <div class="tslyqk_constituent_3d">
  3. <div class="echarts3D" ref="echarts3D"></div>
  4. <div class="bg-nopx"></div>
  5. </div>
  6. </template>
  7.  
  8. <script setup>
  9. import * as echarts from 'echarts';
  10. const { proxy } = getCurrentInstance();
  11.  
  12. const myChart = ref(null);
  13.  
  14. // 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
  15. function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) {
  16. // 计算
  17. let midRatio = (startRatio + endRatio) / 2;
  18.  
  19. let startRadian = startRatio * Math.PI * 2;
  20. let endRadian = endRatio * Math.PI * 2;
  21. let midRadian = midRatio * Math.PI * 2;
  22.  
  23. // 如果只有一个扇形,则不实现选中效果。
  24. // if (startRatio === 0 && endRatio === 1) {
  25. // isSelected = false;
  26. // }
  27. isSelected = false;
  28. // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
  29. k = typeof k !== 'undefined' ? k : 1 / 3;
  30.  
  31. // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
  32. let offsetX = isSelected ? Math.sin(midRadian) * 0.1 : 0;
  33. let offsetY = isSelected ? Math.cos(midRadian) * 0.1 : 0;
  34.  
  35. // 计算高亮效果的放大比例(未高亮,则比例为 1)
  36. let hoverRate = isHovered ? 1.05 : 1;
  37.  
  38. // 返回曲面参数方程
  39. return {
  40. u: {
  41. min: -Math.PI,
  42. max: Math.PI * 3,
  43. step: Math.PI / 32,
  44. },
  45.  
  46. v: {
  47. min: 0,
  48. max: Math.PI * 2,
  49. step: Math.PI / 20,
  50. },
  51.  
  52. x: function (u, v) {
  53. if (u < startRadian) {
  54. return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
  55. }
  56. if (u > endRadian) {
  57. return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
  58. }
  59. return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
  60. },
  61.  
  62. y: function (u, v) {
  63. if (u < startRadian) {
  64. return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
  65. }
  66. if (u > endRadian) {
  67. return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
  68. }
  69. return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
  70. },
  71.  
  72. z: function (u, v) {
  73. if (u < -Math.PI * 0.5) {
  74. return Math.sin(u);
  75. }
  76. if (u > Math.PI * 2.5) {
  77. return Math.sin(u) * h * 0.1;
  78. }
  79. return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
  80. },
  81. };
  82. }
  83. // 生成模拟 3D 饼图的配置项
  84. function getPie3D(pieData, internalDiameterRatio) {
  85. let series = [];
  86. let sumValue = 0;
  87. let startValue = 0;
  88. let endValue = 0;
  89. let legendData = [];
  90. let k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;
  91.  
  92. // 为每一个饼图数据,生成一个 series-surface 配置
  93. for (let i = 0; i < pieData.length; i++) {
  94. sumValue += pieData[i].value;
  95.  
  96. let seriesItem = {
  97. name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
  98. type: 'surface',
  99. parametric: true,
  100. wireframe: {
  101. show: false,
  102. },
  103. pieData: pieData[i],
  104. pieStatus: {
  105. selected: false,
  106. hovered: false,
  107. k: 1 / 10,
  108. },
  109. };
  110.  
  111. if (typeof pieData[i].itemStyle != 'undefined') {
  112. let itemStyle = {};
  113.  
  114. typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
  115. typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
  116.  
  117. seriesItem.itemStyle = itemStyle;
  118. }
  119. series.push(seriesItem);
  120. }
  121.  
  122. // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
  123. // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
  124. for (let i = 0; i < series.length; i++) {
  125. endValue = startValue + series[i].pieData.value;
  126.  
  127. series[i].pieData.startRatio = startValue / sumValue;
  128. series[i].pieData.endRatio = endValue / sumValue;
  129. series[i].parametricEquation = getParametricEquation(
  130. series[i].pieData.startRatio,
  131. series[i].pieData.endRatio,
  132. false,
  133. false,
  134. k,
  135. series[i].pieData.value
  136. );
  137.  
  138. startValue = endValue;
  139.  
  140. legendData.push(series[i].name);
  141. }
  142.  
  143. // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
  144. series.push({
  145. name: 'mouseoutSeries',
  146. type: 'surface',
  147. parametric: true,
  148. wireframe: {
  149. show: false,
  150. },
  151. itemStyle: {
  152. opacity: 0.9,
  153. color: '#296E99',
  154. },
  155. parametricEquation: {
  156. u: {
  157. min: 0,
  158. max: Math.PI * 2,
  159. step: Math.PI / 20,
  160. },
  161. v: {
  162. min: 0,
  163. max: Math.PI,
  164. step: Math.PI / 20,
  165. },
  166. x: function (u, v) {
  167. return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2;
  168. },
  169. y: function (u, v) {
  170. return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2;
  171. },
  172. z: function (u, v) {
  173. return Math.cos(v) > 0 ? -0.5 : -5;
  174. },
  175. },
  176. });
  177.  
  178. // // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
  179. series.push({
  180. name: 'mouseoutSeries',
  181. type: 'surface',
  182. parametric: true,
  183. wireframe: {
  184. show: false,
  185. },
  186. itemStyle: {
  187. opacity: 0.2,
  188. color: '#215983',
  189. },
  190. parametricEquation: {
  191. u: {
  192. min: 0,
  193. max: Math.PI * 3,
  194. step: Math.PI / 20,
  195. },
  196. v: {
  197. min: 0,
  198. max: Math.PI,
  199. step: Math.PI / 20,
  200. },
  201. x: function (u, v) {
  202. return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 3;
  203. },
  204. y: function (u, v) {
  205. return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 3;
  206. },
  207. z: function (u, v) {
  208. return Math.cos(v) > 0 ? -5 : -7;
  209. },
  210. },
  211. });
  212. series.push({
  213. name: 'mouseoutSeries',
  214. type: 'surface',
  215. parametric: true,
  216. wireframe: {
  217. show: false,
  218. },
  219. itemStyle: {
  220. opacity: 1,
  221. color: '#25719D',
  222. },
  223. parametricEquation: {
  224. u: {
  225. min: 0,
  226. max: Math.PI * 2,
  227. step: Math.PI / 20,
  228. },
  229. v: {
  230. min: 0,
  231. max: Math.PI,
  232. step: Math.PI / 20,
  233. },
  234. x: function (u, v) {
  235. return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2.5;
  236. },
  237. y: function (u, v) {
  238. return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2.5;
  239. },
  240. z: function (u, v) {
  241. return Math.cos(v) > 0 ? -7 : -7;
  242. },
  243. },
  244. });
  245.  
  246. // 准备待返回的配置项,把准备好的 legendData、series 传入。
  247. let option = {
  248. tooltip: {
  249. formatter: params => {
  250. if (params.seriesName !== 'mouseoutSeries') {
  251. return `${
  252. params.seriesName
  253. }<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${
  254. params.color
  255. };"></span>${option.series[params.seriesIndex].pieData.value}`;
  256. }
  257. },
  258. },
  259. //animation: false,
  260. legend: {
  261. orient: 'vertical',
  262. icon: 'circle',
  263. right: '3%',
  264. y: 'center',
  265. show: true,
  266. itemGap: 15,
  267. textStyle: {
  268. color: '#A1E2FF',
  269. fontSize: 13,
  270. },
  271. data: legendData,
  272. formatter: params => {
  273. return params;
  274. },
  275. },
  276. xAxis3D: {
  277. min: -1,
  278. max: 1,
  279. },
  280. yAxis3D: {
  281. min: -1,
  282. max: 1,
  283. },
  284. zAxis3D: {},
  285. grid3D: {
  286. viewControl: {
  287. autoRotate: true,
  288. //3d效果可以放大、旋转等,请自己去查看官方配置
  289. // alpha: 0,
  290. // beta: 100,
  291. // rotateSensitivity: 100,
  292. // zoomSensitivity: 100,
  293. // panSensitivity: 100,
  294. },
  295. top: '-10%',
  296. left: 'left',
  297. width: '60%',
  298. show: false,
  299. boxHeight: 50,
  300. },
  301. series: series,
  302. };
  303. return option;
  304. }
  305. // 传入数据生成 option
  306.  
  307. const initEcharts = () => {
  308. const optionData = [
  309. {
  310. name: '草(17.4k㎡,0.4%)',
  311. value: 17.4,
  312. itemStyle: {
  313. color: '#E7EA39',
  314. },
  315. },
  316. {
  317. name: '田 (4051.55k㎡,83.3%)',
  318. value: 4051.55,
  319. itemStyle: {
  320. color: '#E89668',
  321. },
  322. },
  323. {
  324. name: '林 (553.89k㎡,11.4%)',
  325. value: 553.89,
  326. itemStyle: {
  327. color: '#D863F4',
  328. },
  329. },
  330. {
  331. name: '湖 (78.54k㎡,1.6%)',
  332. value: 78.54,
  333. itemStyle: {
  334. color: '#03BABB',
  335. },
  336. },
  337. {
  338. name: '水 (165.05k㎡,3.4%)',
  339. value: 165.05,
  340. itemStyle: {
  341. color: '#3EB4F1',
  342. },
  343. },
  344. ];
  345. // 生成配置选项
  346. const option = getPie3D(optionData, 0.59);
  347. myChart.value = echarts.init(proxy.$refs.echarts3D);
  348.  
  349. // 绘制图形
  350. myChart.value.setOption(option);
  351. };
  352.  
  353. //自适应
  354. function resizeTheChart() {
  355. if (myChart.value) {
  356. myChart.value.resize();
  357. }
  358. }
  359.  
  360. onMounted(() => {
  361. initEcharts();
  362. window.addEventListener('resize', resizeTheChart);
  363. });
  364. </script>
  365.  
  366. <style lang="scss" scoped>
  367. .tslyqk_constituent_3d {
  368. width: 100%;
  369. height: 190px;
  370. position: relative;
  371. top: 30px;
  372. .echarts3D {
  373. height: 100%;
  374. height: 190px;
  375. position: relative;
  376. }
  377. }
  378. </style>