FollowRingChart.vue 12 KB

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