platypush/platypush/backend/http/webapp/src/components/panels/Camera/Index.vue

286 lines
6.2 KiB
Vue

<template>
<div class="camera">
<div class="camera-container">
<div class="frame-container" ref="frameContainer">
<div class="no-frame" v-if="!streaming && !capturing && !captured">The camera is not active</div>
<img class="frame" :src="url" ref="frame" alt="">
</div>
<div class="controls">
<div class="left">
<button type="button" @click="startStreaming" :disabled="capturing" title="Start video" v-if="!streaming">
<i class="fa fa-play" />
</button>
<button type="button" @click="stopStreaming" :disabled="capturing" title="Stop video" v-else>
<i class="fa fa-stop" />
</button>
<button type="button" @click="capture" :disabled="streaming || capturing" title="Take a picture">
<i class="fas fa-camera" />
</button>
</div>
<div class="right">
<button type="button" @click="$refs.paramsModal.show()" title="Settings">
<i class="fas fa-cog" />
</button>
</div>
</div>
</div>
<div class="url" v-if="url?.length">
<label class="row">
<span class="name">Stream URL</span>
<input name="url" type="text" :value="fullURL" disabled="disabled"/>
</label>
</div>
<Modal ref="paramsModal" title="Camera Parameters">
<div class="params">
<label class="row">
<span class="name">Device</span>
<input name="device" type="text" v-model="attrs.device" @change="onDeviceChanged"/>
</label>
<label class="row">
<span class="name">Width</span>
<input name="width" type="text" v-model="attrs.resolution[0]" @change="onSizeChanged"/>
</label>
<label class="row">
<span class="name">Height</span>
<input name="height" type="text" v-model="attrs.resolution[1]" @change="onSizeChanged"/>
</label>
<label class="row">
<span class="name">Horizontal Flip</span>
<input name="horizontal_flip" type="checkbox" v-model="attrs.horizontal_flip" @change="onFlipChanged"/>
</label>
<label class="row">
<span class="name">Vertical Flip</span>
<input name="vertical_flip" type="checkbox" v-model="attrs.vertical_flip" @change="onFlipChanged"/>
</label>
<label class="row">
<span class="name">Rotate</span>
<input name="rotate" type="text" v-model="attrs.rotate" @change="onSizeChanged"/>
</label>
<label class="row">
<span class="name">Scale-X</span>
<input name="scale_x" type="text" v-model="attrs.scale_x" @change="onSizeChanged"/>
</label>
<label class="row">
<span class="name">Scale-Y</span>
<input name="scale_y" type="text" v-model="attrs.scale_y" @change="onSizeChanged"/>
</label>
<label class="row">
<span class="name">Frames per second</span>
<input name="fps" type="text" v-model="attrs.fps" @change="onFpsChanged"/>
</label>
<label class="row">
<span class="name">Grayscale</span>
<input name="grayscale" type="checkbox" v-model="attrs.grayscale" @change="onGrayscaleChanged"/>
</label>
</div>
</Modal>
</div>
</template>
<script>
import CameraMixin from "@/components/panels/Camera/Mixin";
import Modal from "@/components/Modal";
export default {
name: "Camera",
components: {Modal},
mixins: [CameraMixin],
props: {
cameraPlugin: {
type: String,
required: true,
},
},
computed: {
fullURL() {
return `${window.location.protocol}//${window.location.host}${this.url}`
},
},
methods: {
startStreaming() {
this._startStreaming(this.cameraPlugin)
},
capture() {
this._capture(this.cameraPlugin)
},
},
}
</script>
<style lang="scss">
$camera-background: #101520;
.camera {
width: 100%;
height: 100%;
background: $background-color;
overflow: auto;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 3em;
.camera-container {
display: flex;
flex-direction: column;
align-items: center;
background: $camera-background;
.frame-container {
position: relative;
}
.frame, .no-frame {
position: absolute;
top: 0;
width: 100%;
height: 100%;
}
.frame {
z-index: 1;
}
.no-frame {
display: flex;
color: white;
align-items: center;
justify-content: center;
z-index: 2;
background: black;
}
.controls {
width: 100%;
display: flex;
border-top: 1px solid #202530;
padding: .5em .25em;
.left,.right {
width: 50%;
}
.right {
text-align: right;
}
button {
background: none;
color: white;
border: none;
&:hover {
color: $default-hover-fg-2;
}
}
}
}
.url {
@media screen and (max-width: calc(#{$tablet} - 1px)) {
width: 80%;
}
@media screen and (min-width: $tablet) {
width: 640px;
}
display: flex;
margin: 1em;
.row {
width: 100%;
display: flex;
align-items: center;
}
.name {
width: 140px;
}
input {
width: 500px;
font-weight: normal;
}
}
.params {
@media screen and (min-width: $tablet) {
width: 640px;
}
display: flex;
flex-direction: column;
margin: -2em;
label {
font-weight: normal;
}
.head {
display: flex;
justify-content: center;
label {
width: 100%;
display: flex;
justify-content: right;
.name {
margin-right: 1em;
}
}
}
.row {
width: 100%;
display: flex;
align-items: center;
padding: 0.5em 1em;
.name {
width: 30%;
}
input {
width: 70%;
}
&:nth-child(even) {
background: $default-bg-4;
}
&:hover {
background: $hover-bg;
}
}
}
.modal {
.content {
@media screen and (max-width: calc(#{$tablet} - 1px)) {
width: 90% !important;
}
}
}
}
</style>