2024-03-11 01:33:47 +08:00

151 lines
3.3 KiB
Vue

<!--
* @Author: ShawnPhang
* @Date: 2023-11-29 10:34:54
* @Description: 角度手柄
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-11-29 19:24:14
-->
<template>
<div class="angle-input-box">
<input ref="numInput" v-model="num" class="angle-input" @focus="visiable = true" @blur="visiable = false" @input="inputChange" />
<div v-show="visiable" class="AngleHandle" @mousedown="touch($event, true)" @mouseup="touch($event, false)">
<div class="angle" @mouseup="turn" @mousemove="turn">
<div :style="`transform: rotate(${angleInDegrees}deg)`" class="line"></div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, watch, computed } from 'vue'
export default defineComponent({
props: ['modelValue'],
emits: ['change', 'update:modelValue'],
setup(props, { emit }) {
const num = ref(90)
const numInput = ref(null)
const angleInDegrees = computed(() => {
return num.value - 90
})
let inProcess = false
const visiable = ref(false)
const inputChange = (e: any) => {
emit('change', e)
}
watch(
() => num.value,
(v) => {
props.modelValue !== num.value && emit('update:modelValue', v)
emit('change')
},
)
watch(
() => props.modelValue,
(v) => {
num.value = v
},
)
const turn = (e: any) => {
if (!inProcess) {
return
}
const origin = { x: 27, y: 27 }
// 计算相对于原点的坐标差值
const deltaX = e.offsetX - origin.x
const deltaY = e.offsetY - origin.y
// 计算夹角(弧度)
const angleInRadians = Math.atan2(deltaY, deltaX)
// 将弧度转换为角度
const angleInDegrees = (angleInRadians * 180) / Math.PI
num.value = Math.round(angleInDegrees + 90)
}
const touch = (e: any, isHandle: boolean) => {
e.preventDefault()
inProcess = isHandle
}
return { inputChange, num, turn, touch, angleInDegrees, numInput, visiable }
},
})
</script>
<style lang="less">
.angle-input {
width: 38px;
margin-left: 5px;
padding: 0 0 0 4px;
border: 1px solid #e8eaec;
border-radius: 4px;
position: relative;
}
.angle-input-box {
position: relative;
}
.angle-input-box::after {
content: '°';
width: 5px;
height: 2px;
position: absolute;
right: 2px;
top: 0;
}
.AngleHandle {
position: absolute;
z-index: 2;
right: 2px;
margin-top: 3px;
background: #ffffff;
width: 60px;
height: 60px;
border-radius: 7px;
box-shadow: 0 0 2px rgb(0 0 0 / 60%);
display: flex;
align-items: center;
justify-content: center;
.angle {
width: 54px;
height: 54px;
position: relative;
overflow: hidden;
background: #f1f2f4;
border-radius: 50%;
user-select: none;
cursor: pointer;
}
.line {
position: absolute;
top: 50%;
left: 50%;
width: 50%;
height: 1px;
background: #999999;
pointer-events: none;
transform-origin: left top;
}
.line::before {
position: absolute;
content: '';
left: -1px;
top: -1px;
width: 3px;
height: 3px;
border-radius: 50%;
background: #999999;
}
.line::after {
position: absolute;
content: '';
right: 0;
top: -2px;
width: 5px;
height: 5px;
border-radius: 50%;
background: #999999;
}
}
</style>