Files
18xchat/assets/Scripts/Sub/UI/SceneBgVideoLayer.ts
T

381 lines
11 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";
import { DataManager, DataId } from "../../chat18x/data/DataManager";
import { EnvData } from "../../chat18x/data/EnvData";
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;
// 待处理的视频配置(用于在视频准备好后应用)
private pendingVideoConfig: {
size?: { width: number; height: number } | Size;
position?: { x: number; y: number; z?: number } | Vec3;
scale?: number | Vec3;
} | null = 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;
}
getVideoClone() {
return this.videoPlayer;
}
/**初始化视频事件监听 */
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,
};
// 先停止当前视频并清理状态
if (this.isPlaying) {
this.stop();
}
// 存储待处理的配置,在视频准备好后应用
this.pendingVideoConfig = {
size: config.size,
position: config.position,
scale: options?.scale,
};
const envData = DataManager.I.getDataById<EnvData>(DataId.Env);
let newPath = envData.cdn + "/" + "Girls/" + path + ".mp4";
// 加载远程资源
ResManager.I.loadRemoteVideo(
this.videoPlayer,
newPath,
(res: VideoClip) => {
this.videoPlayer.loop = config.loop;
// 设置锚点为中心,确保位置对齐
const uiTransform = this.videoPlayer.node.getComponent(UITransform);
if (uiTransform) {
uiTransform.setAnchorPoint(0.5, 0.5);
console.log("SceneBgVideoLayer: 设置视频锚点为 (0.5, 0.5)");
}
// 立即开始播放,尺寸和位置将在 READY_TO_PLAY 事件中设置
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.clearVideoState();
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;
// 如果视频正在播放,直接应用配置
if (this.isPlaying && this.videoPlayer.isPlaying) {
this.applyVideoConfiguration({
size: targetSize,
position: position,
});
} else {
// 如果视频还未准备好,存储配置等待应用
this.pendingVideoConfig = {
size: targetSize,
position: position,
};
console.log("SceneBgVideoLayer: 视频未准备好,配置已存储等待应用");
}
}
/**
* 清除视频状态和待处理配置
*/
private clearVideoState() {
this.isPlaying = false;
this.pendingVideoConfig = null;
this.currentVideoData = null;
}
//================== 内部辅助方法 ==================
/**
* 应用视频配置(位置、尺寸、缩放)
* @param config 视频配置对象
*/
private applyVideoConfiguration(config: {
size?: { width: number; height: number } | Size;
position?: { x: number; y: number; z?: number } | Vec3;
scale?: number | Vec3;
}) {
if (!this.videoPlayer) return;
const uiTransform = this.videoPlayer.node.getComponent(UITransform);
if (!uiTransform) return;
// 应用位置设置
if (config.position) {
const pos = config.position;
let targetWorldPos: Vec3;
if (pos instanceof Vec3) {
targetWorldPos = pos;
} else {
targetWorldPos = new Vec3(pos.x, pos.y, pos.z || 0);
}
this.videoPlayer.node.setWorldPosition(targetWorldPos);
console.log(
`SceneBgVideoLayer: 设置视频位置 (${targetWorldPos.x}, ${targetWorldPos.y}, ${targetWorldPos.z})`
);
}
// 应用尺寸和缩放设置
if (config.size) {
// 获取视频的原始尺寸(此时应该是准确的)
const videoSize = uiTransform.contentSize;
if (videoSize.width > 0 && videoSize.height > 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 / videoSize.width;
const scaleY = targetHeight / videoSize.height;
const scale = Math.max(scaleX, scaleY);
this.videoPlayer.node.setScale(new Vec3(scale, scale, 1));
console.log(
`SceneBgVideoLayer: 设置视频缩放 ${scale} (原始尺寸: ${videoSize.width}x${videoSize.height}, 目标尺寸: ${targetWidth}x${targetHeight})`
);
} else {
console.warn("SceneBgVideoLayer: 无法获取视频尺寸,跳过缩放设置");
}
} else if (config.scale) {
// 如果显式传入了 scale 参数,直接应用
if (typeof config.scale === "number") {
this.videoPlayer.node.setScale(new Vec3(config.scale, config.scale, 1));
console.log(`SceneBgVideoLayer: 设置显式缩放 ${config.scale}`);
} else {
this.videoPlayer.node.setScale(config.scale);
console.log(
`SceneBgVideoLayer: 设置显式缩放 (${config.scale.x}, ${config.scale.y}, ${config.scale.z})`
);
}
} else {
// 没有指定尺寸或缩放,保持原始缩放
this.videoPlayer.node.setScale(Vec3.ONE);
console.log("SceneBgVideoLayer: 保持默认缩放 (1, 1, 1)");
}
}
//================== 事件回调 ==================
private onVideoReadyToPlay = () => {
console.log("SceneBgVideoLayer: 视频准备完成");
// 应用待处理的视频配置
if (this.pendingVideoConfig) {
this.applyVideoConfiguration(this.pendingVideoConfig);
// 清空待处理配置
this.pendingVideoConfig = null;
}
this.onVideoReady();
};
private onVideoEvent = (event: any, eventType: string) => {
switch (eventType) {
case "completed":
console.log("SceneBgVideoLayer: 视频播放完成");
this.clearVideoState();
this.onVideoEnd();
break;
case "error":
console.error("SceneBgVideoLayer: 视频播放错误");
this.clearVideoState();
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.stop();
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.clearVideoState();
SceneBgVideoLayer.handle = null;
}
}