load video without click

This commit is contained in:
2023-03-29 14:38:13 +08:00
parent afb4a42b34
commit 8c9afda249
11 changed files with 117 additions and 545 deletions

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,14 +1,19 @@
<template>
<!-- <List>123132</List> -->
<CameraList />
<!-- <CameraItem-->
<!-- name="测试球机视频"-->
<!-- source="https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv"-->
<!-- />-->
</template>
<script>
import CameraList from "./components/CameraList.vue";
// import CameraItem from "@/components/CameraItem.vue";
// import List from "./infinite-list-vue.umd.cjs";
export default {
name: "App",
components: {
// CameraItem,
CameraList,
},
};
@ -17,7 +22,6 @@ export default {
<style>
#app {
background-color: #f6f6f6;
//width: 100vw;
//height: 100vh;
//width: 100vw; //height: 100vh;
}
</style>

View File

@ -3,10 +3,12 @@
<div class="camera-name">
{{ props.name }}
</div>
<video ref="previewVideoRef" class="camera-player"></video>
<video
ref="videoElementRef"
:class="`camera-player ${isFullscreen ? 'fullscreen-video' : ''}`"
></video>
<div class="overlay" @click="openPlayModal">
<!-- <img src="../assets/stLine-play-l@3x.png" alt="" width="40" height="40" /> -->
<span class="iconfont icon-bofang_o" style="font-size: 60px"></span>
<!-- <span class="iconfont icon-bofang_o" style="font-size: 60px"></span>-->
</div>
<div v-if="showModal" class="modal-overlay" @click="closePlayModal">
<span
@ -15,16 +17,14 @@
@click.stop="exitFullscreen"
></span>
<div ref="rotateElementRef" :class="`modal`">
<video
ref="videoElementRef"
:class="`camera-player ${isFullscreen ? 'fullscreen-video' : ''}`"
></video>
<div
:class="`video-overlay ${isFullscreen ? 'fullscreen-video' : ''}`"
@click.stop="switchPlayStatus"
>
<div v-if="isFullscreen" class="arrow-control">
<div
v-if="isFullscreen && props.name.includes('球机')"
class="arrow-control"
>
<div
:class="`up iconfont icon-up ${pressed.up ? 'pressed' : ''}`"
@touchend="moveCamera('stop')"
@ -67,108 +67,83 @@
</div>
</template>
<script name="camera" setup>
import { nextTick, onUnmounted, reactive, ref, toRefs } from "vue";
import { nextTick, onMounted, onUnmounted, reactive, ref, toRefs } from "vue";
import flvjs from "flv.js";
import axios from "axios";
// import { useRoute } from "vue-router";
// eslint-disable-next-line no-undef
const props = defineProps({
name: {
type: String,
},
source: {
type: String,
default:
"https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
default: null,
},
});
const { source } = toRefs(props);
// const route = useRoute();
// document.title = route.query.proname;
const showModal = ref(false);
const isFullscreen = ref(false);
const rotateElementRef = ref();
const videoElementRef = ref();
let flvPlayer;
// const previewVideoRef = ref();
// onMounted(() => {
// const flvPlayer = flvjs.createPlayer({
// type: "flv",
// url: props.source,
// isLive: true,
// });
// flvPlayer.attachMediaElement(previewVideoRef.value);
// flvPlayer.load();
// console.log();
// // loadVideo(props.source);
// });
onMounted(() => {
flvPlayer = flvjs.createPlayer({
type: "flv",
url: props.source,
});
if (flvjs.isSupported()) {
flvPlayer.attachMediaElement(videoElementRef.value);
flvPlayer.load();
flvPlayer.on("ERROR", flvPlayerEventHandler);
}
});
const openPlayModal = () => {
showModal.value = true;
nextTick(() => {
flvPlayer = flvjs.createPlayer({
type: "flv",
url: props.source,
});
if (videoElementRef.value) {
rotateElementRef.value.appendChild(videoElementRef.value);
}
if (flvjs.isSupported()) {
flvPlayer.attachMediaElement(videoElementRef.value);
// flvPlayer.attachMediaElement();
flvPlayer.load();
flvPlayer.on("ERROR", flvPlayerEventHandler);
flvPlayer.play();
}
});
};
// file使 input[file]
const loadVideo = async (url) => {
const resp = await axios.get(url);
const file = new File([resp.data], "monitor.flv");
console.log(file);
return new Promise(function (resolve, reject) {
const videoElem = document.createElement("video");
const dataUrl = URL.createObjectURL(file);
//
videoElem.onloadeddata = function () {
resolve(videoElem);
};
videoElem.onerror = function (e) {
console.log(e);
reject("video 后台加载失败");
};
// auto
videoElem.setAttribute("preload", "auto");
videoElem.src = dataUrl;
const closePlayModal = () => {
// flvPlayer.on("ERROR", flvPlayerEventHandler);
// flvPlayer.pause();
// flvPlayer.unload();
// flvPlayer.detachMediaElement();
// flvPlayer.destroy();
// flvPlayer = null;
showModal.value = false;
nextTick(() => {
document.querySelector(".camera-item")?.appendChild(videoElementRef.value);
if (flvjs.isSupported()) {
flvPlayer.pause();
}
});
};
const closePlayModal = () => {
flvPlayer.on("ERROR", flvPlayerEventHandler);
flvPlayer.pause();
flvPlayer.unload();
flvPlayer.detachMediaElement();
flvPlayer.destroy();
flvPlayer = null;
showModal.value = false;
};
const videoElementRef = ref();
// const playBtnRef = ref();
// let flvPlayer = flvjs.createPlayer({
// type: "flv",
// url: "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
// });
const flvPlayerEventHandler = (e) => {
console.log(e);
};
//
const switchPlayStatus = () => {
if (!videoElementRef.value) return;
if (videoElementRef.value.paused) {
console.log(flvPlayer);
flvPlayer.play();
} else {
flvPlayer.pause();
// flvPlayer.pause();
}
};
const isFullscreen = ref(false);
const rotateElementRef = ref();
/* 进入全屏 */
const videoFullscreen = () => {
const scale = screen.availWidth / videoElementRef.value.offsetHeight;
videoElementRef.value.style.setProperty("--scale", scale);
@ -176,6 +151,11 @@ const videoFullscreen = () => {
isFullscreen.value = true;
};
/* 退出全屏 */
const exitFullscreen = () => {
isFullscreen.value = false;
};
const token = ref("");
const loadToken = async () => {
const url = new URL(source.value);
@ -185,17 +165,23 @@ const loadToken = async () => {
return resp.data.URLToken;
};
/*方向按钮是否按下*/
const pressed = reactive({
left: false,
right: false,
up: false,
down: false,
});
/**
* 控制摄像头转动
* @param arrow 上下左右和停止
* @return {Promise<void>}
*/
const moveCamera = async (arrow) => {
if (!token.value) {
token.value = await loadToken();
}
const serial = source.value.split("/flv/hls/")[1].split("_")[0];
const url = new URL(source.value);
await axios.get(
@ -211,9 +197,6 @@ const moveCamera = async (arrow) => {
}
};
const exitFullscreen = () => {
isFullscreen.value = false;
};
onUnmounted(() => {
console.log("destroy player here");
@ -235,7 +218,7 @@ onUnmounted(() => {
@import "../assets/fonts/iconfont.css";
.camera-item {
// height: 300px;
height: 400px;
background-color: white;
border-radius: 10px;
margin-bottom: 20px;
@ -282,8 +265,7 @@ onUnmounted(() => {
left: 0;
top: 0;
z-index: 999;
// position: relative;
background-color: rgba(153, 153, 153, 0.363);
backdrop-filter: blur(12px);
.quit-fullscreen {
position: fixed;
@ -312,14 +294,9 @@ onUnmounted(() => {
width: 100%;
height: 100%;
position: absolute;
//background-color: #eee;
z-index: 999999;
left: 0;
top: 0;
// display: flex;
// align-items: center;
// justify-content: center;
// z-index: 99;
// position: relative;
.fullscreen {
position: absolute;
z-index: 99;
@ -354,6 +331,7 @@ onUnmounted(() => {
right: 25px;
bottom: 25px;
z-index: 9999;
backdrop-filter: blur(12px);
.iconfont {
width: 32px;

View File

@ -5,31 +5,18 @@
<NSelect
v-model:value="queryParams.query"
:options="projectList"
placeholder="请选择项目"
label-field="PRONAME"
placeholder="请选择项目"
value-field="PROID"
@update:value="projectClicked"
/>
</NConfigProvider>
</div>
<!-- <select
name="pets"
v-model="queryParams.query"
id="proj-select"
@change="projectClicked"
>
<option value="">请选择项目</option>
<option v-for="item in projectList" :value="item.PROID">
{{ item.PRONAME }}
</option>
</select> -->
<div v-if="queryParams.query" class="naive-tabs">
<NTabs type="segment">
<NTabPane name="camera" tab="摄像头列表">
<!-- v-if="queryParams.query && activeIndex == 0" -->
<div class="camera-list" ref="cameraListRef">
<div ref="cameraListRef" class="camera-list">
<camera
v-for="(item, index) in cameraList"
:key="index"
@ -37,7 +24,7 @@
:source="item.flvUrl"
></camera>
<div v-if="!cameraList.length" class="empty">
<img src="../assets/empty.png" alt="" />
<img alt="" src="../assets/empty.png" />
摄像头列表为空
</div>
</div>
@ -45,46 +32,31 @@
<NTabPane name="image" tab="图片列表">
<!-- v-else-if="queryParams.query && activeIndex == 1" -->
<div class="image-list">
<div class="image-item" v-for="item in imageList" :key="item.PIC">
<div v-for="item in imageList" :key="item.PIC" class="image-item">
<!-- <img :src="`${protocol}//${host}/portal/r/${item}`" alt="" /> -->
<!-- <img :src="`http://81.68.90.198:8088/portal/r/${item}`" alt="" /> -->
<!-- TODO:test -->
<!-- :src="`${protocol}//${host}/portal/r/${item.PIC}`" -->
<NImage
class="n-image-item"
:src="`http://81.68.90.198:8088/portal/r/${item.PIC}`"
:previewed-img-props="{
draggable: true,
style: { border: 'none' },
}"
:src="`http://81.68.90.198:8088/portal/r/${item.PIC}`"
class="n-image-item"
></NImage>
<div class="uplode-time">{{ item.CREATEDATE }}</div>
</div>
<div v-if="!imageList.length" class="empty">
<img src="../assets/empty.png" alt="" />
<img alt="" src="../assets/empty.png" />
图片列表为空
</div>
</div>
</NTabPane>
</NTabs>
</div>
<!-- <div class="switch" v-if="queryParams.query">
<div
:class="`switch-item ${activeIndex == 0 ? 'active' : ''}`"
@click="activeIndex = 0"
>
摄像头列表
</div>
<div
:class="`switch-item ${activeIndex == 1 ? 'active' : ''}`"
@click="activeIndex = 1"
>
图片列表
</div>
</div> -->
<div v-else class="empty">
<img src="../assets/empty.png" alt="" />
<img alt="" src="../assets/empty.png" />
请选择项目
</div>
</div>
@ -92,16 +64,17 @@
<script setup>
import { reactive, ref } from "vue";
import camera from "./camera.vue";
import axios from "axios";
import {
NSelect,
NConfigProvider,
zhCN,
NTabs,
NTabPane,
NImage,
NSelect,
NTabPane,
NTabs,
zhCN,
} from "naive-ui";
import Camera from "./CameraItem.vue";
const cameraLoading = ref(true);
const projectList = ref([
// {
@ -120,18 +93,18 @@ const queryParams = reactive({
cmd: "com.awspaas.user.apps.cmp_camera_list",
pageNum: 1,
pageSize: 10,
// sid: sid,
sid: sid,
// TODO:test
sid: "b9e99f78-2f6e-4d1c-aea9-4364f4191d30",
// sid: "b9e99f78-2f6e-4d1c-aea9-4364f4191d30",
query: undefined,
});
// const activeIndex = ref(0);
const loadProjectList = async () => {
// const resp = await axios.get(`http://localhost:3000/project-list`);
// TODO:test
const resp = await axios.get(
"/api/portal/r/jd?cmd=com.awspaas.user.apps.cmp_screen_getProjectList&sid=b9e99f78-2f6e-4d1c-aea9-4364f4191d30"
);
// const resp = await axios.get(
// "/api/portal/r/jd?cmd=com.awspaas.user.apps.cmp_screen_getProjectList&sid=b9e99f78-2f6e-4d1c-aea9-4364f4191d30"
// );
// const resp = {
// data: [{
// "SUPERVISORID": "",
@ -180,9 +153,9 @@ const loadProjectList = async () => {
// "ID": "a0fb6df5-48e4-442f-8b9d-da5861c1bf41"
// }]
// }
// const resp = await axios.get(
// "./jd?cmd=com.awspaas.user.apps.cmp_screen_getProjectList&sid=" + sid
// );
const resp = await axios.get(
"./jd?cmd=com.awspaas.user.apps.cmp_screen_getProjectList&sid=" + sid
);
return resp.data;
};
@ -198,9 +171,9 @@ const projectClicked = () => {
const loadCameraList = async () => {
cameraLoading.value = true;
// TODO:test
const resp = await axios(`/api/portal/r/jd`, {
params: queryParams,
});
// const resp = await axios(`/api/portal/r/jd`, {
// params: queryParams,
// });
// const resp = {
// data: {
// "code": 0,
@ -232,19 +205,12 @@ const loadCameraList = async () => {
// "total": 4
// }
// }
const resp = await axios.get("./jd", {
params: queryParams,
});
cameraList.value = resp.data.rows;
total.value = resp.data.total;
cameraLoading.value = false;
// if (total.value == 0) {
// const rep = await axios.get(
// `./jd?cmd=com.awspaas.user.apps.cmp_photo_list&sid=${sid}&proId=${queryParams.query}`
// ); // 摄像头列表为空时,获取图片列表
// const rep = await axios.get(
// `/api/portal/r/jd?cmd=com.awspaas.user.apps.cmp_photo_list&sid=${sid}&proId=${queryParams.query}`
// ); // 摄像头列表为空时,获取图片列表
// imageList.value = rep.data;
// }
};
const loadImageList = async () => {
@ -253,7 +219,7 @@ const loadImageList = async () => {
// ); // 摄像头列表为空时,获取图片列表
// TODO:test
const resp = await axios.get(
`/api/portal/r/jd?cmd=com.awspaas.user.apps.cmp_photo_list&sid=b9e99f78-2f6e-4d1c-aea9-4364f4191d30&proId=${queryParams.query}`
`./jd?cmd=com.awspaas.user.apps.cmp_photo_list&sid=${sid}&proId=${queryParams.query}`
); // 摄像头列表为空时,获取图片列表
imageList.value = resp.data;
};
@ -286,31 +252,29 @@ loadProjectList().then((data) => {
projectList.value = data;
if (projectList.value.length) return projectList.value[0].PROID;
});
// .then((proid) => {
// queryParams.query = proid;
// queryParams.pageNum = 1;
// loadCameraList();
// });
</script>
<style>
/* body {
background-color: #f6f6f6;
} */
</style>
<style scoped lang="scss">
<style lang="scss" scoped>
.app-container {
height: 100vh;
display: flex;
flex-direction: column;
overflow: scroll;
}
.naive-select-wrap {
width: 750px;
padding: 20px 20px 0px;
}
.naive-tabs {
padding: 20px 20px 0;
}
.nav-bar {
width: 750px;
height: 88px;
@ -322,6 +286,7 @@ loadProjectList().then((data) => {
font-size: 32px;
position: fixed;
}
.switch {
width: 686px;
height: 76px;
@ -332,6 +297,7 @@ loadProjectList().then((data) => {
justify-content: space-between;
align-items: center;
padding: 0 8px;
.switch-item {
width: 331px;
height: 60px;
@ -339,20 +305,24 @@ loadProjectList().then((data) => {
display: flex;
justify-content: center;
align-items: center;
&.active {
background-color: #fff;
}
}
}
.camera-list {
// margin-top: 108px;
// padding: 0px 20px 0px;
}
.image-list {
// padding: 0px 20px 0px;
.image-item {
margin-bottom: 20px;
position: relative;
.uplode-time {
color: indianred;
position: absolute;
@ -364,6 +334,7 @@ loadProjectList().then((data) => {
border-radius: 999px;
padding: 0px 10px;
}
// border-radius: 10px;
// overflow: hidden;
:deep(.n-image-item) {
@ -375,6 +346,7 @@ loadProjectList().then((data) => {
border-radius: 10px;
}
}
// img {
// object-fit: cover;
// border-radius: 10px;
@ -392,6 +364,7 @@ loadProjectList().then((data) => {
margin: 2vw 2vw 5vw 2vw;
outline: none;
}
.empty {
// flex: 1;
flex-grow: 1;
@ -399,9 +372,11 @@ loadProjectList().then((data) => {
flex-direction: column;
align-items: center;
justify-content: center;
img {
width: 80%;
}
// height:80vh;
}
</style>

View File

@ -13,7 +13,7 @@
<script setup>
import { onMounted, ref } from "vue";
import camera from "./camera.vue";
import camera from "./CameraItem.vue";
const cameraList = ref([
{

View File

@ -13,6 +13,6 @@ module.exports = defineConfig({
},
},
},
publicPath: "./",
assetsDir: "../apps/com.awspaas.user.apps.cmp/monitor",
// publicPath: "./",
// assetsDir: "../apps/com.awspaas.user.apps.cmp/monitor",
});