cocosCreator视频web模式播放踩坑解决

/*** 对外输出接口*/
export interface VideoPlayerManageInterface {//初始化视频播放器init(list: VideoPlayerManageInitListType[],options?: VideoPlayerManageInitOptionsType): Promise<void>;//播放视频play(url: string, currentTime?: number): Promise<void>;//销毁视频destroyUrl(url: string, currentTag: string): void;//销毁标签的所有视频destroyTag(targetTag: string): void;
}export type VideoPlayerManageInitListType = {//标签,分组tag: string;//视频地址url: string;//视频宽度width: number;//视频高度height: number;//是否循环播放isLoop: boolean;//视频播放结束回调(非循环下生效)endCallback?: () => void;
};export type VideoPlayerManageInitOptionsType = {//视频封面地址poster?: string;//播放按钮地址playerStartButtonUrl?: string;//是否已经点击过播放按钮hasClicked?: boolean;//是否是苹果设备检测函数checkIsAppleFun?: () => boolean;
};/*** 鉴于cocos对于视频播放的支持不够完善,在自带的VideoPlayer组件上进行了封装* 使用VideoPlayerManage进行视频的提前装载,播放,销毁等操作* 解决视频切换时的黑屏问题、视频默认样式修改、ISO自动播放、播放时未就绪报错的问题* web环境使用*/cc.macro.ENABLE_TRANSPARENT_CANVAS = true;export default class VideoPlayerManage implements VideoPlayerManageInterface {private static V: VideoPlayerManageInterface = null;public static get instance(): VideoPlayerManageInterface {if (!this.V) {this.V = new VideoPlayerManage();}return this.V;}private readonly CLASS_NAME = "cocosVideo";private poster = ""; //这里是一个视频封面的图片地址private playerStartButtonUrl = ""; //这里是一个播放按钮的图片地址private hasClicked = false;constructor() {cc.Canvas.instance.node.getComponentInChildren(cc.Camera).backgroundColor = new cc.Color(0, 0, 0, 0);}private map: Map<string,{url: string;videoPlayer: cc.VideoPlayer;tag: string[];}> = new Map();async init(list: VideoPlayerManageInitListType[],options?: VideoPlayerManageInitOptionsType): Promise<void> {Object.keys(options).forEach((key) => {this[key] = options[key];});await Promise.all(list.map((listItem) => this.initVideoPlayerCore(listItem)));await this.initVideos();}async play(url: string, currentTime = 0) {const videoPlayer = this.map.get(url).videoPlayer;this.map.forEach((value) => {value.videoPlayer.node.active = url === value.url;});videoPlayer.currentTime = currentTime;if (videoPlayer.isPlaying()) {videoPlayer.pause();}videoPlayer.node.off("ready-to-play");videoPlayer.node.on("ready-to-play",() => {if (!videoPlayer.isPlaying()) {videoPlayer.play();}},this);await this.waitPlayerClick();if (!videoPlayer.isPlaying()) {videoPlayer.play();} else {videoPlayer.resume();}}destroyUrl(url: string, currentTag: string) {const item = this.map.get(url);if (!item) return;if (item.tag.length > 1) {item.tag = item.tag.filter((tagItem) => {return tagItem !== currentTag;});return;}this.delOneVideo(item);}destroyTag(targetTag: string) {this.map.forEach((item) => {if (item.tag.includes(targetTag)) {item.tag = item.tag.filter((tagItem) => {return tagItem !== targetTag;});if (item.tag.length === 0) {this.delOneVideo(item);}}});}private delOneVideo(item) {item.videoPlayer.node.destroy();const videoDom = this.getVideoDom(item.url);if (videoDom) {videoDom.parentNode.removeChild(videoDom);}this.map.delete(item.url);}private async initVideos() {await this.delayOneFrame();const list = document.getElementsByClassName(this.CLASS_NAME);for (let i = 0; i < list.length; i++) {const video: Element = list[i];video["poster"] = this.poster;video["autoplay"] = true;}}private async initVideoPlayerCore(listItem) {const videoMapItem = this.map.get(listItem.url);if (videoMapItem) {!videoMapItem.tag.includes(listItem.tag) &&videoMapItem.tag.push(listItem.tag);return;}const videoPlayer = await this.createVideoPlayerForUrl(listItem);this.map.set(listItem.url, {url: listItem.url,videoPlayer,tag: [listItem.tag],});}private async createVideoPlayerForUrl(listItem): Promise<cc.VideoPlayer> {const videoNode: cc.Node = new cc.Node();videoNode.active = false;const videoPlayer = videoNode.addComponent(cc.VideoPlayer);videoPlayer.mute = true;videoPlayer.resourceType = cc.VideoPlayer.ResourceType.LOCAL;videoNode.width = listItem.width;videoNode.height = listItem.height;videoPlayer.stayOnBottom = true;cc.Canvas.instance.node.addChild(videoNode);const asset = await this.loadVideo(listItem.url);videoPlayer.clip = asset as unknown as string;this.setLoopAndEndCallBack(videoPlayer,listItem.isLoop,listItem.endCallback);return videoPlayer;}private loadVideo(url: string) {return new Promise((resolve, reject) => {cc.assetManager.loadRemote(url,{ ext: ".mp4" },(err, asset: cc.Asset) => {if (err) {reject(err);} else {resolve(asset);}});});}private loadPng(url: string) {return new Promise((resolve, reject) => {cc.assetManager.loadRemote(url,{ ext: ".png" },(err, asset: cc.Asset) => {if (err) {reject(err);} else {resolve(asset);}});});}private delayOneFrame(): Promise<void> {return new Promise((resole) => {cc.Canvas.instance.scheduleOnce(() => {resole();});});}private setLoopAndEndCallBack(videoPlayer: cc.VideoPlayer,isLoop: boolean,endCallback: () => void) {videoPlayer.node.off("completed");videoPlayer.node.on("completed",() => {if (isLoop) {videoPlayer.currentTime = 0;videoPlayer.play();} else {endCallback && endCallback();}},this);}private async waitPlayerClick(): Promise<void> {return new Promise((resolve) => {if (this.hasClicked || !this.checkIsApple()) {resolve();return;}const node = new cc.Node();node.addComponent(cc.BlockInputEvents);const sprite = node.addComponent(cc.Sprite);this.loadPng(this.playerStartButtonUrl).then((asset) => {sprite.spriteFrame = new cc.SpriteFrame(asset as unknown as cc.Texture2D);node.setPosition(cc.v2(0, 0));cc.Canvas.instance.node.addChild(node);node.once(cc.Node.EventType.TOUCH_END, () => {node.destroy();this.hasClicked = true;resolve();});});});}private checkIsApple() {return /iphone|ipad|ios|mac/gi.test(navigator.userAgent.toLowerCase());}private getVideoDom(url: string) {const list = document.getElementsByClassName(this.CLASS_NAME);for (let i = 0; i < list.length; i++) {const video = list[i];if (url == video["src"]) {return video;}}return null;}
}

使用样例:

import VideoPlayerManage from "./VideoPlayerManage";const { ccclass } = cc._decorator;@ccclass
export default class Index extends cc.Component {private list = ["http://localhost:3000/light1.mp4","http://localhost:3000/light2.mp4",];private index = 0;protected async onLoad(): Promise<void> {await VideoPlayerManage.instance.init(this.list.map((url) => {return {tag: url,url,width: this.node.width,height: this.node.height,isLoop: true,endCallback: () => {console.log("end");},};}),{poster: "",playerStartButtonUrl: "http://localhost:3000/head1.png",});this.playByIndex();this.node.on(cc.Node.EventType.TOUCH_END, () => {this.playByIndex();});}playByIndex() {this.index++;if (this.index >= this.list.length) {this.index = 0;}VideoPlayerManage.instance.play(this.list[this.index]);}
}

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

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

相关文章

C++中的栈(Stack)和堆(Heap)

在C中&#xff0c;堆&#xff08;heap&#xff09;和栈&#xff08;stack&#xff09;是两种用于存储数据的内存区域。理解它们的原理和区别&#xff0c;对于优化代码性能和确保代码的安全性至关重要。以下是对C中堆栈的详细解析&#xff0c;包括它们的分配方式、优缺点、应用场…

爬虫开发工具与环境搭建——环境配置

第二章&#xff1a;爬虫开发工具与环境搭建 第二节&#xff1a;环境配置 在进行爬虫开发之前&#xff0c;首先需要配置好开发环境。一个良好的开发环境不仅能提高开发效率&#xff0c;还能避免因环境不一致带来的问题。以下是环境配置的详细步骤&#xff0c;涵盖了Python开发…

wpf的C1FlexGrid可见表格合并计算操作

计算动态加载行后的部分字段的计算求和操作 表格上添加事件触发ItemsSourceChanged属性&#xff0c;触发事件 <c1:C1FlexGrid Name"CfgSaleOrderReviewItem" Style"{StaticResource Green}" ItemsSource"{Binding SaleOrderList,ModeTwoWay}"…

计算机图形学在游戏开发中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 计算机图形学在游戏开发中的应用 计算机图形学在游戏开发中的应用 计算机图形学在游戏开发中的应用 引言 计算机图形学的基本概念…

计算机视觉和机器人技术中的下一个标记预测与视频扩散相结合

一种新方法可以训练神经网络对损坏的数据进行分类&#xff0c;同时预测下一步操作。 它可以为机器人制定灵活的计划&#xff0c;生成高质量的视频&#xff0c;并帮助人工智能代理导航数字环境。 Diffusion Forcing 方法可以对嘈杂的数据进行分类&#xff0c;并可靠地预测任务的…

大学语文教材电子版(第十一版)教学用书PDF及课件

大学语文课件&#xff1a;https://caiyun.139.com/m/i?005CiDusEVWnR 《大学语文》&#xff08;第十一版&#xff09;主编&#xff1a;徐中玉 齐森华 谭帆。 大学语文教材电子版教师用书PDF第一课《齐桓晋文之事》艺术赏析&#xff1a; 孟子四处游说&#xff0c;养成善辩的…

鸿蒙核心技术理念

文章目录 1)一次开发,多端部署2)可分可合,自由流转3)统一生态,原生智能1)一次开发,多端部署 “一次开发,多端部署”指的是一个工程,一次开发上架,多端按需部署。目的是支撑开发者高效地开发多种终端设备上的应用 2)可分可合,自由流转 元服务是鸿蒙系统提供的一…

数学分组求偶数和

问题描述 小M面对一组从 1 到 9 的数字&#xff0c;这些数字被分成多个小组&#xff0c;并从每个小组中选择一个数字组成一个新的数。目标是使得这个新数的各位数字之和为偶数。任务是计算出有多少种不同的分组和选择方法可以达到这一目标。 numbers: 一个由多个整数字符串组…

PCHMI串口接收实验

插入的唯一一行代码 config1.START((Control)this, System.Reflection.Assembly.GetExecutingAssembly().GetTypes(), null);

华为Ensp模拟器配置RIP路由协议

目录 RIP路由详解&#xff1a;另一种视角解读 1. RIP简介&#xff1a;轻松理解基础概念 2. RIP的核心机制&#xff1a;距离向量的魅力 3. RIP的实用与局限 RIP配置实验 实验图 ​编辑 PC的ip配置 RIP配置步骤 测试 结语&#xff1a;RIP的今天与明天 RIP路由详解&…

IDEA 开发工具常用快捷键有哪些?

‌在IDEA中&#xff0c;输出System.out.println()的快捷键是sout&#xff0c;输入后按回车&#xff08;或Tab键&#xff09;即可自动补全为System.out.println()‌‌。 此外&#xff0c;IDEA中还有一些其他常用的快捷键&#xff1a; 创建main方法的快捷键是psvm&#xff0c;代…

鲸鱼机器人和乐高机器人的比较

鲸鱼机器人和乐高机器人各有其独特的优势和特点&#xff0c;家长在选择时可以根据孩子的年龄、兴趣、经济能力等因素进行综合考虑&#xff0c;选择最适合孩子的教育机器人产品。 优势 鲸鱼机器人 1&#xff09;价格亲民&#xff1a;鲸鱼机器人的产品价格相对乐高更为亲民&…

【java基础】总结一

目录 特点 JavaSE和JavaEE JVM,JDK,JRE 字节码 编译语言和解释语言 AOT介绍 不同jdk java语法 变量 静态方法 静态方法和实例方法 重载和重写 可变长参数 特点 简单&#xff0c;面向对象&#xff08;封装、继承、多态&#xff09;&#xff0c;平台无关&#xff…

vue内置指令和自定义指令

常见的指令&#xff1a; v-bind : 单向绑定解析表达式, 可简写为 :xxx v-model : 双向数据绑定 v-for : 遍历数组/对象/字符串 v-on : 绑定事件监听, 可简…

redis linux 安装

下载解压 https://download.redis.io/releases/ tar -zvxf ----redis-7.4.1编译 进入目录下 # redis 依赖c yum install gcc-cmake可能会有问题&#xff0c;所以记得换源# 安装到 /usr/local/redis make PREFIX/usr/local/redis installcd src ./redis-serverredis.confi…

使用ACF插件向WooCommerce商城产品添加自定义字段

WooCommerce网站的一个常见请求是需要在单个产品页面上包含额外的字段输入&#xff0c;并在前端输出它们。我将解释如何使用出色的ACF高级自定义字段插件&#xff08;免费版&#xff09;来实现这一点。 需要编写一些代码&#xff0c;但不用担心&#xff0c;一切都非常简单。此…

3.task1 suimove helloword 上链

1.创建项目 sui move new hello_world 2.代码部分 module hello_move::hello {use std::ascii::{String, string};use sui::object::{Self,UID};use sui::transfer::transfer;use sui::tx_context::{TxContext, sender};public struct Hello has key{id:UID,say: String}fun …

CSS回顾-基础知识详解

一、引言 在前端开发领域&#xff0c;CSS 曾是构建网页视觉效果的关键&#xff0c;与 HTML、JavaScript 一起打造精彩的网络世界。但随着组件库的大量涌现&#xff0c;我们亲手书写 CSS 样式的情况越来越少&#xff0c;CSS 基础知识也逐渐被我们遗忘。 现在&#xff0c;这种遗…

从0开始机器学习--Day27--主成分分析方法

主成分分析方法(Principal components analysis) 在降维算法中&#xff0c;比较普遍的是使用主成分分析方法&#xff08;PCA&#xff09; PCA算法简单示例 如图&#xff0c;假设我们有一个二维的特征&#xff0c;想要将其降为一维&#xff0c;简单的方法是寻找一条直线&#…

电子工牌独立双通道定向拾音方案(有视频演示)

现在一些行业的客服人员在面对客户都要求使用电子工牌分别记录客服和顾客的声音,我们利用双麦克风阵列双波束拾音的方案设计了一个电子工牌方案.可以有效分别记录客服和顾客的声音. 方案思路: 我们采用了一个双麦阵列波束拾音的模块A-59,此模块可以利用2个麦克风组成阵列进行双…