Vue 3 + ezuikit-js 7.7.7 集成萤石云:3步实现监控、云台控制与全屏
2026/7/12 16:44:50 网站建设 项目流程

Vue 3 集成萤石云监控:从零实现播放、云台控制与全屏功能

在智能安防和远程监控需求日益增长的今天,前端开发者经常需要将视频监控功能集成到Web应用中。本文将详细介绍如何使用Vue 3和官方ezuikit-js SDK(7.7.7版本)快速实现萤石云摄像头的核心功能集成。

1. 环境准备与SDK集成

1.1 创建Vue 3项目

使用Vite快速初始化一个Vue 3项目:

npm create vite@latest vue3-ezviz-demo --template vue cd vue3-ezviz-demo npm install

1.2 安装ezuikit-js

特别注意版本兼容性问题,7.7.7版本经过验证稳定性最佳:

npm install ezuikit-js@7.7.7

1.3 获取萤石云开发凭证

在集成前,需要准备以下关键信息:

  • 设备序列号:摄像头的唯一标识
  • 访问令牌(accessToken):通过萤石云开放平台API获取
  • 视频地址:通常格式为ezopen://open.ys7.com/[设备序列号]/[通道号].hd.live

提示:accessToken有效期通常为7天,生产环境需要实现自动刷新机制

2. 基础播放器实现

2.1 组件初始化

创建EzvizPlayer.vue组件,实现基础播放功能:

<template> <div class="ezviz-container"> <div id="ezviz-player" ref="playerContainer"></div> </template> <script> import { ref, onMounted, onBeforeUnmount } from 'vue' import EZUIKit from 'ezuikit-js' export default { props: { deviceSerial: { type: String, required: true }, accessToken: { type: String, required: true }, channelNo: { type: Number, default: 1 } }, setup(props) { const player = ref(null) const playerContainer = ref(null) const initPlayer = () => { player.value = new EZUIKit.EZUIKitPlayer({ id: 'ezviz-player', accessToken: props.accessToken, url: `ezopen://open.ys7.com/${props.deviceSerial}/${props.channelNo}.hd.live`, autoplay: true, width: '100%', height: 500, template: 'pcLive', // PC端优化模板 header: ['capture', 'save', 'zoom'], // 显示截图、保存、缩放按钮 footer: ['talk', 'broadcast', 'hd', 'fullScreen'] // 显示对讲、语音播报、清晰度切换、全屏按钮 }) } onMounted(() => { initPlayer() }) onBeforeUnmount(() => { if (player.value) { player.value.destroy() } }) return { playerContainer } } } </script> <style scoped> .ezviz-container { position: relative; width: 100%; margin-bottom: 20px; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } </style>

2.2 播放器参数详解

关键配置参数说明:

参数类型说明推荐值
idString播放器容器ID必须与DOM元素ID一致
accessTokenString接口调用凭证通过API获取
urlString视频流地址格式:ezopen://open.ys7.com/设备号/通道号.hd.live
widthNumber/String播放器宽度数字或'100%'
heightNumber/String播放器高度根据布局需要
autoplayBoolean自动播放true
templateString界面模板'pcLive'或'mobileLive'

3. 云台控制功能实现

3.1 云台控制API封装

创建ptzControl.js工具类:

import axios from 'axios' const PTZ_API = { START: 'https://open.ys7.com/api/lapp/device/ptz/start', STOP: 'https://open.ys7.com/api/lapp/device/ptz/stop' } export const ptzControl = { directions: { UP: 0, DOWN: 1, LEFT: 2, RIGHT: 3, UP_LEFT: 4, UP_RIGHT: 6, DOWN_LEFT: 5, DOWN_RIGHT: 7 }, /** * 开始云台控制 * @param {Object} params * @param {String} params.accessToken * @param {String} params.deviceSerial * @param {Number} params.channelNo * @param {Number} params.direction * @param {Number} [params.speed=2] 1-8 */ async start(params) { try { const res = await axios.post(PTZ_API.START, null, { params: { accessToken: params.accessToken, deviceSerial: params.deviceSerial, channelNo: params.channelNo || 1, direction: params.direction, speed: params.speed || 2 } }) return res.data } catch (error) { console.error('PTZ start error:', error) throw error } }, /** * 停止云台控制 * @param {Object} params * @param {String} params.accessToken * @param {String} params.deviceSerial * @param {Number} params.channelNo */ async stop(params) { try { const res = await axios.post(PTZ_API.STOP, null, { params: { accessToken: params.accessToken, deviceSerial: params.deviceSerial, channelNo: params.channelNo || 1 } }) return res.data } catch (error) { console.error('PTZ stop error:', error) throw error } } }

3.2 云台控制UI实现

在播放器组件中添加控制按钮:

<template> <div class="control-panel"> <div class="direction-control"> <button @click="controlPTZ('UP_LEFT')" class="control-btn"> <i class="icon-arrow-up-left"></i> </button> <button @click="controlPTZ('UP')" class="control-btn"> <i class="icon-arrow-up"></i> </button> <button @click="controlPTZ('UP_RIGHT')" class="control-btn"> <i class="icon-arrow-up-right"></i> </button> <button @click="controlPTZ('LEFT')" class="control-btn"> <i class="icon-arrow-left"></i> </button> <button @click="stopPTZ" class="control-btn stop-btn"> <i class="icon-stop"></i> </button> <button @click="controlPTZ('RIGHT')" class="control-btn"> <i class="icon-arrow-right"></i> </button> <button @click="controlPTZ('DOWN_LEFT')" class="control-btn"> <i class="icon-arrow-down-left"></i> </button> <button @click="controlPTZ('DOWN')" class="control-btn"> <i class="icon-arrow-down"></i> </button> <button @click="controlPTZ('DOWN_RIGHT')" class="control-btn"> <i class="icon-arrow-down-right"></i> </button> </div> <div class="speed-control"> <label>云台速度:</label> <input type="range" min="1" max="8" v-model="ptzSpeed" @change="updateSpeed" > <span>{{ ptzSpeed }}</span> </div> </div> </template> <script> import { ref } from 'vue' import { ptzControl } from '../utils/ptzControl' export default { props: { deviceSerial: String, accessToken: String, channelNo: Number }, setup(props) { const ptzSpeed = ref(3) const controlPTZ = async (direction) => { try { await ptzControl.start({ accessToken: props.accessToken, deviceSerial: props.deviceSerial, channelNo: props.channelNo, direction: ptzControl.directions[direction], speed: ptzSpeed.value }) } catch (error) { console.error('PTZ control failed:', error) } } const stopPTZ = async () => { try { await ptzControl.stop({ accessToken: props.accessToken, deviceSerial: props.deviceSerial, channelNo: props.channelNo }) } catch (error) { console.error('PTZ stop failed:', error) } } const updateSpeed = () => { console.log('PTZ speed updated to:', ptzSpeed.value) } return { ptzSpeed, controlPTZ, stopPTZ, updateSpeed } } } </script> <style scoped> .control-panel { margin-top: 15px; padding: 15px; background: #f5f7fa; border-radius: 8px; } .direction-control { display: grid; grid-template-columns: repeat(3, 60px); grid-template-rows: repeat(3, 60px); gap: 5px; justify-content: center; } .control-btn { width: 100%; height: 100%; border: 1px solid #dcdfe6; border-radius: 4px; background: white; cursor: pointer; transition: all 0.3s; } .control-btn:hover { background: #f2f6fc; border-color: #c0c4cc; } .stop-btn { grid-column: 2; grid-row: 2; background: #f56c6c; color: white; } .speed-control { margin-top: 15px; text-align: center; } .speed-control input { width: 200px; margin: 0 10px; } </style>

4. 高级功能实现

4.1 全屏控制优化

虽然SDK自带全屏按钮,但我们可以通过浏览器API实现更灵活的控制:

const handleFullscreen = () => { const playerEl = document.getElementById('ezviz-player') if (!document.fullscreenElement) { playerEl.requestFullscreen().catch(err => { console.error(`全屏错误: ${err.message}`) }) } else { document.exitFullscreen() } } // 监听全屏变化 onMounted(() => { document.addEventListener('fullscreenchange', () => { console.log('全屏状态变化:', !!document.fullscreenElement) }) })

4.2 音频控制

实现音频的开启/关闭功能:

const audioEnabled = ref(false) const toggleAudio = () => { if (!player.value) return if (audioEnabled.value) { player.value.closeSound() } else { player.value.openSound() } audioEnabled.value = !audioEnabled.value }

4.3 异常处理与重连机制

增强播放器的稳定性:

const initPlayerWithRetry = async (retryCount = 3) => { try { initPlayer() // 监听错误事件 player.value.on('error', (error) => { console.error('播放器错误:', error) if (retryCount > 0) { setTimeout(() => { initPlayerWithRetry(retryCount - 1) }, 3000) } }) } catch (error) { console.error('播放器初始化失败:', error) if (retryCount > 0) { setTimeout(() => { initPlayerWithRetry(retryCount - 1) }, 3000) } } }

5. 生产环境优化建议

5.1 Token自动刷新

实现accessToken的自动刷新机制:

let refreshTimer = null const setupTokenRefresh = () => { // 假设token有效期为7天(604800秒),提前1小时刷新 const refreshInterval = 604800 - 3600 refreshTimer = setInterval(async () => { try { const newToken = await fetchNewToken() player.value.updateToken(newToken) props.accessToken = newToken } catch (error) { console.error('Token刷新失败:', error) } }, refreshInterval * 1000) } onMounted(() => { setupTokenRefresh() }) onBeforeUnmount(() => { if (refreshTimer) { clearInterval(refreshTimer) } })

5.2 性能优化技巧

  • 懒加载播放器:当监控组件不在可视区域时暂停播放
  • 清晰度自适应:根据网络状况动态调整视频清晰度
  • 心跳检测:定期检查视频流状态,异常时自动恢复
// 使用IntersectionObserver实现懒加载 const setupLazyLoad = () => { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { if (player.value && player.value.isPaused()) { player.value.play() } } else { if (player.value && !player.value.isPaused()) { player.value.pause() } } }) }, { threshold: 0.1 }) observer.observe(playerContainer.value) }

5.3 移动端适配

针对移动设备的特殊处理:

const checkMobile = () => { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( navigator.userAgent ) } const initPlayer = () => { const isMobile = checkMobile() player.value = new EZUIKit.EZUIKitPlayer({ // ...其他配置 template: isMobile ? 'mobileLive' : 'pcLive', width: isMobile ? window.innerWidth : '100%', height: isMobile ? window.innerHeight * 0.6 : 500 }) }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询