VUE 轮盘方向控制获取角度
功能实现样式

(拖拽中间的球 获取当前的角度)
百分比样式 适配多种组件
<template>
<div class="videoControl" @mousemove="movePoint">
<!-- <div class="cubeList" >
<p></p>
<p></p>
</div> -->
<div class="round">
<div class="video videoData">
<div class="movePoint" :class="downStatus?'pos':''" :style="{'left': left + 'px', 'top': top + 'px'}" @mousedown="downPoint" @mouseup="upPoint">
</div>
</div>
</div>
<ul>
<li @mousedown="toMove(8)" @mouseup="upPoint"><img src="@/assets/img/icon/fangda.svg" alt=""></li>
<li @mousedown="toMove(9)" @mouseup="upPoint"><img src="@/assets/img/icon/suoxiao.svg" alt=""></li>
<li><img src="@/assets/img/icon/xiangji1.svg" alt=""></li>
<li @click="presetMove"><img src="@/assets/img/icon/zhongzhi.svg" alt=""></li>
</ul>
</div>
</template>
<script lang="ts">
import { Options, Vue} from 'vue-class-component';
import axios from "axios";
import { message} from 'ant-design-vue';
const http = axios.create({
baseURL: 'https://open.ys7.com/api/lapp/device/',
timeout: 20000, // request timeout
});
http.interceptors.request.use(
config => {
let headers:any = config.headers;
headers['Content-Type'] = 'application/x-www-form-urlencoded';
return config;
}
)
http.interceptors.response.use(
response => {
let data = response.data;
if(data.code != 200) {
message.error(data.msg)
}
return data;
},
error => {
console.error(error);
}
);
interface Point{
x: number;
y: number;
}
let currentIndex:string|number = 1000000 //当前操作的方向
@Options({
props: {
msg: String,
videoParams: null
},
watch: {
downStatus:function (ne:any, old:any) {
if(!ne) {
this.upPoint()
}
}
},
data: () => {
return {
a: 1,
downStatus: false,
left: 0,
top: 0,
offsetWidth: 0,
pageX: 0,
pageY: 0,
moveWidth: 0, // 可移动区域长度
status : false,
status2: false,
}
},
mounted() {
let _this =this
let video:any = document.querySelector('.videoData')
let crcile:any = document.querySelector('.movePoint')
this.offsetWidth = crcile.offsetWidth
this.moveWidth = video.offsetWidth / 1.5 - (crcile.offsetWidth / 1.5);
window.addEventListener('mouseup', function(e:any) {
_this.downStatus = false
})
},
methods: {
// 发送请求
toRequstion() {
},
angle(start:Point,end:Point):number{
let diff_x = end.x - start.x,
diff_y = end.y - start.y;
//返回角度,不是弧度
return 360*Math.atan2(diff_y,diff_x)/(2*Math.PI);
},
upPoint(e:any) {
let params = this.videoParams
let data = `accessToken=${params.accessToken}&deviceSerial=${params.deviceSerial}&channelNo=${params.channelNo}`
http.post('ptz/stop', data).then(res => {
})
currentIndex = 1000000
this.downStatus = false
},
downPoint(e:any) {
this.left = e.offsetX - (this.offsetWidth /2 )
this.top = e.offsetY - (this.offsetWidth /2 )
this.pageX = e.pageX
this.pageY = e.pageY
this.downStatus= true
},
// 获取方向
getdirtion(value:number):number {
let newValue = value
if(value < 0) {
newValue = 360 + value
}
return newValue
},
// 请求方向接口
getVideoDIrction(angle: number) {
let part = 45;
let fistAngle = 45/2
let index = 0
if(angle > fistAngle && angle <= (part + fistAngle) ) { // 右上方
index = 6
} else if(angle > (part + fistAngle) && angle <= (2 * part + fistAngle)) { // 正上
index = 0
} else if(angle > (2 * part + fistAngle) && angle <= (3 * part + fistAngle)) { // 左上
index = 4
} else if(angle > (3 * part + fistAngle) && angle <= (4 * part + fistAngle)) { // 左
index = 2
} else if(angle > (4 * part + fistAngle) && angle <= (5 * part + fistAngle)) { // 左下
index = 5
} else if(angle > (5 * part + fistAngle) && angle <= (6 * part + fistAngle)) { // 下
index = 1
} else if(angle > (6 * part + fistAngle) && angle <= (7 * part + fistAngle)) { // 右下
index = 7
} else { // 右
index = 3
}
if(currentIndex != index) {
this.toMove(index)
}
// if(angle > 0)
},
// 移动视频
toMove(index:number|string) {
currentIndex = index
let params = this.videoParams
let data = `accessToken=${params.accessToken}&deviceSerial=${params.deviceSerial}&channelNo=${params.channelNo}&direction=${index}&speed=1`
http.post('ptz/start', data).then(res => {
console.log(res, 'start')
})
},
// 调用预置点
presetMove() {
let params = this.videoParams
let data = `accessToken=${params.accessToken}&deviceSerial=${params.deviceSerial}&channelNo=${params.channelNo}&index=1`;
http.post('preset/move', data).then(res => {
console.log(res, 'move')
})
},
movePoint(e:any) {
if(this.downStatus) {
let x = e.pageX - this.pageX
let y = e.pageY - this.pageY
let pos = this.angle({x:0, y:0}, {x:x, y:-y})
// 获取角度
let angle = this.getdirtion(pos)
// 超过范围执行
if(Math.abs(x) > this.moveWidth / 2 || Math.abs(y) > this.moveWidth / 2 ) {
this.getVideoDIrction(angle)
}
if(Math.abs(x) > this.moveWidth) {
this.left = Math.cos( 2 * Math.PI / 360 * angle) * (this.moveWidth + this.offsetWidth/5)
this.top = Math.sin( 2 * Math.PI / 360 * angle) * (this.moveWidth + this.offsetWidth/5) * -1
this.status = false
} else {
this.status = true
if(this.status && this.status2) {
this.left = x
}
}
if(Math.abs(y) > this.moveWidth) {
this.left = Math.cos( 2 * Math.PI / 360 * angle) * (this.moveWidth + this.offsetWidth/5)
this.top = Math.sin( 2 * Math.PI / 360 * angle) * (this.moveWidth + this.offsetWidth/5) * -1
this.status2 = false
} else {
this.status2 = true
if(this.status && this.status2) {
this.top = y
}
}
}
}
}
})
export default class HelloWorld extends Vue {
msg!: string
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.videoControl {
width: 100%;
height: 100%;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
.cubeList {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
p:first-child {
width: 100%;
height: 10px;
background-color: #122656;
}
p:last-child {
position: absolute;
height: 100%;
width: 10px;
background-color: #122656;
}
}
ul {
position: absolute;
display: flex;
justify-content: space-between;
// align-content: flex-start;
align-content: space-between;
width: 100%;
height: 100%;
// top: 0;
flex-wrap: wrap;
li{
width: 45%;
height: 45%;
display: flex;
justify-content: center;
position: relative;
cursor: pointer;
user-select: none;
background-image: url(../assets/img/icon/yueya.svg);
background-repeat: no-repeat;
background-size: 100% 100%;
img {
width: 20%;
position: relative;
left: 12%;
top: -12%;
}
}
li:active {
background-image: url(../assets/img/icon/yueya2.svg);
}
li:nth-child(1) {
img {
transform: rotate(90deg);
}
transform: rotate(-90deg);
}
li:nth-child(3) {
transform: rotate(-180deg);
// top: 10px;
img {
transform: rotate(180deg);
}
}
li:nth-child(4) {
transform: rotate(-270deg);
// top: 10px;
}
}
}
.round {
width: 50%;
height: 50%;
}
.video {
width: 100%;
height: 100%;
// background-color: rgba(12,27,61, 0.3);
border-radius: 50%;
display: flex;
flex-wrap: wrap;
position: relative;
justify-content: center;
align-items: center;
z-index: 10;
.movePoint {
cursor: pointer;
height: 50%;
width: 50%;
border: 2px solid #777B89;
border-radius: 50%;
cursor: pointer;
background-color: #777B89;
}
}
.pos {
position: relative;
}
// .video>div {
// width: 50%;
// height: 50%;
// cursor: pointer;
// }
</style>

浙公网安备 33010602011771号