FollowRingChart.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. <template>
  2. <div class="chart-container">
  3. <div id="ring-chart" ref="chart" class="ring-chart"></div>
  4. <!-- //圆心-->
  5. <!-- <div id="cc" style="position: absolute;width: 5px;height: 5px;background: #ff0000"></div>-->
  6. <!--<div id="ring-chart" ref="chart" class="ring-chart"-->
  7. <!--@mousedown="begin" @mousemove="moving" @mouseup="stopMoving"-->
  8. <!--&gt;</div>-->
  9. </div>
  10. </template>
  11. <script setup>
  12. import {ref, onMounted, onBeforeUnmount} from 'vue'
  13. import * as echarts from 'echarts'
  14. document.body.style.overflow = 'hidden';
  15. const props = defineProps({
  16. chartProps: {}
  17. })
  18. const isFinish = ref(props.chartProps.isFinish)
  19. const chart = ref(null)
  20. let myChart = null
  21. const currentValue = ref(1) // 初始值
  22. const isBegin = ref(false)
  23. // 鼠标状态
  24. const isDragging = ref(false)
  25. const startAngle = ref(0)
  26. const startValue = ref(0)
  27. const option = ref({})
  28. // 初始化图表
  29. const initChart = () => {
  30. if (!chart.value) return
  31. myChart = echarts.init(chart.value, null, {width: width, height: height})
  32. option.value = {
  33. series: [{
  34. type: 'pie',
  35. radius: [radiusMax, radiusMin],
  36. startAngle: 180,
  37. endAngle: 360,
  38. avoidLabelOverlap: false,
  39. itemStyle: {
  40. borderRadius: 10,
  41. borderColor: '#ffff00',
  42. borderWidth: 2
  43. },
  44. label: {
  45. show: true,
  46. position: 'center',
  47. // formatter: '{c}%',
  48. fontSize: 24,
  49. fontWeight: 'bold',
  50. color: '#333'
  51. },
  52. emphasis: {
  53. scale: false,
  54. itemStyle: {
  55. shadowBlur: 10,
  56. shadowOffsetX: 0,
  57. shadowColor: 'rgba(0, 0, 0, 0.5)'
  58. }
  59. },
  60. data: [
  61. {value: currentValue.value, name: '', itemStyle: {color: '#5470c6'}},
  62. {value: 100 - currentValue.value, name: '', itemStyle: {color: '#eee'}}
  63. ]
  64. }]
  65. }
  66. myChart.setOption(option.value)
  67. // 添加图表鼠标事件
  68. // myChart.getZr().on('mousedown', startDrag)
  69. // myChart.getZr().on('mouseup', endDrag)
  70. // myChart.getZr().on('mousemove', handleDrag)
  71. }
  72. // 更新图表数据
  73. const updateChart = (newValue) => {
  74. if (!myChart) return
  75. currentValue.value = Math.max(0, Math.min(100, newValue)) // 限制在0-100之间
  76. myChart.setOption({
  77. series: [{
  78. data: [
  79. {value: currentValue.value},
  80. {value: 100 - currentValue.value}
  81. ]
  82. }]
  83. })
  84. }
  85. // 开始拖动
  86. const startDrag = (e) => {
  87. if (e.target && e.target.type === 'arc') {
  88. isDragging.value = true
  89. startAngle.value = getAngle(e.offsetX, e.offsetY)
  90. startValue.value = currentValue.value
  91. }
  92. }
  93. // 结束拖动
  94. const endDrag = () => {
  95. isDragging.value = false
  96. }
  97. // 处理拖动
  98. const handleDrag = (e) => {
  99. if (!isDragging.value) return
  100. const currentAngle = getAngle(e.offsetX, e.offsetY)
  101. const angleDiff = currentAngle - startAngle.value
  102. const valueDiff = (angleDiff / 360) * 100
  103. updateChart(startValue.value + valueDiff)
  104. }
  105. // 获取鼠标位置对应的角度
  106. const getAngle = (x, y) => {
  107. const rect = chart.value.getBoundingClientRect()
  108. const centerX = rect.width / 2
  109. const centerY = rect.height / 2
  110. const deltaX = x - centerX
  111. const deltaY = y - centerY
  112. const angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI
  113. return (angle + 450) % 360 // 调整为0-360度
  114. }
  115. onMounted(() => {
  116. initChart()
  117. init()
  118. })
  119. onBeforeUnmount(() => {
  120. // 移除事件监听
  121. if (myChart) {
  122. myChart.getZr().off('mousedown', startDrag)
  123. myChart.getZr().off('mouseup', endDrag)
  124. myChart.getZr().off('mousemove', handleDrag)
  125. myChart.dispose()
  126. }
  127. })
  128. //环形相关信息
  129. const width = props.chartProps.width
  130. const height = props.chartProps.height
  131. const left = props.chartProps.left
  132. const top = props.chartProps.top
  133. const radiusMax = props.chartProps.radiusMax
  134. const radiusMin = props.chartProps.radiusMin
  135. const center_x = left + width / 2
  136. const center_y = top + height / 2
  137. function init() {
  138. //
  139. // var cc = document.getElementById("cc");
  140. // console.log("cccccc", "top:", top, "left:", left, "width:", width, "height:", height, "center_y:", center_y, "center_x", center_x)
  141. // cc.style.top = center_y + "px"
  142. // cc.style.left = center_x + "px"
  143. let chartDiv = document.getElementById("ring-chart");
  144. chartDiv.addEventListener('touchstart', begin)
  145. chartDiv.addEventListener('touchmove', moving)
  146. chartDiv.addEventListener('touchend', stopMoving)
  147. }
  148. function testMove(e) {
  149. let x = e.targetTouches[0].pageX;
  150. let y = e.targetTouches[0].pageY;
  151. }
  152. //取点和圆心的角度
  153. function calculateAngleDegrees(x1, y1, x2, y2) {
  154. const dx = x2 - x1;
  155. const dy = y2 - y1;
  156. const radians = Math.atan2(dy, dx);
  157. return radians * (180 / Math.PI); // 弧度转角度
  158. }
  159. //
  160. const beginCorner = ref(0)
  161. const beginPoint = ref({})
  162. const endCorner = ref(0)
  163. const endPoint = ref({})
  164. // function begin(event) {
  165. //
  166. // if (!isFinish.value) {
  167. // if (!event.targetTouches || event.targetTouches.length === 0) return;
  168. //
  169. // // 获取触摸点坐标(相对于视口)
  170. // const touch = event.targetTouches[0];
  171. // const mouse_x = touch.clientX;
  172. // const mouse_y = touch.clientY;
  173. //
  174. // // 获取图表容器的位置(相对于视口)
  175. // const chartRect = chart.value.getBoundingClientRect();
  176. // const center_x = chartRect.left + chartRect.width / 2;
  177. // const center_y = chartRect.top + chartRect.height / 2;
  178. //
  179. // // 计算角度(调整为 ECharts 需要的坐标系)
  180. // let ang = calculateAngleDegrees(mouse_x, mouse_y, center_x, center_y);
  181. // console.log("*********************",mouse_x, mouse_y, center_x, center_y)
  182. // // 将角度转换为 ECharts 的 startAngle 格式:
  183. // // - 0° 在右侧,顺时针方向
  184. // // - 调整为 180° 到 360° 的范围(根据你的半圆设计)
  185. // ang = (360 - ang) % 360; // 反转方向(逆时针 → 顺时针)
  186. // ang = Math.max(180, Math.min(360, ang)); // 限制在半圆范围内
  187. //
  188. // // 更新起始角度
  189. // beginCorner.value = 360 - ang;
  190. // option.value.series[0].startAngle = 180 + ang;
  191. // myChart.setOption(option.value);
  192. // isBegin.value = true;
  193. // }
  194. //
  195. // }
  196. function begin(event) {
  197. console.log("=====", props.chartProps.status)
  198. if (!event.targetTouches || event.targetTouches.length === 0) return;
  199. //鼠标按下,取点
  200. let mouse_x = event.targetTouches[0].pageX;
  201. let mouse_y = event.targetTouches[0].pageY;
  202. if (props.chartProps.status == 'edit' && isFinish.value == true) {
  203. //
  204. console.log(mouse_x,"11111",mouse_x)
  205. //
  206. //计算一下离谁比较近
  207. let d_begin = Math.pow((beginPoint.value.x - mouse_x),2)+ Math.pow((beginPoint.value.y - mouse_y),2)
  208. let d_end = Math.pow((endPoint.value.x - mouse_x),2)+ Math.pow((endPoint.value.y - mouse_y),2)
  209. if(d_begin <= d_end){
  210. let ang = calculateAngleDegrees(mouse_x, mouse_y, center_x, center_y)
  211. option.value.series[0].startAngle = 180 - ang
  212. beginCorner.value = ang
  213. beginPoint.value ={
  214. x : mouse_x,
  215. y : mouse_y
  216. }
  217. //
  218. }else{
  219. let eang = calculateAngleDegrees(mouse_x, mouse_y, center_x, center_y)
  220. option.value.series[0].data[0].value = (eang - beginCorner.value) * 5 / 9 + beginCorner.value / 30
  221. option.value.series[0].data[1].value = 100 - (eang - beginCorner.value) * 5 / 9 - beginCorner.value / 30
  222. endCorner.value = eang
  223. endPoint.value ={
  224. x : mouse_x,
  225. y : mouse_y
  226. }
  227. }
  228. } else if (props.chartProps.status == 'new') {
  229. let ang = calculateAngleDegrees(mouse_x, mouse_y, center_x, center_y)
  230. beginCorner.value = ang
  231. beginPoint.value = {
  232. x: mouse_x,
  233. y: mouse_y
  234. }
  235. console.log("ssss",beginPoint.value.x,beginPoint.value.y)
  236. option.value.series[0].startAngle = 180 - ang
  237. isBegin.value = true
  238. }
  239. myChart.setOption(option.value)
  240. }
  241. function stopMoving() {
  242. nextTick(() => {
  243. isFinish.value = true
  244. isBegin.value = false
  245. myChart.setOption(option.value)
  246. })
  247. window.setTimeout(finish, 1000)
  248. }
  249. function finish() {
  250. //alert("绘制完成!")
  251. }
  252. function moving(event) {
  253. let mouse_x = event.targetTouches[0].pageX;
  254. let mouse_y = event.targetTouches[0].pageY;
  255. let eang = calculateAngleDegrees(mouse_x, mouse_y, center_x, center_y)
  256. if (eang >= 0 && eang <= 180) {
  257. option.value.series[0].data[0].value = (eang - beginCorner.value) * 5 / 9 + beginCorner.value / 30
  258. option.value.series[0].data[1].value = 100 - (eang - beginCorner.value) * 5 / 9 - beginCorner.value / 30
  259. }
  260. endCorner.value = eang
  261. endPoint.value = {
  262. x: mouse_x,
  263. y: mouse_y
  264. }
  265. myChart.setOption(option.value)
  266. }
  267. // function moving(event) {
  268. // if (!isFinish.value && isBegin.value == true) {
  269. // let mouse_x = event.targetTouches[0].pageX;
  270. // let mouse_y = event.targetTouches[0].pageY;
  271. // let eang = calculateAngleDegrees(mouse_x, mouse_y, center_x, center_y)
  272. //
  273. // eang = (360 - eang) % 360; // 反转方向(逆时针 → 顺时针)
  274. // eang = Math.max(180, Math.min(360, eang)); // 限制在半圆范围内
  275. // eang = 360 - eang
  276. // if (eang >= 0 && eang <= 180) {
  277. // if (eang >= 0 && eang <= 90) {
  278. // option.value.series[0].data[0].value = (eang - beginCorner.value) * 5 / 9
  279. // option.value.series[0].data[1].value = 100 - (eang - beginCorner.value) * 5 / 9
  280. // } else {
  281. // option.value.series[0].data[0].value = (eang) * 5 / 9
  282. // option.value.series[0].data[1].value = 100 - (eang) * 5 / 9
  283. // }
  284. // myChart.setOption(option.value)
  285. // }
  286. // }
  287. //
  288. // }
  289. </script>
  290. <style scoped>
  291. .chart-container {
  292. position: relative;
  293. width: 1700px;
  294. height: 910px;
  295. display: flex;
  296. flex-direction: column;
  297. align-items: center;
  298. justify-content: center;
  299. }
  300. .ring-chart {
  301. width: 1700px;
  302. height: 910px;
  303. cursor: pointer;
  304. border: 1px solid white;
  305. margin-top: 219px;
  306. }
  307. </style>