vue3 videojs实现播放器,动态更改src

一、背景

vue3下载第三方插件videojs,达到播放器的效果,并且点击事件能够动态更改播放器的src。实现思路:

  1. 场景一:只有一个播放器,当点击事件,直接赋值,动态更改封装好的组件的src参数,能够实现切换播放器的效果。
  2. 场景二:不止有一个窗口,而是多窗口,要选中多个窗口中的一个窗口,然后点击,达到动态切换选中播放器的src。

二、实现总结

1.vue框架更改对应数据data,对应的视图view变化。

2.在封装的组件监听传参src的变化,如果点击的 src和之前的不同,进行切换资源,播放器重新加载播放;

3.播放器的资源source的参数有src+type,注意type可以不设置。

推荐用法:不设置type,如果要设置一定要更传过来的视频流的数据格式保持一致,否则会爆红

三、效果展示

四、完整代码 

(一)、封装组件 

 组件封装,videojs封装成一个组件:

完整代码:

<script setup lang="ts">
import { computed, CSSProperties, onMounted, ref, watch } from 'vue'
import videojs from 'video.js'
import type { VideoJsPlayerOptions } from 'video.js'
import 'video.js/dist/video-js.min.css'
type MyVideoProps = {/** 视频地址 */src: stringwidth?: stringheight?: string
}
const props = withDefaults(defineProps<MyVideoProps>(), {})
// video标签
const videoRef = ref<HTMLElement | null>(null)
// video实例对象
let videoPlayer: videojs.Player | null = null
const videoWrapStyles = computed<CSSProperties>(() => {return {width: props.width || '100%',height: props.height || '100%'}
})
// 初始化videojs
const initVideo = () => {// https://gitcode.gitcode.host/docs-cn/video.js-docs-cn/docs/guides/options.htmlconst options: VideoJsPlayerOptions = {language: 'zh-CN', // 设置语言controls: true, // 是否显示控制条preload: 'auto', // 预加载autoplay: true, // 是否自动播放fluid: false, // 自适应宽高src: props.src, // 要嵌入的视频源的源 URLobjectFit: 'cover', // 同css object-fit,作用于video标签notSupportedMessage: 'Ajiang此视频暂无法播放,请稍后再试' //允许覆盖Video.js无法播放媒体源时显示的默认信息。}if (videoRef.value) {// 创建 video 实例videoPlayer = videojs(videoRef.value, options, onPlayerReady)}
}
// video初始化完成的回调函数
const onPlayerReady = () => { }
onMounted(() => {initVideo()
})
watch(() => props.src, (now) => {if (now) {videoPlayer.pause()// videoPlayer.dispose()videoPlayer.reset()setTimeout(() => {videoPlayer.src([{src: props.src,// type: "application/x-mpegURL"  //type可以不写,一旦写了一定要符合否则报错}])videoPlayer.load()videoPlayer.play()}, 10)}
})
</script>
<template><div :style="videoWrapStyles"><video id="my-player" ref="videoRef" class="video-js w-full h-full"><source :src="src" /></video></div>
</template>
<style lang="less" scoped>
.w-full {width: 100%;
}.h-full {height: 100%;
}
</style>

(二)、局部引入 

 页面引入,引入封装的组件:

完整代码:

<template><div class="home_box"><div class="leftBox"><div class="top_nav"><div v-for="(item, index) in topnav" :key="index" :class="item.class_name" @click="toOtherModel(item.path)">{{ item.name }}</div></div><div class="videoBox"><div :class="data.cruentIndex == 0 ? 'playerBox1' :data.cruentIndex == 1 ? 'playerBox2' :data.cruentIndex == 2 ? 'playerBox3' : ''"><template v-if="data.cruentIndex == 0 && data.videoUrlData"><div class="myVedioBox"><Vediojs :src="data.videoUrlData[0].url" width="100%" height="78vh" /></div></template><template v-if="data.cruentIndex == 1"><div class="myVedioBox" v-for="(item, index) in fourBoxData[1].controlTypy" :id="'player_' + index":key="index" @click="changePlayer(index)"><Vediojs :src="data.videoUrlData[index]?.url" /></div></template><template v-if="data.cruentIndex == 2"><div class="myVedioBox" v-for="(item, index) in fourBoxData[2].controlTypy" :id="'player_' + index":key="index" @click="changePlayer(index)"><Vediojs :src="data.videoUrlData[index]?.url" /></div></template></div><div :class="data.cruentIndex == 3 ? 'playerBox4' : ''" v-show="data.isCloseed == true"><template v-if="data.cruentIndex == 3"><div class="myVedioBox" v-for="(item, index) in fourBoxData[3].controlTypy" :key="index"><Vediojs :src="data.videoUrlData[index].url" width="100%" height="88vh" /><div class="closeBox" @click="handleCloseBtn">关闭</div></div></template></div></div></div><div class="rightBox"><div class="rightTitleBox"><img :src="getAssetsFile('pmsHome/title_icon.png')" alt="一张图" /><span>选择窗口</span></div><div class="fourBox"><div class="fourInnerBox" v-for="(item, index) in fourBoxData" @click="handleImageIcon(index)"><img :src="getAssetsFile(data.cruentIndex == index ? item.activeImgUrl : item.imgUrl)" alt="一张图"></div></div><div class="rightTitleBox"><img :src="getAssetsFile('pmsHome/title_icon.png')" alt="一张图" /><span>监控列表</span></div><div class="rightTreeBox"><div class="treeInnerBox"><el-tree ref="treeRef" node-key="myTreeKey" :data="treeData" :props="treeDefaultProps"@node-click="handleNodeClick" :current-node-key='4' default-expand-all :highlight-current="true" /></div></div></div></div>
</template>
<script setup>
import { nextTick, onMounted, reactive, ref, watch } from "vue";
import { useRouter } from "vue-router";
import { getAssetsFile } from '@/utils'
import Vediojs from "./common/Vediojs.vue";
import 'video.js/dist/video-js.min.css'
const router = useRouter();
const treeRef = ref()
const treeDefaultProps = {children: 'children',label: 'label',id: 'myTreeKey',url: 'url'
}
onMounted(() => {})
const topnav = reactive([{name: "360゜全景影像",class_name: "panoramicImage_title",path: "panoramicImage"},{name: "综合感知",class_name: "comprehensivePerception_title",path: "comprehensivePerception"},
]);
const data = reactive({cruentIndex: 0,videoUrlData: [{id: '1',label: '常规监控',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '2',label: '常规监控2',url: 'http://vjs.zencdn.net/v/oceans.mp4',},{id: '3',label: '常规监控',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '4',label: '常规监控',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '5',label: '常规监控',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '6',label: '常规监控2',url: 'http://vjs.zencdn.net/v/oceans.mp4',},{id: '7',label: '常规监控',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '8',label: '常规监控',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '9',label: '常规监控',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},],isCloseed: false,
})
const fourBoxData = reactive([{imgUrl: 'intelligentNavigation/rightIcon/icon1.png',activeImgUrl: 'intelligentNavigation/rightIcon/icon1_isChecked.png',controlTypy: 1,},{imgUrl: 'intelligentNavigation/rightIcon/icon2.png',activeImgUrl: 'intelligentNavigation/rightIcon/icon2_isChecked.png',controlTypy: 4,},{imgUrl: 'intelligentNavigation/rightIcon/icon3.png',activeImgUrl: 'intelligentNavigation/rightIcon/icon3_isChecked.png',controlTypy: 9,},{imgUrl: 'intelligentNavigation/rightIcon/icon4.png',activeImgUrl: 'intelligentNavigation/rightIcon/icon3_isChecked.png',controlTypy: 1,},
])
const treeData = [{id: '1',label: '常规监控',url: '#',children: [{id: '2',label: '监控器1',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '3',label: '监控器2',url: 'http://vjs.zencdn.net/v/oceans.mp4',},{id: '4',label: '监控器3',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '5',label: '监控器4',url: 'http://vjs.zencdn.net/v/oceans.mp4',},{id: '6',label: '监控器集群',children: [{id: '8',label: '监控器5',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '9',label: '监控器6',url: 'http://vjs.zencdn.net/v/oceans.mp4',},{id: '10',label: '监控器7',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},],},{id: '7',label: '监控器集群二',url: '#',children: [{id: '11',label: '监控器1',url: 'http://vjs.zencdn.net/v/oceans.mp4',},{id: '12',label: '监控器2',url: 'https://media.w3.org/2010/05/sintel/trailer.mp4',},{id: '13',label: '监控器3',url: 'http://vjs.zencdn.net/v/oceans.mp4',},],},],},
]
const changePlayer = (id) => {console.log('ddd>>>', id, typeof id);//2.如何获取到多个播放器中的一个?data.currentPlayerId = id
}const toOtherModel = (path) => {router.push({ name: path });
}const handleNodeClick = (treeNode, node) => {if (node.level !== 1) {defaultHighlight(treeNode.myTreeKey)if (data.cruentIndex !== 0) {data.videoUrlData[data.currentPlayerId].url = treeNode.url} else {data.videoUrlData[0].url = treeNode.url}} else {console.log('高亮在根元素>>>');}
}
const handleImageIcon = (value) => {data.cruentIndex = valueif (value == 3) {data.isCloseed = !data.isCloseed}
}
const defaultHighlight = (param) => {nextTick(() => {treeRef.value.setCurrentKey(param)  //树状图选中效果})
}
const handleCloseBtn = () => {data.isCloseed = !data.isCloseeddata.cruentIndex = 0
}
</script>
<style scoped lang='less'>
.home_box {width: 100%;height: 88vh;display: flex;position: relative;.leftBox {width: 80%;height: 100%;margin-right: 1%;display: flex;flex-direction: column;.top_nav {width: 100%;height: 6%;margin-bottom: 12px;color: rgba(106, 151, 218, 1);font-weight: bold;font-size: calc(100vw * 22 / 1920);display: flex;>div {height: 46px;margin-right: 30px;text-align: center;line-height: 43px;}.panoramicImage_title {width: 235px;background: url("@/assets/images/intelligentNavigation/panoramicImage_title.png");background-size: 100% 100%;background-position: center;}.comprehensivePerception_title {width: 185px;background: url("@/assets/images/intelligentNavigation/comprehensivePerception_title.png");background-size: 100% 100%;background-position: center;}}.videoBox {width: 100%;flex: 1;background-color: #EBF1F6;border: 1px solid #95DEF7;border-radius: 8px;display: flex;justify-content: center;align-items: center;.video-player-wrapper {width: 100%;height: 100%;background-color: pink;display: flex;flex-wrap: wrap;justify-content: space-between;.video-player-item {width: 500px;height: 300px;background-color: skyblue;}}.playerBox1 {width: 98%;height: 96%;.myvideoBox {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;img {width: 1400px;height: 700px;object-fit: cover;}}}.playerBox2 {width: 98%;height: 96%;display: flex;flex-wrap: wrap;.myVedioBox {width: 49%;margin: 0px auto 8px auto;img {width: 100%;height: 100%;object-fit: cover;}}}.playerBox3 {width: 98%;height: 96%;display: flex;flex-wrap: wrap;.myVedioBox {width: 33%;margin: 0px auto 7px auto;img {width: 100%;height: 100%;object-fit: cover;}}}.playerBox4 {width: 100%;height: 100%;position: absolute;top: 0px;left: 0px;z-index: 2;.myVedioBox {.closeBox {width: 40px;height: 40px;line-height: 40px;border: 1px solid #fff;border-radius: 50%;color: #4279CA;font-weight: 600;background-color: #fff;position: absolute;top: 10px;right: 20px;z-index: 3;&:hover {border: 1px solid #4279CA;color: #fff;background-color: #4279CA;cursor: pointer;}}}}}}.rightBox {flex: 1;display: flex;flex-direction: column;.rightTitleBox {width: 100%;height: 46px;background: url('@/assets/images/intelligentNavigation/rightTitle_bg_long.png');background-size: 100% 100%;margin-bottom: 12px;font-size: calc(100vw * 24 / 1920);color: #4279CA;font-weight: bold;display: flex;justify-content: flex-start;align-items: center;img {width: 27px;height: 27px;margin: 8px 11px 8px 18px;}}.fourBox {width: 100%;height: 64px;background-color: #EBF0F5;border: 3px solid #fff;border-radius: 4px;margin-bottom: 16px;display: flex;justify-content: space-around;align-items: center;box-sizing: border-box;.fourInnerBox {width: 40px;height: 40px;display: flex;justify-content: center;align-items: center;img {width: 40px;height: 40px;object-fit: cover;}}}.rightTreeBox {width: 100%;flex: 1;padding-left: 16px;background-color: #EBF1F6;border: 3px solid #fff;border-radius: 4px;box-sizing: border-box;.treeInnerBox {width: 100%;height: 100%;:deep(.el-tree) {background-color: #EBF0F5;}}}}
}
</style>
<style lang='less'>
@import "@/assets/css/reset.less";
</style>

五、核心代码

 封装组件的监听watch:

watch(() => props.src, (now) => {if (now) {videoPlayer.pause()// videoPlayer.dispose()videoPlayer.reset()setTimeout(() => {videoPlayer.src([{src: props.src,// type: "application/x-mpegURL"  //type可以不写,一旦写了一定要符合否则报错}])videoPlayer.load()videoPlayer.play()}, 10)}
})

局部引入的点击事件:

const changePlayer = (id) => {console.log('ddd>>>', id, typeof id);//2.如何获取到多个播放器中的一个?data.currentPlayerId = id
}const handleNodeClick = (treeNode, node) => {if (node.level !== 1) {if (data.cruentIndex !== 0) {data.videoUrlData[data.currentPlayerId].url = treeNode.url   //变data,变视频} else {data.videoUrlData[0].url = treeNode.url}} else {console.log('高亮在根元素>>>');}
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/94472.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线程|线程的使用、四种实现方式

1.线程的实现方式 1.用户级线程 开销小&#xff0c;用户空间就可以创建多个。缺点是&#xff1a;内核无法感知用户级多个线程的存在&#xff0c;把其当作只有一个线程&#xff0c;所以只会提供一个处理器。 2.内核级线程 相对于用户级开销稍微大一点&#xff0c;可以利用多…

无涯教程-Perl - setgrent函数

描述 此功能将枚举设置(或重置)到组条目集的开头。该函数应在第一次调用getgrent之前调用。 语法 以下是此函数的简单语法- setgrent返回值 此函数不返回任何值。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl -wwhile( ($name,$passwd,$gid,$members)getgrent…

c语言每日一练(9)

前言&#xff1a;每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;暑假时三天之内必有一更&#xff0c;到了开学之后&#xff0c;将看学业情…

QT的设计器介绍

设计器介绍 Qt制作 UI 界面&#xff0c;一般可以通过UI制作工具QtDesigner和纯代码编写两种方式来实现。纯代码实现暂时在这里不阐述了在后续布局章节详细说明&#xff0c;QtDesigner已经继承到开发环境中&#xff0c;在工程中直接双击ui文件就可以直接在QtDesigner设计器中打…

unity发布WebGL遇到的坑(持续更新)

1、unity默认字体在网页中不会显示 解决方法&#xff1a;自己新导入一个字体&#xff0c;使用导入的字体 2、之前打过包并运行过&#xff0c;后面又在unity中进行了修改&#xff0c;重新打包&#xff0c;运行发现还是修改之前的效果&#xff0c;虽然是新包&#xff0c; 解决方…

Windows上使用dump文件调试

dump文件 dump文件记录当前程序运行某一时刻的信息&#xff0c;包括内存&#xff0c;线程&#xff0c;线程栈&#xff0c;变量等等&#xff0c;相当于调试程序时运行到某个断点上&#xff0c;把程序运行的信息记录下来。可以通过Windbg打开dump&#xff0c;查看程序运行的变量…

在IDEA中创建properties配置文件

第一步&#xff1a;在 src路径下找到resources文件 第二步&#xff1a;右击选择新建Resource Bundle配置文件 第三步&#xff1a;为Resource Bundle配置文件命名 完成创建

第十课:Qt 字符编码和中文乱码相关问题

功能描述&#xff1a;最全的 Qt 字符编码相关知识以及中文乱码的原因与解决办法 一、字符编码种类 ASCII 码 美国人对信息交流的编码&#xff0c;包括 26 个字母&#xff08;大小写&#xff09;、数字和标点符号等&#xff0c;用一个字节&#xff08;8 位&#xff09;表示这些…

vue-组件库-storybook:理解storybook、实践

一、理解 storybook Storybook是一个开源的工具&#xff0c;可以帮助前端开发者更好地构建、测试和展示组件。 具体来说&#xff0c;Storybook可以做以下几件事情&#xff1a; 1、为每个组件提供一个独立的页面&#xff0c;可以快速展示或调试组件。 2、管理多个组件&#x…

vue利用 sortable 完成表格拖拽

先讲一下vue2&#xff0c;使用sortable完成表格拖拽【不只是表格&#xff0c;div也可以实现&#xff0c;但我项目中是表格拖拽】 github地址 安装 npm install sortablejs --save使用 &#xff08;我的项目中是拖拽一个小按钮移动&#xff0c;而不是整行&#xff09; <te…

VMware虚拟机Ubuntu无法连接网络的解决方法

一、解决办法 网络适配器设置 终端依次执行下面命令即可 sudo nmcli networking off sudo nmcli networking onsudo service network-manager start #或者 sudo service NetworkManager start成功出现这个图标&#xff0c;即代表网络连接成功。

单元测试到底是什么?应该怎么做?

一、什么是单元测试&#xff1f; 单元测试&#xff08;unit testing&#xff09;&#xff0c;是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小或范围&#xff0c;并没有一个明确的标准&#xff0c;“单元”可以是一个函数、方法、类、功能模块或者子系统。 …

滴滴Ceph分布式存储系统优化之锁优化

摘自&#xff1a;https://mp.weixin.qq.com/s/oWujGOLLGItu1Bv5AuO0-A 2020-09-02 21:45 0.引言 Ceph是国际知名的开源分布式存储系统&#xff0c;在工业界和学术界都有着重要的影响。Ceph的架构和算法设计发表在国际系统领域顶级会议OSDI、SOSP、SC等上。Ceph社区得到Red Hat…

指针、数组、sizeof、strlen相关知识与练习题目

目录 前提回顾&#x1f50d;&#xff1a; 关于一维数组&#x1f92e;&#xff1a; 关于二维数组&#x1f600;&#xff1a; sizeof与strlen&#x1f415;&#xff1a; sizeof&#x1f3c0;&#xff1a; strlen&#x1f413;&#xff1a; 相关练习&#x1f4da;&#xff1a…

numpy基础知识

文章目录 安装numpynumpy的ndarray对象ndarray 和 list 效率比较创建一/二维数组ndarray的常用属性调整数组形状ndarray转list numpy的数据类型数组的运算数组和数的计算数组和数组的计算 数组的轴数组的索引和切片数组的与或非和三目运算符numpy的插入、删除、去重插入删除去重…

高并发内存池(threadcache)[1]

高并发内存池 分层处理 thread cache 定义一个公共的FreeList管理切分的小空间 static void*& NextObj(void* obj) {return *(void**)obj; }//管理切分好的小对象的自由链表 class FreeList { public:void Push(void* obj){assert(obj);//头插//*(void**)obj _freeLis…

Linux工具【2】(调试器gdb、项目自动化构建工具make/Makefile)

gdb、make/Makefile 引言调试器gdb介绍常用指令 自动化构建工具make/Makefile介绍使用依赖关系与依赖方法编辑Makefile伪目标 总结 引言 在上一篇文章中介绍了Linux中的编辑器vim与编译器gcc与g&#xff1a; 戳我看vim与gcc详解哦 在本篇文章中将继续来介绍Linux中的工具&…

js ajax 国内快速 映像

ajax 快速 映像 https://www.bootcdn.cn/ axios入门和axios基本请求方式 https://blog.csdn.net/m0_68997646/article/details/127438174 使用 jsDelivr CDN: <script src"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>因为我们国…

【Linux命令详解 | ssh命令】 ssh命令用于远程登录到其他计算机,实现安全的远程管理

文章标题 简介一&#xff0c;参数列表二&#xff0c;使用介绍1. 连接远程服务器2. 使用SSH密钥登录2.1 生成密钥对2.2 将公钥复制到远程服务器 3. 端口转发3.1 本地端口转发3.2 远程端口转发 4. X11转发5. 文件传输与远程命令执行5.1 文件传输5.1.1 从本地向远程传输文件5.1.2 …

vue使用jsplumb 流程图

安装jsPlumb库&#xff1a;在Vue项目中使用npm或yarn安装jsPlumb库。 npm install jsplumb 创建一个Vue组件&#xff1a;创建一个Vue组件来容纳jsPlumb的功能和呈现。 <template><div style"margin: 20px"><div style"margin: 20px">&l…