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>