项目中实现点击监控,实现在线监控画面实时查看,但是video组件无法实现,使用JessibucaMobile组件实现,uniappx项目使用h5页面跳转实现:
javascript
// uniapp项目页面
<template>
<view class="player-box">
<web-view update-title="false" :src="webViewUrl"></web-view>
</view>
</template>
<script>
import { REDIRECT_URL } from '../../api/request.js'
export default {
data () {
return {
accE_URL: REDIRECT_URL,//当前请求地址
videoUrl: '',
webViewUrl: '',
token: ''
}
},
onLoad (options) {
if (options.videoUrl) {
const videoUrl = decodeURIComponent(options.videoUrl)
const token = options.token || uni.getStorageSync('token')
this.videoUrl = videoUrl
this.token = token
// 构建web-view的URL,传递视频地址和token
this.webViewUrl = `${this.accE_URL}player?videoUrl=${encodeURIComponent(
videoUrl
)}`.replace(/\.mp4$/, '.flv') // JessibucaMobile播放只支持flv格式,所以格式需要注意
} else {
uni.showToast({
title: '视频地址无效',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
},
methods: {
backClick () {
uni.switchTab({
url: '/pages/data/index' // 页面返回跳转的uniapp页面
})
},
onWebViewMessage (e) {
console.log('收到web-view消息:', e.detail)
}
}
}
</script>
<style scoped lang="scss">
.player-box {
width: 100%;
height: 100vh;
background-color: #000;
}
</style>
注意: JessibucaMobile只支持flv格式的文件!!!
javascript
// pc端项目------------------------JessibucaMobile.vue
<template>
<div class="jessibuca-content-box">
<div id="container" ref="container">
</div>
</div>
</template>
<script setup>
import { nextTick, onMounted, ref, watch } from 'vue'
import { flexbile } from '../utils/flexbile'
const props = defineProps({
currentUrl: {
type: String
},
currentIndex: {
type: Number
}
})
const emits = defineEmits(['clearData'])
const container = ref(null)
const buttonsBox = ref(null)
const currentId = ref(0)
const jessibuca = ref({})
const playing = ref(false)
const loaded = ref(false)
const showOperateBtns = ref(true)
const err = ref('')
const create = options => {
options = options || {}
jessibuca.value[currentId.value] = new window.Jessibuca(
Object.assign(
{
container: container.value,
isResize: false,
loadingText: '疯狂加载中...',
debug: true,
supportDblclickFullscreen: true,
operateBtns: {
fullscreen: showOperateBtns.value,
play: showOperateBtns.value,
audio: showOperateBtns.value
},
isNotMute: true,
timeout: 10,
useWebFullScreen: true
},
options
)
)
jessibuca.value[currentId.value].on('play', () => {
playing.value = true
loaded.value = true
})
}
const destroy = () => {
if (jessibuca.value[currentId.value]) {
jessibuca.value[currentId.value].destroy()
}
jessibuca.value[currentId.value] = null
emits('clearData', currentId.value)
}
watch(
() => props.currentIndex,
newVal => {
currentId.value = newVal
nextTick(() => {
if (currentId.value > 0) create()
})
},
{ immediate: true, deep: true }
)
watch(
() => props.currentUrl,
newVal => {
console.log('newVal', newVal)
nextTick(() => {
if (jessibuca.value[currentId.value]) {
jessibuca.value[currentId.value].play(newVal)
}
})
},
{ immediate: true, deep: true }
)
defineExpose({
destroy
})
onMounted(() => {
flexbile()
})
</script>
<style scoped lang="less">
.jessibuca-content-box {
width: 100%;
height: 100%;
}
#container {
background: rgba(13, 14, 27, 0.7);
width: 100%;
height: 100%;
position: relative;
.destory-box {
position: absolute;
left: 0;
bottom: 0;
color: #fff;
z-index: 9999;
background-color: #000;
height: 1.2rem;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 0.7rem;
box-sizing: border-box;
.iconfont {
font-size: 0.3rem;
margin-bottom: 0.1rem;
}
.box-l,
.box-c,
.box-r {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 0.3rem;
}
}
}
</style>
上述文件引用的文件 until/flexbile.js
javascript
// flexbile.js
const flexbile = () => {
var timer = null
var PAGE_WIDTH = ''
var PAGE_FONT_SIZE = '100' // 设计稿1rem的大小
if (
/phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone/i.test(
navigator.userAgent
)
) {
PAGE_WIDTH = 750 // 设计稿的宽度
} else {
PAGE_WIDTH = 1920
}
function onResize () {
const screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
var e = (PAGE_FONT_SIZE * screenWidth) / PAGE_WIDTH
console.log(e)
document.documentElement.style.fontSize = e + 'px'
}
window.addEventListener('resize', function () {
if (timer) clearTimeout(timer)
timer = setTimeout(onResize, 100)
})
onResize()
}
export { flexbile }
javascript
// h5视频播放页面
<template>
<div class="fullscreen-player">
<div class="video-container" :class="{ 'landscape-mode': isLandscape }">
<!-- 视频播放器 -->
<JessibucaMobile
v-if="videoUrl"
:currentIndex="1"
:currentUrl="videoUrl"
@clearData="clearData"
></JessibucaMobile>
<!-- 加载状态 -->
<div v-if="isLoading" class="loading-indicator">
<div class="spinner"></div>
<span>视频加载中...</span>
</div>
<!-- 错误提示 -->
<div v-if="hasError" class="error-message">
<p>视频加载失败</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import JessibucaMobile from '../../components/JessibucaMobile.vue'
const route = useRoute()
const videoPlayer = ref(null)
const videoUrl = ref('')
const isFullscreen = ref(false)
const isPlaying = ref(false)
const isLoading = ref(true)
const hasError = ref(false)
const showControls = ref(true)
const isLandscape = ref(true) // 默认横屏模式
// 隐藏控制栏的定时器
let controlsTimeout = null
// 初始化视频
const initVideo = () => {
if (route.query.videoUrl) {
videoUrl.value = decodeURIComponent(route.query.videoUrl)
console.log('视频地址:', videoUrl.value)
isLoading.value = false
} else {
hasError.value = true
isLoading.value = false
}
}
const clearData = () => {
videoUrl.value = ''
}
// 视频元数据加载完成
const onVideoLoaded = () => {
isLoading.value = false
// 尝试自动播放(静音模式下)
playVideo()
// 强制横屏显示
forceLandscape()
}
// 强制横屏显示
const forceLandscape = () => {
isLandscape.value = true
// 尝试进入全屏模式
setTimeout(() => {
enterFullscreen()
}, 500)
}
// 进入全屏
const enterFullscreen = () => {
const video = videoPlayer.value
if (!video) return
if (video.requestFullscreen) {
video.requestFullscreen()
} else if (video.webkitRequestFullscreen) {
video.webkitRequestFullscreen()
} else if (video.mozRequestFullScreen) {
video.mozRequestFullScreen()
} else if (video.msRequestFullscreen) {
video.msRequestFullscreen()
}
isFullscreen.value = true
}
// 退出全屏
const exitFullscreen = () => {
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen()
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen()
} else if (document.msExitFullscreen) {
document.msExitFullscreen()
}
isFullscreen.value = false
}
// 切换全屏
const toggleFullscreen = () => {
if (isFullscreen.value) {
exitFullscreen()
} else {
enterFullscreen()
}
resetControlsTimer()
}
// 播放视频
const playVideo = () => {
const video = videoPlayer.value
if (video) {
video
.play()
.then(() => {
isPlaying.value = true
})
.catch(error => {
console.error('播放失败:', error)
// 如果自动播放失败,显示控制栏让用户手动播放
showControls.value = true
})
}
}
// 重试加载
const retry = () => {
hasError.value = false
isLoading.value = true
const video = videoPlayer.value
if (video) {
video.load()
}
}
// 返回上一页
const goBack = () => {
if (window.history.length > 1) {
window.history.back()
} else {
window.close()
}
}
// 重置控制栏隐藏计时器
const resetControlsTimer = () => {
if (controlsTimeout) {
clearTimeout(controlsTimeout)
}
showControls.value = true
controlsTimeout = setTimeout(() => {
showControls.value = false
}, 3000)
}
// 处理全屏变化事件
const handleFullscreenChange = () => {
isFullscreen.value = !!(
document.fullscreenElement ||
document.webkitFullscreenElement ||
document.mozFullScreenElement ||
document.msFullscreenElement
)
}
// 点击视频区域显示/隐藏控制栏
const toggleControls = () => {
showControls.value = !showControls.value
if (showControls.value) {
resetControlsTimer()
}
}
onMounted(() => {
initVideo()
// 添加全屏变化监听
document.addEventListener('fullscreenchange', handleFullscreenChange)
document.addEventListener('webkitfullscreenchange', handleFullscreenChange)
document.addEventListener('mozfullscreenchange', handleFullscreenChange)
document.addEventListener('MSFullscreenChange', handleFullscreenChange)
// 初始隐藏控制栏
controlsTimeout = setTimeout(() => {
showControls.value = false
}, 3000)
})
onUnmounted(() => {
// 清理事件监听和定时器
document.removeEventListener('fullscreenchange', handleFullscreenChange)
document.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
document.removeEventListener('mozfullscreenchange', handleFullscreenChange)
document.removeEventListener('MSFullscreenChange', handleFullscreenChange)
if (controlsTimeout) {
clearTimeout(controlsTimeout)
}
// 退出全屏
if (isFullscreen.value) {
exitFullscreen()
}
})
</script>
<style lang="less" scoped>
.fullscreen-player {
width: 100vw;
height: 100vh;
background: #000;
overflow: hidden;
position: fixed;
top: 0;
left: 0;
z-index: 9999;
}
.video-container {
width: 100%;
height: 100%;
position: relative;
display: flex;
align-items: center;
justify-content: center;
&.landscape-mode {
// 强制横屏样式
transform: rotate(0deg);
}
}
.video-element {
width: 100%;
height: 100%;
object-fit: contain;
background: #000;
}
.control-bar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.7);
padding: 10px 20px;
border-radius: 20px;
display: flex;
gap: 15px;
align-items: center;
transition: opacity 0.3s ease;
z-index: 1000;
}
.control-btn {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
padding: 8px 16px;
border-radius: 15px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
&:hover {
background: rgba(255, 255, 255, 0.3);
}
}
.back-btn {
background: rgba(255, 0, 0, 0.6);
}
.loading-indicator {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
text-align: center;
z-index: 1000;
span {
font-size: 32px;
}
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.error-message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
text-align: center;
z-index: 1000;
p {
font-size: 32px;
}
}
.retry-btn {
background: #007aff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 15px;
cursor: pointer;
margin-top: 10px;
}
// 横屏优化样式
@media (orientation: landscape) {
.video-container.landscape-mode {
.video-element {
object-fit: cover;
}
}
}
// 竖屏时的横屏强制样式
@media (orientation: portrait) {
.video-container.landscape-mode {
transform: rotate(90deg);
width: 100vh;
height: 100vw;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(90deg);
}
}
</style>
这样,在uniapp端实时查看在线监控视频,横屏展示就实现了。