WorldMap.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import React from "react";
  2. import * as echarts from "echarts";
  3. import worldJson from "../world.json";
  4. import topIcon from "../../../../assets/bigscreen/map-bar-head.svg";
  5. class WorldMap2D extends React.Component {
  6. constructor(props) {
  7. super(props);
  8. this.chartRef = React.createRef();
  9. this.chartInstance = null;
  10. this.pulseTimer = null;
  11. }
  12. componentDidMount() {
  13. echarts.registerMap("world", worldJson);
  14. this.initChart();
  15. window.addEventListener("resize", this.resizeChart);
  16. this.startPulseAnimation();
  17. }
  18. componentWillUnmount() {
  19. if (this.chartInstance) this.chartInstance.dispose();
  20. if (this.pulseTimer) clearInterval(this.pulseTimer);
  21. window.removeEventListener("resize", this.resizeChart);
  22. }
  23. componentDidUpdate() {
  24. this.initChart();
  25. }
  26. resizeChart = () => {
  27. if (this.chartInstance) this.chartInstance.resize();
  28. };
  29. initChart() {
  30. if (!this.chartRef.current) return;
  31. this.chartInstance = echarts.init(this.chartRef.current);
  32. // 顶部图标数据
  33. this.topIconData = this.props.data.map((d) => ({
  34. name: d.name,
  35. value: [d.value[0], d.value[1], d.value[2]],
  36. symbolOffset: [0, (-d.value[2] / 2) * 50],
  37. }));
  38. // 底部光圈初始化
  39. this.pulseData = this.props.data.map((d) => ({
  40. name: d.name,
  41. value: [d.value[0], d.value[1], 0],
  42. }));
  43. // 初始图表
  44. const option = this.getChartOption();
  45. this.chartInstance.setOption(option);
  46. }
  47. // 获取图表配置
  48. getChartOption(currentPulseIndex = -1) {
  49. // 当前显示光圈的柱子
  50. const pulseSeriesData =
  51. currentPulseIndex >= 0 ? [this.pulseData[currentPulseIndex]] : [];
  52. return {
  53. tooltip: {
  54. // formatter: (params) => `${params.name} <br/>${params.value[2]}`,
  55. textStyle: { fontSize: 30, fontWeight: "bold" },
  56. },
  57. geo: {
  58. map: "world",
  59. roam: true,
  60. zoom: 1.1,
  61. itemStyle: {
  62. areaColor: "rgba(126, 206, 244, 0.1)",
  63. borderColor: "#2EA7E0",
  64. borderWidth: 2,
  65. },
  66. label: { show: false },
  67. emphasis: {
  68. itemStyle: {
  69. areaColor: "rgba(41, 241, 250, 0.6)",
  70. borderWidth: 1,
  71. borderColor: "rgba(41, 241, 250, 1)",
  72. shadowColor: "rgba(41, 241, 250, 1)",
  73. shadowBlur: 2,
  74. shadowOffsetX: 10,
  75. shadowOffsetY: -10,
  76. },
  77. label: { show: false },
  78. },
  79. },
  80. series: [
  81. // 主柱子
  82. {
  83. type: "scatter",
  84. coordinateSystem: "geo",
  85. symbol: "rect",
  86. symbolSize: (val) => [28, val[2] * 50],
  87. itemStyle: {
  88. color: {
  89. type: "linear",
  90. x: 0,
  91. y: 0,
  92. x2: 0,
  93. y2: 1,
  94. colorStops: [
  95. { offset: 0, color: "#EC903A" },
  96. { offset: 1, color: "rgba(18,92,178,0)" },
  97. ],
  98. },
  99. shadowColor: "#4fd2dd",
  100. shadowBlur: 10,
  101. borderRadius: [8, 8, 0, 0],
  102. },
  103. encode: { tooltip: 2 },
  104. data: this.props.data,
  105. },
  106. // 顶部图标
  107. {
  108. type: "scatter",
  109. coordinateSystem: "geo",
  110. symbol: `image://${topIcon}`,
  111. symbolSize: [33, 33],
  112. data: this.topIconData,
  113. },
  114. // 底部光圈
  115. {
  116. type: "effectScatter",
  117. coordinateSystem: "geo",
  118. rippleEffect: {
  119. period: 5, // 波纹扩散周期
  120. scale: 800, // 扩散倍数
  121. brushType: "stroke", // 只描边
  122. },
  123. symbol: "circle",
  124. symbolSize: 1, // 小中心点
  125. itemStyle: {
  126. color: "rgba(76,216,255,0.8)", // 中心点颜色
  127. shadowBlur: 0, // 去掉阴影
  128. shadowColor: "transparent",
  129. },
  130. data: pulseSeriesData,
  131. },
  132. ],
  133. };
  134. }
  135. // 启动依次扩散动画
  136. startPulseAnimation() {
  137. const { data } = this.props;
  138. clearInterval(this.pulseTimer);
  139. this.currentIndex = 0;
  140. this.pulseTimer = setInterval(() => {
  141. if (!this.chartInstance) return;
  142. const pulseData = [data[this.currentIndex]]; // 当前光圈数据
  143. // 更新 effectScatter
  144. this.chartInstance.setOption({
  145. series: [{}, {}, { data: pulseData }],
  146. });
  147. // 自动显示 tooltip
  148. this.chartInstance.dispatchAction({
  149. type: "showTip",
  150. seriesIndex: 0, // 主柱子 series
  151. dataIndex: this.currentIndex,
  152. });
  153. this.currentIndex = (this.currentIndex + 1) % data.length;
  154. }, 2000);
  155. }
  156. render() {
  157. return (
  158. <div ref={this.chartRef} style={{ width: "100%", height: "100%" }} />
  159. );
  160. }
  161. }
  162. export default WorldMap2D;