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

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([
{