Skip to content

Commit cc05a1b

Browse files
committed
ui: add camera move feature
1 parent 3ead95f commit cc05a1b

File tree

7 files changed

+225
-12
lines changed

7 files changed

+225
-12
lines changed

ui/src/components/Video.vue

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ onBeforeRouteLeave(() => {
3737
defineExpose({ init, pause, mute, play })
3838
3939
async function init(src, pos, live) {
40+
media.value = {}
4041
src = src || media.value.src
4142
pos = pos || media.value.pos
4243
live = live || media.value.live

ui/src/components/icons/IconDown.vue

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<template>
2+
<svg
3+
t="1738401849216"
4+
class="icon"
5+
viewBox="0 0 1024 1024"
6+
version="1.1"
7+
xmlns="http://www.w3.org/2000/svg"
8+
p-id="1465"
9+
width="512"
10+
height="512"
11+
>
12+
<path
13+
d="M904 332c0-8.189-3.124-16.379-9.372-22.628-12.497-12.496-32.759-12.496-45.256 0L512 646.745 174.628 309.372c-12.497-12.496-32.758-12.496-45.255 0-12.497 12.498-12.497 32.758 0 45.256l360 360c12.497 12.496 32.758 12.496 45.255 0l360-360C900.876 348.379 904 340.189 904 332z"
14+
fill=""
15+
p-id="1466"
16+
></path>
17+
</svg>
18+
</template>

ui/src/components/icons/IconLeft.vue

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<template>
2+
<svg
3+
t="1738401751522"
4+
class="icon"
5+
viewBox="0 0 1024 1024"
6+
version="1.1"
7+
xmlns="http://www.w3.org/2000/svg"
8+
p-id="1957"
9+
width="512"
10+
height="512"
11+
>
12+
<path
13+
d="M704 828.8L387.84 512 704 195.2a32 32 0 1 0-45.44-45.44L320 489.6a32 32 0 0 0 0 45.44l339.2 339.2a32 32 0 0 0 44.8-45.44z"
14+
fill=""
15+
p-id="1958"
16+
></path>
17+
</svg>
18+
</template>

ui/src/components/icons/IconRight.vue

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<template>
2+
<svg
3+
t="1738401751522"
4+
class="icon"
5+
viewBox="0 0 1024 1024"
6+
version="1.1"
7+
xmlns="http://www.w3.org/2000/svg"
8+
p-id="1957"
9+
width="512"
10+
height="512"
11+
>
12+
<path
13+
d="M320 828.8L636.16 512 320 195.2a32 32 0 1 1 45.44-45.44L704 489.6a32 32 0 0 1 0 45.44l-339.2 339.2a32 32 0 0 1-44.8-45.44z"
14+
fill=""
15+
p-id="1958"
16+
></path>
17+
</svg>
18+
</template>

ui/src/components/icons/IconTop.vue

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<template>
2+
<svg
3+
t="1738401819334"
4+
class="icon"
5+
viewBox="0 0 1024 1024"
6+
version="1.1"
7+
xmlns="http://www.w3.org/2000/svg"
8+
p-id="1467"
9+
width="512"
10+
height="512"
11+
>
12+
<path
13+
d="M904 692c0 8.189-3.124 16.379-9.372 22.628-12.497 12.496-32.759 12.496-45.256 0L512 377.255 174.628 714.628c-12.497 12.496-32.758 12.496-45.255 0-12.497-12.498-12.497-32.758 0-45.256l360-360c12.497-12.496 32.758-12.496 45.255 0l360 360C900.876 675.621 904 683.811 904 692z"
14+
fill=""
15+
p-id="1468"
16+
></path>
17+
</svg>
18+
</template>

ui/src/i18n.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ export default {
2424
},
2525
settings: {
2626
camerasHideAdd: 'Hide the add button on the cameras page',
27-
go_version: "GoVersion",
28-
version: "ProgramVersion",
29-
build_info: "BuildInfo"
30-
}
27+
go_version: 'GoVersion',
28+
version: 'ProgramVersion',
29+
build_info: 'BuildInfo',
30+
},
3131
},
3232
zh: {
3333
signin: {
@@ -51,9 +51,9 @@ export default {
5151
},
5252
settings: {
5353
camerasHideAdd: '隐藏添加摄像机按钮',
54-
go_version: "Go版本",
55-
version: "程序版本",
56-
build_info: "构建信息"
57-
}
54+
go_version: 'Go版本',
55+
version: '程序版本',
56+
build_info: '构建信息',
57+
},
5858
},
5959
}

ui/src/views/Camera.vue

+144-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import Calendar from 'vue3-slot-calendar'
44
import Video from '@/components/Video.vue'
55
import { useRoute } from 'vue-router'
66
import { onMounted, onUnmounted, ref } from 'vue'
7+
import IconLeft from '@/components/icons/IconLeft.vue'
8+
import IconTop from '@/components/icons/IconTop.vue'
9+
import IconRight from '@/components/icons/IconRight.vue'
10+
import IconDown from '@/components/icons/IconDown.vue'
711
812
const route = useRoute()
913
const filterDay = ref(new Date())
@@ -13,6 +17,9 @@ const currentTime = ref(0)
1317
const camera = ref({})
1418
const video = ref()
1519
const progress = ref()
20+
const live = ref(true)
21+
const toLiveTab = ref()
22+
const toSeekTab = ref()
1623
1724
onMounted(() => {
1825
loadCamera()
@@ -22,7 +29,7 @@ onMounted(() => {
2229
})
2330
2431
onUnmounted(() => {
25-
window.removeEventListener('keydown', shortcutKey)
32+
window.removeEventListener('key ref="moveLeft" down', shortcutKey)
2633
})
2734
2835
const prepareVideo = () => {
@@ -52,7 +59,10 @@ async function requestVideo() {
5259
selectDate.setHours(0, 0, 0, 0)
5360
let pos = Math.floor(selectDate.getTime() / 1000) + Number(currentTime.value)
5461
let src = `${http.apiServer}/media/${route.params.id}/live.ts`
55-
video.value.init(src, pos)
62+
if (live.value) {
63+
pos = null
64+
}
65+
video.value.init(src, pos, live.value)
5666
}
5767
5868
function formatTime(time) {
@@ -64,7 +74,7 @@ function formatTime(time) {
6474
6575
function updateProgress(e) {
6676
let newTime = Number(lastPlayOffet.value) + Math.floor(e.currentTime)
67-
if (camera.value.disableTimeUpdate) {
77+
if (camera.value && camera.value.disableTimeUpdate) {
6878
return
6979
}
7080
currentTime.value = newTime
@@ -115,6 +125,19 @@ function shortcutKey(e) {
115125
return
116126
}
117127
}
128+
129+
const moveCamera = async (x, y) => {
130+
let session = JSON.parse(window.localStorage.getItem('session'))
131+
let r = await http.post(`/v1/api/cameras/${route.params.id}/move`, {
132+
session: session,
133+
body: {
134+
x: x,
135+
y: y,
136+
},
137+
})
138+
console.log(r)
139+
}
140+
118141
const showprogress = e => {
119142
console.log(e, progress.value.getBoundingClientRect().width)
120143
camera.value.progress = formatTime(
@@ -127,6 +150,25 @@ const disableArrowKeys = event => {
127150
event.preventDefault()
128151
}
129152
}
153+
154+
const toLive = e => {
155+
if (live.value) {
156+
return
157+
}
158+
toLiveTab.value.classList.add('selected')
159+
toSeekTab.value.classList.remove('selected')
160+
live.value = true
161+
requestVideo()
162+
}
163+
const toSeek = e => {
164+
if (!live.value) {
165+
return
166+
}
167+
toLiveTab.value.classList.remove('selected')
168+
toSeekTab.value.classList.add('selected')
169+
live.value = false
170+
requestVideo()
171+
}
130172
</script>
131173

132174
<template>
@@ -135,7 +177,7 @@ const disableArrowKeys = event => {
135177
<div class="video">
136178
<Video ref="video" @timeupdate="updateProgress"></Video>
137179
</div>
138-
<div class="timeline">
180+
<div class="timeline" v-if="!live">
139181
<input
140182
ref="progress"
141183
class="progress"
@@ -171,7 +213,46 @@ const disableArrowKeys = event => {
171213
{{ camera.meta ? camera.meta.model : 'loading' }}
172214
</div>
173215
</div>
216+
<div class="change-mode">
217+
<div class="tab tab1 selected" @click="toLive" ref="toLiveTab">
218+
Live
219+
</div>
220+
<div class="tab tab2" @click="toSeek" ref="toSeekTab">Seek</div>
221+
</div>
222+
<div v-if="live" class="ptz">
223+
<div class="btns">
224+
<div
225+
class="btn"
226+
@click="moveCamera(-0.1, 0)"
227+
style="left: 0; top: 60px"
228+
>
229+
<IconLeft />
230+
</div>
231+
<div
232+
class="btn"
233+
@click="moveCamera(0, 0.2)"
234+
style="left: 60px; top: 0px"
235+
>
236+
<IconTop />
237+
</div>
238+
<div
239+
class="btn"
240+
@click="moveCamera(0.1, 0)"
241+
style="right: 0; top: 60px"
242+
>
243+
<IconRight />
244+
</div>
245+
<div
246+
class="btn"
247+
@click="moveCamera(0, -0.2)"
248+
style="right: 60px; bottom: 0px"
249+
>
250+
<IconDown />
251+
</div>
252+
</div>
253+
</div>
174254
<Calendar
255+
v-else
175256
v-model="filterDay"
176257
:has-input="false"
177258
@day-click="selectDay"
@@ -229,6 +310,62 @@ const disableArrowKeys = event => {
229310
font-size: 13px;
230311
color: darkslategray;
231312
}
313+
.change-mode {
314+
display: flex;
315+
justify-content: center;
316+
align-items: center;
317+
height: 32px;
318+
line-height: 32px;
319+
margin-bottom: 10px;
320+
}
321+
.change-mode .tab {
322+
height: 32px;
323+
line-height: 32px;
324+
cursor: pointer;
325+
flex: 1;
326+
text-align: center;
327+
background-color: #d1d9e0;
328+
user-select: none;
329+
}
330+
.change-mode .tab1 {
331+
border-radius: 5px 0 0 5px;
332+
}
333+
.change-mode .tab2 {
334+
border-radius: 0 5px 5px 0;
335+
}
336+
.change-mode .selected {
337+
background-color: #083f39;
338+
color: #fff;
339+
}
340+
.ptz {
341+
width: 247px;
342+
display: flex;
343+
justify-content: center;
344+
}
345+
.ptz .btns {
346+
display: block;
347+
margin-top: 20px;
348+
width: 180px;
349+
height: 180px;
350+
position: absolute;
351+
}
352+
.ptz .btns .btn {
353+
display: flex;
354+
width: 60px;
355+
height: 60px;
356+
position: absolute;
357+
cursor: pointer;
358+
justify-content: center;
359+
align-items: center;
360+
user-select: none;
361+
}
362+
.ptz .btns .btn:hover {
363+
background-color: #d1d9e0;
364+
}
365+
.ptz .btns .btn svg {
366+
width: 32px;
367+
height: 32px;
368+
}
232369
@media screen and (max-width: 1024px) {
233370
.cameraContainer {
234371
padding: 0px 0 0 0;
@@ -245,6 +382,9 @@ const disableArrowKeys = event => {
245382
.operation {
246383
margin-right: 10px;
247384
}
385+
.ptz {
386+
margin: 0 auto;
387+
}
248388
}
249389
</style>
250390

0 commit comments

Comments
 (0)