From 678f5050633c1bc53ba92bdb7aa8b85d055b443f Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Fri, 18 Apr 2025 19:54:47 +0800
Subject: [PATCH] feat: 指南针60%
---
src/hooks/controlDrone/useManualControl.js | 4
src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue | 14
src/components/CurrentTaskDetails/TaskDetailsHead.vue | 11
src/components/CurrentTaskDetails/TaskDetailsRight.vue | 33 ++-
src/components/CurrentTaskDetails/ControlPanel/ControlComPass/ControlComPass.vue | 571 ++++++++++++++++++---------------------------------
src/components/CurrentTaskDetails/CurrentTaskDetails.vue | 12
6 files changed, 255 insertions(+), 390 deletions(-)
diff --git a/src/components/CurrentTaskDetails/ControlPanel/ControlComPass/ControlComPass.vue b/src/components/CurrentTaskDetails/ControlPanel/ControlComPass/ControlComPass.vue
index 5685ca1..60530dc 100644
--- a/src/components/CurrentTaskDetails/ControlPanel/ControlComPass/ControlComPass.vue
+++ b/src/components/CurrentTaskDetails/ControlPanel/ControlComPass/ControlComPass.vue
@@ -1,392 +1,229 @@
<template>
- <div class="instrument-content">
- <div class="left-img" :data-text="`${attitude_pitch}°`">
- <div class="scaleImg">
- <p class="scale" :style="{ top: 45 + ScaleTop + 'px' }"></p>
- <img src="../../../../assets/images/rightmapidentification.png" />
- </div>
- </div>
- <div class="instrument-center">
- <div class="compass-box" :data-text="`${prevRotate?.toFixed(2)}°`">
- <div v-for="(item, index) in str" :key="index" class="scale"
- :style="{ '--rotate': 30 * index - prevRotate + 'deg' }">
- <span class="text">{{ item }}</span>
- </div>
- </div>
- <div class="center-show">
- <img src="../../../../assets/images/mapidentification.png" />
- </div>
- <div class="rotat-btn"></div>
- </div>
- <div class="right-img" :data-text="`${height}m`">
- <div class="ident-arrow">
- <img src="../../../../assets/images/leftmapidentification.png" />
- <div class="arrow-box" :style="{ bottom: realHeight }">
- <div class="arrow"></div>
- </div>
- </div>
- </div>
- </div>
-</template>
-<script>
-import _ from 'lodash';
-export default {
- data () {
- return {
- str: ['W', 30, 33, 'N', 3, 6, 'E', 12, 15, 'S', 21, 24],
- // 俯仰角度数
- attitude_pitch: 0,
- // 真空高度
- height: 0,
- // 旋转方向角度
- prevRotate: 0,
- dockHeight: 0,
- }
- },
- watch: {
- roamPoint: {
- handler (val) {
- // this.prevRotate = val.arrowHeading
- // this.attitude_pitch = val.roll
- // this.height = _.round(val.altitude,1)
- },
- },
- },
- computed: {
- // roamPoint: vuexStateSimplify('pointsWayLine', 'roamPoint'),
- ScaleTop () {
- return (-this.attitude_pitch * 30) / 90
- },
- realHeight () {
- return 0
- // 无人机高度
- const maxHeight = 240
- // 真空高度
- const vacuumHeight = 120
- // 机场高度
- const dockHeightConfig = {
- 'e3dea0f5-37f2-4d79-ae58-490af3228069': 14.7,
- '4a574db8-4ad3-48f7-9f16-3edbcd8056e1': 54,
- // 'f47ac10b-58cc-4372-a567-0e02b2c3d479': 81,
- }
- const workspaceId =
- this.$store.state.drone.selectedWorkSpaceId ||
- window.localStorage.getItem('bs_workspace_id')
- const dockHeight = dockHeightConfig?.[workspaceId] || 1
+ <div class="instrument-content">
+ <div class="left-img">
+ <div class="valueBox">{{props?.pitchAngle || 0}}°</div>
+ <img src="@/assets/images/rightmapidentification.png" alt="" />
+ <div class="triangle" :style="pitchAngleStyle"></div>
+ <div class="nameBox" >俯仰角度</div>
+ </div>
- let proportion = 0
- // 处于真空高度和最大高度之间
- if (this.height < maxHeight && this.height >= vacuumHeight) {
- const height = this.height - vacuumHeight
- const proport = height / maxHeight
- proportion = Math.round(proport * 50 + 50)
- }
- // 小于等于真空高度计算
- if (this.height < vacuumHeight && this.height >= dockHeight) {
- const height = this.height - dockHeight
- const copyVacuumHeight = vacuumHeight - dockHeight
- const proport = height / copyVacuumHeight
- proportion = Math.round(proport * 25 + 25)
- }
- // 小于机场高度计算
- if (this.height < dockHeight && this.height >= 0) {
- const proport = this.height / dockHeight
- proportion = Math.round(proport * 25)
- }
- // 大于最大高度计算
- if (this.height >= maxHeight) {
- proportion = 95
- }
- return proportion + '%'
- // const proportion = Math.ceil((this.height / maxHeight) * 100) || 50;
- },
- },
- methods: {
- getAngle (currentLngLat, targetLngLat) {
- const { longitude: lng_a, latitude: lat_a } = currentLngLat
- const { longitude: lng_b, latitude: lat_b } = targetLngLat
- var a = ((90 - lat_b) * Math.PI) / 180
- var b = ((90 - lat_a) * Math.PI) / 180
- var AOC_BOC = ((lng_b - lng_a) * Math.PI) / 180
- var cosc =
- Math.cos(a) * Math.cos(b) +
- Math.sin(a) * Math.sin(b) * Math.cos(AOC_BOC)
- var sinc = Math.sqrt(1 - cosc * cosc)
- var sinA = (Math.sin(a) * Math.sin(AOC_BOC)) / sinc
- var A = (Math.asin(sinA) * 180) / Math.PI
- var res = 0
- if (lng_b > lng_a && lat_b > lat_a) res = A
- else if (lng_b > lng_a && lat_b < lat_a) res = 180 - A
- else if (lng_b < lng_a && lat_b < lat_a) res = 180 - A
- else if (lng_b < lng_a && lat_b > lat_a) res = 360 + A
- else if (lng_b > lng_a && lat_b == lat_a) res = 90
- else if (lng_b < lng_a && lat_b == lat_a) res = 270
- else if (lng_b == lng_a && lat_b > lat_a) res = 0
- else if (lng_b == lng_a && lat_b < lat_a) res = 180
- return res
- },
- },
-}
+ <div class="instrument-center">
+ <div class="compass-box" >
+ <div
+ v-for="(item, index) in str"
+ :key="index"
+ class="scale"
+ :style="{ '--rotate': 30 * index - prevRotate + 'deg' }"
+ >
+ <span class="text">{{ item }}</span>
+ </div>
+ </div>
+ <div class="center-show">
+ <img src="@/assets/images/mapidentification.png" alt="" />
+ </div>
+ </div>
+
+ <div class="right-img">
+ <div class="valueBox">{{props.trueAltitude}}m</div>
+ <img src="@/assets/images/leftmapidentification.png" alt="" />
+ <div class="rightTriangle" :style="trueAltitudeStyle"></div>
+ <div class="nameBox">真空高度</div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+const str = ['W', 30, 33, 'N', 3, 6, 'E', 12, 15, 'S', 21, 24]
+const attitude_pitch = 0 // 俯仰角度数
+const height = 0 // 真空高度
+const prevRotate = 0
+const dockHeight = 0
+
+const props = defineProps(['pitchAngle','trueAltitude'])
+const pitchAngleStyle = computed(() => {
+ const pitchAngle = props?.pitchAngle || 0;
+ // 将 [-90, 90] 映射到 [0%, 100%]
+ const percentage = ((pitchAngle + 90) / 180 * 100).toFixed(2);
+ return {
+ bottom: `${percentage}%`
+ };
+})
+
+const trueAltitudeStyle = computed(() => {
+ const trueAltitude = props?.trueAltitude || 0;
+ // 将 [-240,240] 映射到 [0%, 100%]
+ const percentage = ((trueAltitude + 240) / 480 * 100).toFixed(2);
+ return {
+ bottom: `${percentage}%`
+ };
+})
+
+
</script>
+
<style lang="scss" scoped>
.instrument-content {
- height: 100%;
- width: 100%;
- display: flex;
- justify-content: space-between;
- align-items: center;
- position: relative;
- transform: translateY(20px);
+ height: 100%;
+ width: 100%;
+ display: flex;
+ gap: 0 30px;
+ justify-content: center;
+ align-items: center;
+ position: relative;
- .left-img,
- .right-img {
- position: relative;
- height: 100%;
- display: flex;
- align-items: center;
- width: 40px;
+ .left-img,
+ .right-img {
+ position: relative;
+ width: 9px;
+ height: 127px;
- img {
- height: 100px;
- }
- }
+ img {
+ width: 100%;
+ height: 100%;
+ }
- .left-img {
- .text {
- position: absolute;
- left: 0px;
- top: 50px;
- width: 60px;
- text-align: center;
- }
+ .triangle {
+ width: 0px;
+ height: 0px;
+ margin: auto;
+ border: 6px solid transparent;
+ border-left-color: #0fff7b;
+ position: absolute;
+ left: -10px;
- .scaleImg {
- position: relative;
- width: 100%;
- height: 100px;
+ transform: translateY(50%);
+ }
- img {
- margin-left: 25px;
- }
+ .rightTriangle {
+ width: 0px;
+ height: 0px;
+ margin: auto;
+ border: 6px solid transparent;
+ border-left-color: #0fff7b;
+ position: absolute;
+ right: -10px;
+ transform: translateY(50%) rotate(180deg);
+ }
- .scale {
- width: 10px;
- height: 10px;
- background-color: #1fa3f6;
- position: absolute;
- margin-left: 10px;
+ .valueBox {
+ position: absolute;
+ top: -20px;
+ left: 50%;
+ transform: translateX(-50%);
+ }
- &::before {
- content: '';
- position: absolute;
- width: 0;
- height: 0;
- top: 0px;
- left: 10px;
- border-top: solid 5px transparent;
- border-left: solid 5px #1fa3f6;
- border-bottom: solid 5px transparent;
- }
- }
- }
+ .nameBox {
+ position: absolute;
+ bottom: -20px;
+ left: 50%;
+ width: auto;
+ transform: translateX(-50%);
+ font-family: Segoe UI, Segoe UI;
+ font-weight: 400;
+ font-size: 12px;
+ white-space: nowrap;
+ color: #ffffff;
+ }
+ }
- &::before {
- content: attr(data-text);
- position: absolute;
- right: 0px;
- top: 50px;
- font-size: 14px;
- font-weight: bolder;
- font-family: none;
- }
+ .instrument-center {
+ position: relative;
- &::after {
- content: '俯仰角度';
- position: absolute;
- right: 0px;
- text-align: right;
- bottom: 30px;
- font-size: 14px;
- font-weight: bolder;
- font-family: none;
- }
- }
+ .compass-box {
+ width: 180px;
+ height: 180px;
+ border-radius: 50%;
+ position: relative;
+ border: 30px solid rgba($color: #323931, $alpha: 0.5);
+ box-shadow: 0 2px 12px 0 #158aff;
+ user-select: none;
+ transform: none !important;
- .right-img {
- &::before {
- content: attr(data-text);
- position: absolute;
- left: 0px;
- top: 50px;
- font-size: 14px;
- font-weight: bolder;
- font-family: none;
- }
+ .scale {
+ width: 135%;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ font-weight: bold;
+ color: #c1c3c4;
+ text-align: left;
+ transform: translate(-50%, -50%) rotate(var(--rotate));
- &::after {
- content: '真空高度';
- position: absolute;
- left: 0px;
- bottom: 30px;
- font-size: 14px;
- font-weight: bolder;
- font-family: none;
- }
+ &:nth-child(3n - 2) {
+ color: #fff;
+ font-weight: bolder;
+ font-size: 20px;
+ }
+ }
- .ident-arrow {
- position: relative;
+ .scale {
+ .text {
+ display: inline-block;
+ // rotate: -90deg;
+ transform: rotate(-90deg);
+ -ms-transform: rotate(-90deg);
+ -moz-transform: rotate(-90deg);
+ -webkit-transform: rotate(-90deg);
+ -o-transform: rotate(-90deg);
+ }
+ }
+ }
- .arrow-box {
- position: absolute;
- bottom: 0;
- left: 20px;
+ .rotat-btn {
+ width: 16px;
+ height: 16px;
+ background-color: rgba($color: #1fa3f6, $alpha: 1);
+ position: absolute;
+ top: 10px;
+ left: 50%;
+ transform: translateX(-50%);
- .arrow {
- position: relative;
- width: 10px;
- height: 10px;
- background-color: #1fa3f6;
+ &::before {
+ content: '';
+ display: block;
+ width: 0;
+ height: 0;
+ border-left: 8px solid transparent;
+ border-right: 8px solid transparent;
+ border-bottom: 8px solid #1fa3f6;
+ position: absolute;
+ bottom: 100%;
+ }
+ }
- &::before {
- content: '';
- position: absolute;
- left: -9px;
- top: 0;
+ .center-show {
+ width: 30px;
+ height: 40px;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
- border: 5px solid transparent {
- right: solid 5px #1fa3f6;
- }
- }
- }
- }
- }
- }
+ img {
+ width: 100%;
+ height: 100%;
+ transform: rotate(var(--rotate));
+ transition: all 0.5s linear;
+ }
+ }
- .instrument-center {
- position: relative;
+ &::after {
+ content: '';
+ position: absolute;
+ width: 40px;
+ top: 50%;
+ left: 0;
+ z-index: 99;
+ }
- .compass-box {
- width: 180px;
- height: 180px;
- border-radius: 50%;
- position: relative;
- border: 30px solid rgba($color: #323931, $alpha: 0.5);
- box-shadow: 0 2px 12px 0 #158aff;
- user-select: none;
- transform: none !important;
-
- .scale {
- width: 135%;
- position: absolute;
- top: 50%;
- left: 50%;
- font-weight: bold;
- color: #c1c3c4;
- text-align: left;
- transform: translate(-50%, -50%) rotate(var(--rotate));
-
- &:nth-child(3n - 2) {
- color: #fff;
- font-weight: bolder;
- font-size: 20px;
- }
- }
-
- .scale {
- .text {
- display: inline-block;
- // rotate: -90deg;
- transform: rotate(-90deg);
- -ms-transform: rotate(-90deg);
- -moz-transform: rotate(-90deg);
- -webkit-transform: rotate(-90deg);
- -o-transform: rotate(-90deg);
- }
- }
-
- &::before {
- content: '';
- position: absolute;
- top: -45px;
- left: 50%;
- transform: translateX(-50%);
- width: 7px;
- height: 15px;
- background: #00ee8b;
- margin: 0 auto;
- box-shadow: 0 0 4px rgba(0, 0, 0, 0.5), -1px -1px 0 rgba(0, 0, 0, 0.5),
- 1px -1px 0 rgba(0, 0, 0, 0.5), -1px 1px 0 rgba(0, 0, 0, 0.5),
- 1px 1px 0 rgba(0, 0, 0, 0.5);
- }
-
- &::after {
- content: attr(data-text);
- position: absolute;
- top: -60px;
- font-size: 16px;
- line-height: 16px;
- font-weight: 600;
- color: #00ee8b;
- left: 50%;
- transform: translateX(-50%);
- }
- }
-
- .rotat-btn {
- width: 16px;
- height: 16px;
- background-color: rgba($color: #1fa3f6, $alpha: 1);
- position: absolute;
- top: 10px;
- left: 50%;
- transform: translateX(-50%);
-
- &::before {
- content: '';
- display: block;
- width: 0;
- height: 0;
- border-left: 8px solid transparent;
- border-right: 8px solid transparent;
- border-bottom: 8px solid #1fa3f6;
- position: absolute;
- bottom: 100%;
- }
- }
-
- .center-show {
- width: 30px;
- height: 40px;
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
-
- img {
- width: 100%;
- height: 100%;
- transform: rotate(var(--rotate));
- transition: all 0.5s linear;
- }
- }
-
- &::after {
- content: '';
- position: absolute;
- width: 40px;
- top: 50%;
- left: 0;
- z-index: 99;
- }
-
- &::before {
- content: '';
- position: absolute;
- width: 40px;
- top: 50%;
- right: 0;
- z-index: 99;
- }
- }
+ &::before {
+ content: '';
+ position: absolute;
+ width: 40px;
+ top: 50%;
+ right: 0;
+ z-index: 99;
+ }
+ }
}
</style>
diff --git a/src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue b/src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue
index 901d30d..f56ff8c 100644
--- a/src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue
+++ b/src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue
@@ -68,7 +68,7 @@
</div>
<!-- 指南针-->
<div class="compass">
- <ControlComPass />
+ <ControlComPass :pitchAngle="pitchAngle.angle" :trueAltitude="trueAltitude" />
</div>
<div class="ptzControlBox">
@@ -119,7 +119,7 @@
<div v-for="arr in list4" class="info">
<div v-for="item in arr" class="infoItem">
<div class="infoName">{{ item.name }}</div>
- <div class="infoValue">{{ item.value }}</div>
+ <div class="infoValue">{{ item.value + (item.unit || '') }}</div>
</div>
</div>
</div>
@@ -154,6 +154,8 @@
const taskDetails = inject('taskDetails')
const dockSn = inject('dockSn')
const droneSn = inject('droneSn')
+
+const trueAltitude = inject('trueAltitude')
const store = useStore()
let mqttState = null
@@ -190,8 +192,8 @@
return [
[
{ name: '焦距倍数', value: '0' },
- { name: '俯仰角度', value: pitchAngle.value.angle },
- { name: '横向角度', value: yawAngle.value.angle },
+ { name: '俯仰角度', value: pitchAngle.value.angle,unit:'°' },
+ { name: '横向角度', value: yawAngle.value.angle,unit:'°' },
],
[
{ name: '储存', value: '64.5G' },
@@ -201,7 +203,7 @@
]
})
const pitchAngle = computed(() => {
- const { longitude, latitude, height, payloads } = host?.value || {}
+ const { payloads } = host?.value || {}
const gimbal_pitch = payloads?.[0]?.gimbal_pitch || 0
let direction = ''
if (gimbal_pitch > -2 && gimbal_pitch < 2) {
@@ -216,7 +218,7 @@
direction = '正下'
}
return {
- angle: _.round(gimbal_pitch || 0, 1) + '°',
+ angle: _.round(gimbal_pitch || 0, 1),
direction,
}
})
diff --git a/src/components/CurrentTaskDetails/CurrentTaskDetails.vue b/src/components/CurrentTaskDetails/CurrentTaskDetails.vue
index cc4fe6f..3034518 100644
--- a/src/components/CurrentTaskDetails/CurrentTaskDetails.vue
+++ b/src/components/CurrentTaskDetails/CurrentTaskDetails.vue
@@ -22,6 +22,7 @@
</template>
<!-- 控制面板,里面有方法需要立即执行,不可用v-if -->
+<!-- <ControlPanel />-->
<ControlPanel v-show="!isAutoControl" />
<img alt="" :src="amplifyImg" class="amplify" @click="isMaxMap = !isMaxMap" />
</div>
@@ -67,6 +68,9 @@
provide('dockSn', dockSn)
provide('droneSn', droneSn)
+const trueAltitude = ref('')
+provide('trueAltitude', trueAltitude)
+
const isAiLive = ref(false)
provide('isAiLive', isAiLive)
@@ -86,9 +90,13 @@
currentLiveUrl.value = res.data.data.rtcs_url
}
-const getAiLiveUrl = ()=>{
- const res = getLiveAiLinkApi({ original_stream_url: `${CURRENT_CONFIG.rtmpURL}${video_id.value}`, video_id:video_id.value })
+const getAiLiveUrl = async ()=>{
+ const res =await getLiveAiLinkApi({
+ original_stream_url: `${CURRENT_CONFIG.rtmpURL}${video_id.value.replace(/\//g, '-')}`,
+ video_id:video_id.value
+ })
currentLiveUrl.value = res.data.data.rtcs_url
+ ElMessage.success('开启成功')
isAiLive.value = true
}
diff --git a/src/components/CurrentTaskDetails/TaskDetailsHead.vue b/src/components/CurrentTaskDetails/TaskDetailsHead.vue
index 19f7ca0..d417c4b 100644
--- a/src/components/CurrentTaskDetails/TaskDetailsHead.vue
+++ b/src/components/CurrentTaskDetails/TaskDetailsHead.vue
@@ -1,6 +1,6 @@
<template>
<div class="detailsHead">
- <div class="droneName" :title="taskDetails.name">{{ taskDetails.name }}</div>
+ <div class="droneName" :title="taskDetails.device_names">{{ taskDetails.device_names }}</div>
<div class="infoListBox">
<div v-for="item in infoList">
<div class="infoValue" :title="item.value">{{ item.value }}{{ item.unit }}</div>
@@ -61,7 +61,7 @@
}
}
-function refreshLive(){
+function refreshLive() {
EventBus.emit('CurrentTaskDetails-getDroneLiveUrl')
}
@@ -72,6 +72,9 @@
})
}
+const trueAltitude = inject('trueAltitude')
+
+// 获取真实高度
function getRealTimeReallyHigh() {
if (!taskDetailsViewer?.value) return
const device_osd_host = wsInfo?.value?.device_osd?.data?.host || {}
@@ -79,7 +82,8 @@
if (!latitude) return
getLnglatAltitude(longitude, latitude, taskDetailsViewer.value).then(res => {
const last = height - res?.height
- infoList.value[0].value = last ? infoList.value[0].value : _.round(height - res?.height, 1)
+ infoList.value[0].value = last ? _.round(height - res?.height, 1) : infoList.value[0].value
+ trueAltitude.value = infoList.value[0].value
})
}
@@ -187,7 +191,6 @@
color: #ffffff;
line-height: 18px;
margin-bottom: 10px;
-
}
.infoTitle {
diff --git a/src/components/CurrentTaskDetails/TaskDetailsRight.vue b/src/components/CurrentTaskDetails/TaskDetailsRight.vue
index 61b2189..1e7776f 100644
--- a/src/components/CurrentTaskDetails/TaskDetailsRight.vue
+++ b/src/components/CurrentTaskDetails/TaskDetailsRight.vue
@@ -1,6 +1,6 @@
<template>
<div class="task-details-right-container">
- <div class="titleImg">
+ <div class="titleImg">
<img :src="droneImg" alt="" />
</div>
@@ -11,7 +11,7 @@
</div>
</div>
- <BaseControl v-if="taskDetails.workspace_id"/>
+ <BaseControl v-if="taskDetails.workspace_id" />
</div>
</template>
<script setup>
@@ -19,12 +19,6 @@
import BaseControl from '@/components/CurrentTaskDetails/ControlPanel/BaseControl.vue'
const taskDetails = inject('taskDetails')
-
-watch(taskDetails, () => {
- list.value.forEach(item => {
- item.value = taskDetails?.value?.[item.field] || ''
- })
-})
const list = ref([
{ name: '任务编号', value: '', field: 'job_info_num' },
@@ -37,6 +31,21 @@
{ name: '关联算法', value: '', field: 'ai_type_str' },
{ name: '任务描述', value: '', field: 'remark' },
])
+
+watch(
+ taskDetails,
+ () => {
+ list.value.forEach(item => {
+ item.value = taskDetails?.value?.[item.field] || ''
+ })
+ },
+ {
+ immediate: true,
+ deep: true,
+ }
+)
+
+
</script>
<style scoped lang="scss">
@@ -54,7 +63,7 @@
gap: 25px 0;
align-items: center;
- .manualControl{
+ .manualControl {
background: transparent !important;
}
@@ -68,7 +77,8 @@
display: flex;
align-items: center;
justify-content: center;
- img{
+
+ img {
width: 100px;
height: 68px;
}
@@ -86,11 +96,12 @@
display: flex;
font-family: Segoe UI, Segoe UI;
font-size: 14px;
+
.itemName {
flex-shrink: 0;
font-weight: 400;
- color: #D3D3D3;
+ color: #d3d3d3;
}
.itemValue {
diff --git a/src/hooks/controlDrone/useManualControl.js b/src/hooks/controlDrone/useManualControl.js
index c21c5c6..62085ce 100644
--- a/src/hooks/controlDrone/useManualControl.js
+++ b/src/hooks/controlDrone/useManualControl.js
@@ -184,24 +184,28 @@
case 'ArrowUp':
if (activeCodeKey === keyCode) return
clearInterval(myInterval)
+ ptzControl('up')
myInterval = setInterval(()=>ptzControl('up'), 200)
activeCodeKey = keyCode
break
case 'ArrowDown':
if (activeCodeKey === keyCode) return
clearInterval(myInterval)
+ ptzControl('down')
myInterval = setInterval(()=>ptzControl('down'), 200)
activeCodeKey = keyCode
break
case 'ArrowLeft':
if (activeCodeKey === keyCode) return
clearInterval(myInterval)
+ ptzControl('left')
myInterval = setInterval(()=>ptzControl('left'), 200)
activeCodeKey = keyCode
break
case 'ArrowRight':
if (activeCodeKey === keyCode) return
clearInterval(myInterval)
+ ptzControl('right')
myInterval = setInterval(()=>ptzControl('right'), 200)
activeCodeKey = keyCode
break
--
Gitblit v1.9.3