|
@@ -0,0 +1,221 @@
|
|
|
+<template>
|
|
|
+ <div class="chart-container">
|
|
|
+ <div ref="chart" class="ring-chart" @mousedown="begin" @mousemove="moving" @mouseup="stopMoving"></div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+ import {ref, onMounted, onBeforeUnmount} from 'vue'
|
|
|
+ import * as echarts from 'echarts'
|
|
|
+
|
|
|
+ const props = defineProps({
|
|
|
+ chartProps: {}
|
|
|
+ })
|
|
|
+
|
|
|
+
|
|
|
+ const chart = ref(null)
|
|
|
+ let myChart = null
|
|
|
+ const currentValue = ref(1) // 初始值
|
|
|
+
|
|
|
+ const isBegin = ref(false)
|
|
|
+
|
|
|
+ // 鼠标状态
|
|
|
+ const isDragging = ref(false)
|
|
|
+ const startAngle = ref(0)
|
|
|
+ const startValue = ref(0)
|
|
|
+
|
|
|
+ const option = ref({})
|
|
|
+
|
|
|
+ // 初始化图表
|
|
|
+ const initChart = () => {
|
|
|
+ if (!chart.value) return
|
|
|
+
|
|
|
+ myChart = echarts.init(chart.value)
|
|
|
+
|
|
|
+ option.value = {
|
|
|
+ series: [{
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['80%', '85%'],
|
|
|
+ startAngle: 180,
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 10,
|
|
|
+ borderColor: props.chartProps.borderColor,
|
|
|
+ borderWidth: 2
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'center',
|
|
|
+ // formatter: '{c}%',
|
|
|
+ fontSize: 24,
|
|
|
+ fontWeight: 'bold',
|
|
|
+ color: '#333'
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ scale: false,
|
|
|
+ itemStyle: {
|
|
|
+ shadowBlur: 10,
|
|
|
+ shadowOffsetX: 0,
|
|
|
+ shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data: [
|
|
|
+ {value: currentValue.value, name: '', itemStyle: {color: '#5470c6'}},
|
|
|
+ {value: 100 - currentValue.value, name: '', itemStyle: {color: '#eee'}}
|
|
|
+ ]
|
|
|
+ }]
|
|
|
+ }
|
|
|
+
|
|
|
+ myChart.setOption(option.value)
|
|
|
+
|
|
|
+ // 添加图表鼠标事件
|
|
|
+ // myChart.getZr().on('mousedown', startDrag)
|
|
|
+ // myChart.getZr().on('mouseup', endDrag)
|
|
|
+ // myChart.getZr().on('mousemove', handleDrag)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新图表数据
|
|
|
+ const updateChart = (newValue) => {
|
|
|
+ if (!myChart) return
|
|
|
+
|
|
|
+ currentValue.value = Math.max(0, Math.min(100, newValue)) // 限制在0-100之间
|
|
|
+
|
|
|
+ myChart.setOption({
|
|
|
+ series: [{
|
|
|
+ data: [
|
|
|
+ {value: currentValue.value},
|
|
|
+ {value: 100 - currentValue.value}
|
|
|
+ ]
|
|
|
+ }]
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 开始拖动
|
|
|
+ const startDrag = (e) => {
|
|
|
+ if (e.target && e.target.type === 'arc') {
|
|
|
+ isDragging.value = true
|
|
|
+ startAngle.value = getAngle(e.offsetX, e.offsetY)
|
|
|
+ startValue.value = currentValue.value
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 结束拖动
|
|
|
+ const endDrag = () => {
|
|
|
+ isDragging.value = false
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理拖动
|
|
|
+ const handleDrag = (e) => {
|
|
|
+ if (!isDragging.value) return
|
|
|
+
|
|
|
+ const currentAngle = getAngle(e.offsetX, e.offsetY)
|
|
|
+ const angleDiff = currentAngle - startAngle.value
|
|
|
+ const valueDiff = (angleDiff / 360) * 100
|
|
|
+
|
|
|
+ updateChart(startValue.value + valueDiff)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取鼠标位置对应的角度
|
|
|
+ const getAngle = (x, y) => {
|
|
|
+ const rect = chart.value.getBoundingClientRect()
|
|
|
+ const centerX = rect.width / 2
|
|
|
+ const centerY = rect.height / 2
|
|
|
+ const deltaX = x - centerX
|
|
|
+ const deltaY = y - centerY
|
|
|
+ const angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI
|
|
|
+ return (angle + 450) % 360 // 调整为0-360度
|
|
|
+ }
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ initChart()
|
|
|
+ })
|
|
|
+
|
|
|
+ onBeforeUnmount(() => {
|
|
|
+ // 移除事件监听
|
|
|
+ if (myChart) {
|
|
|
+ myChart.getZr().off('mousedown', startDrag)
|
|
|
+ myChart.getZr().off('mouseup', endDrag)
|
|
|
+ myChart.getZr().off('mousemove', handleDrag)
|
|
|
+ myChart.dispose()
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ //环形相关信息
|
|
|
+ const width = props.chartProps.width
|
|
|
+ const height = props.chartProps.height
|
|
|
+ const left = props.chartProps.left
|
|
|
+ const top = props.chartProps.top
|
|
|
+ const center_x = left + width / 2
|
|
|
+ const center_y = top + height / 2
|
|
|
+
|
|
|
+ function init() {
|
|
|
+ console.log(center_x, "***", center_y)
|
|
|
+ }
|
|
|
+
|
|
|
+ //取点和圆心的角度
|
|
|
+ function calculateAngleDegrees(x1, y1, x2, y2) {
|
|
|
+ const dx = x2 - x1;
|
|
|
+ const dy = y2 - y1;
|
|
|
+ const radians = Math.atan2(dy, dx);
|
|
|
+ return radians * (180 / Math.PI); // 弧度转角度
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //
|
|
|
+ const beginCorner = ref(0)
|
|
|
+
|
|
|
+ function begin(event) {
|
|
|
+ //鼠标按下,取点
|
|
|
+ let mouse_x = event.pageX;
|
|
|
+ let mouse_y = event.pageY;
|
|
|
+ let ang = calculateAngleDegrees(mouse_x, mouse_y, center_x, center_y)
|
|
|
+ beginCorner.value = ang
|
|
|
+ option.value.series[0].startAngle = 180 - ang
|
|
|
+ myChart.setOption(option.value)
|
|
|
+ isBegin.value = true
|
|
|
+ }
|
|
|
+
|
|
|
+ function stopMoving() {
|
|
|
+ isBegin.value = false
|
|
|
+ }
|
|
|
+
|
|
|
+ function moving(event) {
|
|
|
+ if (isBegin.value == true) {
|
|
|
+ let mouse_x = event.pageX;
|
|
|
+ let mouse_y = event.pageY;
|
|
|
+ let eang = calculateAngleDegrees(mouse_x, mouse_y, center_x, center_y)
|
|
|
+ if (eang >= 0 && eang <= 180) {
|
|
|
+ option.value.series[0].data[0].value = (eang - beginCorner.value) * 5 / 18
|
|
|
+ option.value.series[0].data[1].value = 100 - (eang - beginCorner.value) * 5 / 18
|
|
|
+ myChart.setOption(option.value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ init()
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+ .chart-container {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 400px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ring-chart {
|
|
|
+ width: 1000px;
|
|
|
+ height: 1000px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ .instructions {
|
|
|
+ text-align: center;
|
|
|
+ font-size: 16px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+</style>
|