Files
18xchat/assets/Scripts/Sub/UI/SceneBgVideoLayer.ts
T
2025-09-11 10:54:06 +08:00

347 lines
9.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
_decorator,
isValid,
Node,
VideoClip,
UITransform,
Vec3,
VideoPlayer,
Size,
} from "cc";
import Utils from "../../Main/Common/Utils";
import li_BaseView from "../../Main/Common/li_BaseView";
import ResManager from "../../Main/Manager/ResManager";
const { ccclass, property } = _decorator;
//场景背景层 - 视频模式
@ccclass("SceneBgVideoLayer")
export class SceneBgVideoLayer extends li_BaseView {
private _nodeTab: any = {};
private _data;
static handle: SceneBgVideoLayer | null = null;
private videoPlayer: VideoPlayer = null;
private isPlaying: boolean = false;
private currentVideoData: any = null;
//----重写父类接口---------------------------------------
onLoadCT() {
Utils.parseNode(this.node, this._nodeTab);
this.videoPlayer = this._nodeTab.videoPlayer.getComponent(VideoPlayer);
if (!this.videoPlayer) {
console.error("SceneBgVideoLayer: video节点未找到或没有VideoPlayer组件");
}
this.initVideoEvents();
SceneBgVideoLayer.handle = this;
}
openUIDataCT(data: any): void {
this._data = data;
}
/**初始化视频事件监听 */
private initVideoEvents() {
if (!this.videoPlayer) return;
// 移除之前的事件监听器,避免重复绑定
this.videoPlayer.node.off(
VideoPlayer.EventType.READY_TO_PLAY,
this.onVideoReadyToPlay,
this
);
//this.videoPlayer.node.off(VideoPlayer.EventType.VIDEO_PLAYER_EVENT, this.onVideoEvent, this);
// 添加新的事件监听
this.videoPlayer.node.on(
VideoPlayer.EventType.READY_TO_PLAY,
this.onVideoReadyToPlay,
this
);
// this.videoPlayer.node.on(VideoPlayer.EventType.VIDEO_PLAYER_EVENT, this.onVideoEvent, this);
}
//================== 公共 API 方法 ==================
/**
* 播放视频 - 统一的视频播放接口
* @param path 视频路径
* @param options 可选配置项
* @param options.loop 是否循环播放,默认true
* @param options.size 视频尺寸 {width, height}
* @param options.position 视频位置 {x, y, z}
* @param options.scale 视频缩放值
* @param options.autoFill 是否自动填充到指定尺寸
*/
playVideo(
path: string,
options?: {
loop?: boolean;
size?: { width: number; height: number } | Size;
position?: { x: number; y: number; z?: number } | Vec3;
scale?: number | Vec3;
}
) {
if (!this.videoPlayer) {
console.error("SceneBgVideoLayer: VideoPlayer未初始化");
return;
}
// 默认参数
const config = {
loop: options?.loop ?? true,
size: options?.size,
position: options?.position,
scale: options?.scale,
};
// 先停止当前视频
if (this.isPlaying) {
this.stop();
}
// 加载本地视频资源
ResManager.I.changeBundleVideo(
this.videoPlayer,
path,
"Girls",
(res: VideoClip) => {
this.videoPlayer.loop = config.loop;
// 设置锚点为中心,确保位置对齐
const uiTransform = this.videoPlayer.node.getComponent(UITransform);
if (uiTransform) {
uiTransform.setAnchorPoint(0.5, 0.5);
}
// 应用位置设置(使用世界坐标)
if (config.position) {
const pos = config.position;
if (pos instanceof Vec3) {
this.videoPlayer.node.setWorldPosition(pos);
} else {
this.videoPlayer.node.setWorldPosition(pos.x, pos.y, pos.z || 0);
}
}
// 如果传入了目标尺寸,计算并应用缩放
if (config.size && uiTransform) {
// 获取视频的原始尺寸
const videoWidth = uiTransform.contentSize.width;
const videoHeight = uiTransform.contentSize.height;
if (videoWidth > 0 && videoHeight > 0) {
const targetWidth = config.size instanceof Size ? config.size.width : config.size.width;
const targetHeight = config.size instanceof Size ? config.size.height : config.size.height;
// 计算缩放比例(填满整个区域)
const scaleX = targetWidth / videoWidth;
const scaleY = targetHeight / videoHeight;
const scale = Math.max(scaleX, scaleY);
this.videoPlayer.node.setScale(new Vec3(scale, scale, 1));
console.log(`SceneBgVideoLayer: 视频原始尺寸: ${videoWidth}x${videoHeight}, 目标尺寸: ${targetWidth}x${targetHeight}, 缩放: ${scale}`);
} else {
// 如果视频尺寸还未准备好,延迟重试
this.scheduleOnce(() => {
if (config.size && uiTransform) {
const videoWidth = uiTransform.contentSize.width;
const videoHeight = uiTransform.contentSize.height;
if (videoWidth > 0 && videoHeight > 0) {
const targetWidth = config.size instanceof Size ? config.size.width : config.size.width;
const targetHeight = config.size instanceof Size ? config.size.height : config.size.height;
const scaleX = targetWidth / videoWidth;
const scaleY = targetHeight / videoHeight;
const scale = Math.max(scaleX, scaleY);
this.videoPlayer.node.setScale(new Vec3(scale, scale, 1));
console.log(`SceneBgVideoLayer: (延迟) 视频原始尺寸: ${videoWidth}x${videoHeight}, 目标尺寸: ${targetWidth}x${targetHeight}, 缩放: ${scale}`);
}
}
}, 0.1);
}
}
// 如果显式传入了 scale 参数,覆盖自动计算的缩放
else if (config.scale !== undefined) {
this.setVideoScale(config.scale);
}
this.videoPlayer.play();
this.isPlaying = true;
console.log(`SceneBgVideoLayer: 播放视频 ${path}, 配置:`, config);
}
);
}
/**
* 暂停视频
*/
pause() {
if (this.videoPlayer && this.isPlaying) {
this.videoPlayer.pause();
console.log("SceneBgVideoLayer: 暂停视频");
}
}
/**
* 继续播放视频
*/
resume() {
if (this.videoPlayer && !this.videoPlayer.isPlaying) {
this.videoPlayer.play();
console.log("SceneBgVideoLayer: 继续播放视频");
}
}
/**
* 停止视频
*/
stop() {
if (this.videoPlayer) {
this.videoPlayer.stop();
this.isPlaying = false;
console.log("SceneBgVideoLayer: 停止视频");
}
}
/**
* 设置视频缩放
* @param scale 缩放值(数字或Vec3
*/
setVideoScale(scale: number | Vec3) {
if (this.videoPlayer) {
if (typeof scale === "number") {
this.videoPlayer.node.setScale(new Vec3(scale, scale, 1));
console.log(`SceneBgVideoLayer: 设置视频缩放 ${scale}`);
} else {
this.videoPlayer.node.setScale(scale);
console.log(
`SceneBgVideoLayer: 设置视频缩放 (${scale.x}, ${scale.y}, ${scale.z})`
);
}
}
}
/**
* 调整视频以填满指定区域
* @param targetSize 目标尺寸
* @param position 可选的位置
*/
adjustToFillArea(targetSize: Size, position?: Vec3) {
if (!this.videoPlayer) return;
const tryAdjust = () => {
const uiTransform = this.videoPlayer.node.getComponent(UITransform);
if (!uiTransform) return false;
const videoSize = uiTransform.contentSize;
if (videoSize.width > 0 && videoSize.height > 0) {
// 计算宽度和高度的缩放比例
const scaleX = targetSize.width / videoSize.width;
const scaleY = targetSize.height / videoSize.height;
// 选择较大的缩放比例以填满整个区域
const scale = Math.max(scaleX, scaleY);
this.videoPlayer.node.setScale(new Vec3(scale, scale, 1));
if (position) {
this.videoPlayer.node.setWorldPosition(position);
}
console.log(`SceneBgVideoLayer: 自适应填充区域,缩放: ${scale}`);
return true;
}
return false;
};
// 立即尝试一次
if (!tryAdjust()) {
// 如果失败,延迟尝试
this.scheduleOnce(() => {
if (!tryAdjust()) {
this.scheduleOnce(() => {
tryAdjust();
}, 0.5);
}
}, 0.1);
}
}
//================== 事件回调 ==================
private onVideoReadyToPlay = () => {
console.log("SceneBgVideoLayer: 视频准备完成");
this.onVideoReady();
};
private onVideoEvent = (event: any, eventType: string) => {
switch (eventType) {
case "completed":
console.log("SceneBgVideoLayer: 视频播放完成");
this.isPlaying = false;
this.onVideoEnd();
break;
case "error":
console.error("SceneBgVideoLayer: 视频播放错误");
this.isPlaying = false;
this.onVideoError();
break;
}
};
/**
* 视频准备完成回调(可重写)
*/
protected onVideoReady() {
// 由子类重写或外部监听
}
/**
* 视频播放完成回调(可重写)
*/
protected onVideoEnd() {
// 由子类重写或外部监听
}
/**
* 视频播放错误回调(可重写)
*/
protected onVideoError() {
// 由子类重写或外部监听
}
//================== 其他方法 ==================
/**
* 获取当前播放状态
*/
getIsPlaying(): boolean {
return this.isPlaying;
}
/**
* 获取VideoPlayer组件引用
*/
getVideoPlayer(): VideoPlayer | null {
return this.videoPlayer;
}
/**
* 销毁时清理资源
*/
onDestroy() {
if (this.videoPlayer) {
this.videoPlayer.node.off(
VideoPlayer.EventType.READY_TO_PLAY,
this.onVideoReadyToPlay,
this
);
//this.videoPlayer.node.off(VideoPlayer.EventType.VIDEO_PLAYER_EVENT, this.onVideoEvent, this);
}
SceneBgVideoLayer.handle = null;
}
}