行业资讯
📅 2026/7/12 16:45:06
Vue 3 + ezuikit-js 7.7.7 集成萤石云:3步实现监控、云台控制与全屏
Vue 3 集成萤石云监控从零实现播放、云台控制与全屏功能在智能安防和远程监控需求日益增长的今天前端开发者经常需要将视频监控功能集成到Web应用中。本文将详细介绍如何使用Vue 3和官方ezuikit-js SDK7.7.7版本快速实现萤石云摄像头的核心功能集成。1. 环境准备与SDK集成1.1 创建Vue 3项目使用Vite快速初始化一个Vue 3项目npm create vitelatest vue3-ezviz-demo --template vue cd vue3-ezviz-demo npm install1.2 安装ezuikit-js特别注意版本兼容性问题7.7.7版本经过验证稳定性最佳npm install ezuikit-js7.7.71.3 获取萤石云开发凭证在集成前需要准备以下关键信息设备序列号摄像头的唯一标识访问令牌(accessToken)通过萤石云开放平台API获取视频地址通常格式为ezopen://open.ys7.com/[设备序列号]/[通道号].hd.live提示accessToken有效期通常为7天生产环境需要实现自动刷新机制2. 基础播放器实现2.1 组件初始化创建EzvizPlayer.vue组件实现基础播放功能template div classezviz-container div idezviz-player refplayerContainer/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); } /style2.2 播放器参数详解关键配置参数说明参数类型说明推荐值idString播放器容器ID必须与DOM元素ID一致accessTokenString接口调用凭证通过API获取urlString视频流地址格式ezopen://open.ys7.com/设备号/通道号.hd.livewidthNumber/String播放器宽度数字或100%heightNumber/String播放器高度根据布局需要autoplayBoolean自动播放truetemplateString界面模板pcLive或mobileLive3. 云台控制功能实现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.speed2] 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 classcontrol-panel div classdirection-control button clickcontrolPTZ(UP_LEFT) classcontrol-btn i classicon-arrow-up-left/i /button button clickcontrolPTZ(UP) classcontrol-btn i classicon-arrow-up/i /button button clickcontrolPTZ(UP_RIGHT) classcontrol-btn i classicon-arrow-up-right/i /button button clickcontrolPTZ(LEFT) classcontrol-btn i classicon-arrow-left/i /button button clickstopPTZ classcontrol-btn stop-btn i classicon-stop/i /button button clickcontrolPTZ(RIGHT) classcontrol-btn i classicon-arrow-right/i /button button clickcontrolPTZ(DOWN_LEFT) classcontrol-btn i classicon-arrow-down-left/i /button button clickcontrolPTZ(DOWN) classcontrol-btn i classicon-arrow-down/i /button button clickcontrolPTZ(DOWN_RIGHT) classcontrol-btn i classicon-arrow-down-right/i /button /div div classspeed-control label云台速度/label input typerange min1 max8 v-modelptzSpeed changeupdateSpeed 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; } /style4. 高级功能实现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 }) }