使用vue实现模拟摇杆组件
点击中心圆拖动鼠标,最大范围是外圈圆,实时获取摇动角度
新建rocker.vue
html
<script setup lang="ts">
const { r } = withDefaults(defineProps<{
r: number,
inR: number
}>(), {
r: 100,
inR: 30
})
let outCircleRef = ref<HTMLElement>()
let innerCircleRef = ref<HTMLElement>()
let mouseAngle = ref<number>(0)
let mouseAngleComp = computed(() => {
return mouseAngle.value.toFixed(2) + '°'
})
defineExpose({ mouseAngleComp })
function mouseDown(e: MouseEvent) {
document.body.addEventListener('mousemove', mouseMove)
}
function mouseMove(e: MouseEvent) {
if (outCircleRef.value && innerCircleRef.value) {
let centerX = outCircleRef.value.offsetLeft + r
let centerY = outCircleRef.value.offsetTop + r
let disToCenter = distance(centerX, centerY, e.clientX, e.clientY)
let ratioX = 0
let ratioY = 0
if (disToCenter <= r) {
ratioX = (e.clientX - outCircleRef.value.offsetLeft) / (r * 2) * 100
ratioY = (e.clientY - outCircleRef.value.offsetTop) / (r * 2) * 100
} else {
let copyX = e.clientX
let copyY = e.clientY
while (disToCenter > r) {
copyX += copyX > centerX ? -2 : 2
copyY += copyY > centerY ? -2 : 2
disToCenter = distance(centerX, centerY, copyX, copyY)
}
ratioX = (copyX - outCircleRef.value.offsetLeft) / (r * 2) * 100
ratioY = (copyY - outCircleRef.value.offsetTop) / (r * 2) * 100
}
innerCircleRef.value.style.left = ratioX + '%'
innerCircleRef.value.style.top = ratioY + '%'
mouseAngle.value = (Math.atan2(ratioX, ratioY) * 180) % 360
}
}
function mouseUp(e: MouseEvent) {
document.body.removeEventListener('mousemove', mouseMove)
innerCircleRef.value!.style.left = '50%'
innerCircleRef.value!.style.top = '50%'
mouseAngle.value = 0
}
function distance(x0: number, y0: number, x1: number, y1: number) {
return Math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2)
}
onMounted(() => {
innerCircleRef.value?.addEventListener('mousedown', mouseDown)
document.body.addEventListener('mouseup', mouseUp)
})
</script>
<template>
<div>
<div class="out_circle" ref="outCircleRef" :style="{ width: r * 2 + 'px', height: r * 2 + 'px' }">
<div class="inner_circle" ref="innerCircleRef" :style="{ width: inR * 2 + 'px', height: inR * 2 + 'px' }">
Press
</div>
</div>
</div>
</template>
<style scoped>
.out_circle {
border-radius: 50%;
background-color: #ddd;
position: relative;
margin-top: 8px;
.inner_circle {
border-radius: 50%;
background-color: #999;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
user-select: none;
&:active {
background: #666;
}
}
}
</style>
使用:
html
<rocker ref="rockerRef" :r="100" :in-r="50"/>