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

ops/2024/11/18 1:39:49/
/*** 对外输出接口*/
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.ppmy.cn/ops/134592.html

相关文章

C语言入门到精通(第六版)——第十六章

16、网络套接字编程 16.1、计算机网络基础 计算机网络技术是计算机技术和通信技术相结合的产物&#xff0c;代表计算机的一个重要发展方向。了解计算机的网络结构&#xff0c;有助于用户开发网络应用程序。 16.1.1、IP地址 为了使网络上的计算机能够彼此识别对方&#xff0c;…

解决Windows远程桌面 “为安全考虑,已锁定该用户账户,原因是登录尝试或密码更改尝试过多。请稍后片刻再重试,或与系统管理员或技术支持联系“问题

当我们远程连接服务器连接不上并提示“为安全考虑&#xff0c;已锁定该用户账户&#xff0c;原因是登录尝试或密码更改尝试过多。请稍候片刻再重试&#xff0c;或与系统管理员或技术支持联系”时&#xff0c;根本原因是当前计算机远程连接时输入了过多的错误密码&#xff0c;触…

进程的程序替换exec*函数和shell实现

进程程序替换函数&#xff1a;execl - - - 让进程使用 exec 函数把自己得到代码和程序替换掉&#xff0c;执行新的程序*。不会直接更改原进程PCB页表等&#xff0c;只会把原进程PCB原本所映射页表映射到的物理内存的代码和数据覆盖成 execl 所填写的程序数据。只要替换成功&…

深入解析贪心算法及其应用实例

标题&#xff1a;深入解析贪心算法及其应用实例 一、引言 贪心算法&#xff08;Greedy Algorithm&#xff09;是一类简单、直观的算法设计策略&#xff0c;广泛应用于优化问题中。其基本思想是每一步都选择当前状态下最优的选择&#xff0c;即在每一步做出局部最优的决策&…

lua实现雪花算法

lua实现雪花算法 雪花算法介绍组成部分优点缺点 代码示例 雪花算法介绍 雪花算法&#xff08;Snowflake Algorithm&#xff09;是一种用于生成唯一ID的分布式生成算法&#xff0c;最初由Twitter开发。它的主要目的是在分布式系统中生成唯一的、时间有序的ID&#xff0c;这些ID通…

【IEEE出版 | 中国石油大学(华东)主办】第六届信息与计算机前沿术国际学术会议(ICFTIC 2024,12月13-15日)

第六届信息与计算机前沿术国际学术会议(ICFTIC 2024) 2024 6th International Conference on Frontier Technologies of Information and Computer 官方信息 会议官网&#xff1a;WWW.ICFTIC.ORG 2024 6th International Conference on Frontier Technologies of Information…

Python爬虫项目 | 一、网易云音乐热歌榜歌曲

文章目录 1.文章概要1.1 实现方法1.2 实现代码1.3 最终效果 2.具体讲解2.1 使用的Python库2.2 代码说明2.2.1 创建目录保存文件2.2.2 爬取网易云音乐热歌榜单歌曲 2.3 过程展示 3 总结 1.文章概要 学习Python爬虫知识&#xff0c;实现简单的一个小案例&#xff0c;网易云音乐热…

【设计模式】结合Tomcat源码,分析外观模式/门面模式的特性和应用场景

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码解析 目录 一、经典的组建家庭影院流程 二、传统方式解决影院管理 2.1 实现方案&#xff1a;客户端直接调用各流程 2.2 …