头像入版 页面跳转优化

This commit is contained in:
2025-09-11 17:07:29 +08:00
parent d6a9898c8d
commit 6bf85c6ad8
74 changed files with 4238 additions and 1070 deletions
@@ -2,6 +2,25 @@ import { ViewManager } from "db://assets/Scripts/Main/Manager/ViewManager";
import { UITransitionHelper } from "../utils/UITransitionHelper";
import GameRootUI from "../../Main/Common/GameRootUI";
/**
* 页面过渡动画类型枚举
*/
export enum PageTransitionType {
NONE = "none",
SLIDE_LEFT = "slide_left",
SLIDE_RIGHT = "slide_right",
}
/**
* 页面过渡动画配置接口
*/
export interface PageTransitionConfig {
incomingTransition: PageTransitionType;
outgoingTransition: PageTransitionType;
duration?: number;
simultaneous?: boolean;
}
/**
* 导航管理器
*
@@ -28,6 +47,183 @@ export class NavigationManager {
private constructor() {}
/**
* 通用页面导航方法,支持过渡动画
*
* @param {string} targetPanel - 目标面板名称
* @param {any} navigationData - 导航数据
* @param {PageTransitionConfig} transitionConfig - 过渡动画配置
* @param {any} currentPanel - 当前面板实例(可选)
* @param {Function} onComplete - 完成回调(可选)
*
* @example
* ```typescript
* NavigationManager.Instance.navigateWithTransition(
* "ChatPanel",
* { roleId: 10001 },
* { incomingTransition: PageTransitionType.SLIDE_RIGHT, outgoingTransition: PageTransitionType.SLIDE_LEFT },
* currentPanel
* );
* ```
*/
public navigateWithTransition(
targetPanel: string,
navigationData: any,
transitionConfig: PageTransitionConfig,
currentPanel?: any,
onComplete?: Function
): void {
if (!targetPanel) {
console.warn("NavigationManager: Target panel name is required");
return;
}
console.log(
`Navigating to ${targetPanel} with transition:`,
transitionConfig
);
//const correntPos = currentPanel.node.position;
const duration = transitionConfig.duration || 0.1;
const useTransition =
transitionConfig.incomingTransition !== PageTransitionType.NONE ||
transitionConfig.outgoingTransition !== PageTransitionType.NONE;
if (!useTransition) {
ViewManager.I.openBundlesView(targetPanel, navigationData, onComplete);
if (
currentPanel &&
currentPanel.onClose &&
typeof currentPanel.onClose === "function"
) {
currentPanel.onClose();
//currentPanel.node.position = correntPos;
}
return;
}
const extendedNavigationData = {
...navigationData,
withTransition: true,
transitionType: transitionConfig.incomingTransition,
};
ViewManager.I.openBundlesView(
targetPanel,
extendedNavigationData,
(targetNode) => {
this._executeTransition(
targetNode,
currentPanel,
transitionConfig,
duration,
onComplete
);
}
);
}
/**
* 执行页面过渡动画的私有方法
*
* @private
* @param {any} targetNode - 目标节点
* @param {any} currentPanel - 当前面板
* @param {PageTransitionConfig} config - 动画配置
* @param {number} duration - 动画时长
* @param {Function} onComplete - 完成回调
*/
private _executeTransition(
targetNode: any,
currentPanel: any,
config: PageTransitionConfig,
duration: number,
onComplete?: Function
): void {
const executeIncomingAnimation = (callback?: Function) => {
if (
config.incomingTransition === PageTransitionType.NONE ||
!targetNode
) {
callback && callback();
return;
}
switch (config.incomingTransition) {
case PageTransitionType.SLIDE_RIGHT:
UITransitionHelper.slideInFromRight(targetNode, duration, callback);
break;
case PageTransitionType.SLIDE_LEFT:
UITransitionHelper.slideInFromLeft(targetNode, duration, callback);
break;
default:
callback && callback();
break;
}
};
const executeOutgoingAnimation = (callback?: Function) => {
if (
config.outgoingTransition === PageTransitionType.NONE ||
!currentPanel ||
!currentPanel.node ||
!currentPanel.node.isValid
) {
callback && callback();
return;
}
switch (config.outgoingTransition) {
case PageTransitionType.SLIDE_LEFT:
UITransitionHelper.slideOutToLeft(
currentPanel.node,
duration,
callback
);
break;
case PageTransitionType.SLIDE_RIGHT:
UITransitionHelper.slideOutToRight(
currentPanel.node,
duration,
callback
);
break;
default:
callback && callback();
break;
}
};
const closeCurrentPanel = () => {
if (
currentPanel &&
currentPanel.onClose &&
typeof currentPanel.onClose === "function"
) {
if (currentPanel.node.name !== "ThemePanel") currentPanel.onClose();
}
onComplete && onComplete();
};
if (config.simultaneous) {
let completedAnimations = 0;
const animationComplete = () => {
completedAnimations++;
if (completedAnimations >= 2) {
closeCurrentPanel();
}
};
executeIncomingAnimation(animationComplete);
executeOutgoingAnimation(animationComplete);
} else {
executeOutgoingAnimation(() => {
executeIncomingAnimation(closeCurrentPanel);
});
}
}
/**
* 进入角色列表页面
*
@@ -38,15 +234,56 @@ export class NavigationManager {
* NavigationManager.Instance.navigateToGirlList(1);
* ```
*/
public navigateToGirlList(themeId: number): void {
public navigateToGirlList(themeId: number): void;
/**
* 进入角色列表页面(带过渡动画)
*
* @param {number} themeId - 主题ID,用于筛选角色
* @param {PageTransitionConfig} transitionConfig - 过渡动画配置
* @param {any} currentPanel - 当前面板实例(可选)
*
* @example
* ```typescript
* NavigationManager.Instance.navigateToGirlList(1,
* { incomingTransition: PageTransitionType.SLIDE_RIGHT, outgoingTransition: PageTransitionType.FADE },
* this
* );
* ```
*/
public navigateToGirlList(
themeId: number,
transitionConfig?: PageTransitionConfig,
currentPanel?: any
): void;
public navigateToGirlList(
themeId: number,
transitionConfig?: PageTransitionConfig,
currentPanel?: any
): void {
if (themeId == null || themeId < 0) {
console.warn("Invalid theme ID for girl list navigation:", themeId);
return;
}
GameRootUI.I.hideDefaultView();
console.log(`Navigating to girl list with theme ID: ${themeId}`);
ViewManager.I.openBundlesView("GirlListPanel", themeId);
if (transitionConfig) {
const navigationData = {
category: themeId,
withTransition: true,
};
this.navigateWithTransition(
"GirlListPanel",
navigationData,
transitionConfig,
currentPanel,
() => {
GameRootUI.I.hideDefaultView();
}
);
} else {
ViewManager.I.openBundlesView("GirlListPanel", themeId);
}
}
/**
@@ -59,26 +296,62 @@ export class NavigationManager {
* NavigationManager.Instance.navigateToChat(10001);
* ```
*/
public navigateToChat(roleId: number): void {
public navigateToChat(roleId: number): void;
/**
* 进入聊天页面(带过渡动画)
*
* @param {number} roleId - 角色ID
* @param {PageTransitionConfig} transitionConfig - 过渡动画配置
* @param {any} currentPanel - 当前面板实例(可选)
*
* @example
* ```typescript
* NavigationManager.Instance.navigateToChat(10001,
* { incomingTransition: PageTransitionType.FADE, outgoingTransition: PageTransitionType.SLIDE_LEFT },
* this
* );
* ```
*/
public navigateToChat(
roleId: number,
transitionConfig?: PageTransitionConfig,
currentPanel?: any
): void;
public navigateToChat(
roleId: number,
transitionConfig?: PageTransitionConfig,
currentPanel?: any
): void {
if (roleId == null || roleId <= 0) {
console.warn("Invalid role ID for chat navigation:", roleId);
return;
}
GameRootUI.I.hideDefaultView();
console.log(`Navigating to chat with role ID: ${roleId}`);
ViewManager.I.openBundlesView("ChatPanel", roleId);
if (transitionConfig) {
this.navigateWithTransition(
"ChatPanel",
roleId,
transitionConfig,
currentPanel
);
} else {
ViewManager.I.openBundlesView("ChatPanel", roleId);
}
}
/**
* 带过渡动画进入聊天页面
* 从GirlDetailPanel平滑过渡到ChatPanel
*
* @param {string} categoryId - 类别ID
* @param {number} roleId - 角色ID
* @param {any} currentPanel - 当前面板实例(GirlDetailPanel
*
* @example
* ```typescript
* NavigationManager.Instance.navigateToChatWithTransition(10001, this);
* NavigationManager.Instance.navigateToChatWithTransition("1", 10001, this);
* ```
*/
public navigateToChatWithTransition(
@@ -94,30 +367,24 @@ export class NavigationManager {
return;
}
console.log(`Navigating to chat with transition, role ID: ${roleId}`);
const navigationData = {
categoryId: categoryId,
roleId: roleId,
withSlideTransition: true,
};
// 打开ChatPanel,同时标记需要执行滑入动画
ViewManager.I.openBundlesView(
const transitionConfig: PageTransitionConfig = {
incomingTransition: PageTransitionType.SLIDE_RIGHT,
outgoingTransition: PageTransitionType.SLIDE_LEFT,
duration: 0.3,
simultaneous: true,
};
this.navigateWithTransition(
"ChatPanel",
{
categoryId: categoryId,
roleId: roleId,
withSlideTransition: true,
},
(chatNode) => {
// ChatPanel打开后,如果有当前面板,执行滑出动画
if (currentPanel && currentPanel.node && currentPanel.node.isValid) {
UITransitionHelper.slideOutToLeft(currentPanel.node, 0.3, () => {
// 滑出动画完成后关闭原面板
if (
currentPanel.onClose &&
typeof currentPanel.onClose === "function"
) {
currentPanel.onClose();
}
});
}
}
navigationData,
transitionConfig,
currentPanel
);
}
@@ -131,14 +398,49 @@ export class NavigationManager {
* NavigationManager.Instance.navigateToGirlDetail(10001);
* ```
*/
public navigateToGirlDetail(roleId: number): void {
public navigateToGirlDetail(roleId: number): void;
/**
* 进入角色详情页面(带过渡动画)
*
* @param {number} roleId - 角色ID
* @param {PageTransitionConfig} transitionConfig - 过渡动画配置
* @param {any} currentPanel - 当前面板实例(可选)
*
* @example
* ```typescript
* NavigationManager.Instance.navigateToGirlDetail(10001,
* { incomingTransition: PageTransitionType.SLIDE_RIGHT, outgoingTransition: PageTransitionType.SLIDE_LEFT },
* this
* );
* ```
*/
public navigateToGirlDetail(
roleId: number,
transitionConfig?: PageTransitionConfig,
currentPanel?: any
): void;
public navigateToGirlDetail(
roleId: number,
transitionConfig?: PageTransitionConfig,
currentPanel?: any
): void {
if (roleId == null || roleId <= 0) {
console.warn("Invalid role ID for detail navigation:", roleId);
return;
}
console.log(`Navigating to girl detail with role ID: ${roleId}`);
ViewManager.I.openBundlesView("GirlDetailPanel", roleId);
if (transitionConfig) {
this.navigateWithTransition(
"GirlDetailPanel",
roleId,
transitionConfig,
currentPanel
);
} else {
ViewManager.I.openBundlesView("GirlDetailPanel", roleId);
}
}
/**
@@ -184,4 +486,39 @@ export class NavigationManager {
// TODO: 实现返回上一页功能
console.log("Go back feature not implemented yet");
}
/**
* 获取预设的过渡动画配置
*/
public static getTransitionPresets() {
return {
SLIDE_LEFT_TO_RIGHT: {
incomingTransition: PageTransitionType.SLIDE_RIGHT,
outgoingTransition: PageTransitionType.SLIDE_LEFT,
duration: 0.1,
simultaneous: true,
} as PageTransitionConfig,
SLIDE_RIGHT_TO_LEFT: {
incomingTransition: PageTransitionType.SLIDE_LEFT,
outgoingTransition: PageTransitionType.SLIDE_RIGHT,
duration: 0.1,
simultaneous: true,
} as PageTransitionConfig,
SLIDE_OVER: {
incomingTransition: PageTransitionType.SLIDE_RIGHT,
outgoingTransition: PageTransitionType.NONE,
duration: 0.1,
simultaneous: true,
} as PageTransitionConfig,
SLIDE_UNDER: {
incomingTransition: PageTransitionType.NONE,
outgoingTransition: PageTransitionType.SLIDE_LEFT,
duration: 0.1,
simultaneous: true,
} as PageTransitionConfig,
};
}
}
@@ -7,6 +7,8 @@ import {
instantiate,
UITransform,
Size,
Vec3,
tween,
} from "cc";
import li_BaseView from "db://assets/Scripts/Main/Common/li_BaseView";
import ResManager from "db://assets/Scripts/Main/Manager/ResManager";
@@ -69,10 +71,14 @@ export class GirlDetailPanel extends li_BaseView {
openUIDataCT(data) {
this.id = data;
}
videoAreaPos;
onLoadCT() {
super.onLoadCT();
this.imgItemInst.node.active = false;
this.videoAreaPos = this.videoArea.node.getWorldPosition();
this.node.scale = Vec3.ZERO;
tween(this.node).to(0.3, { scale: Vec3.ONE }).start();
// 检查 SceneBgVideoLayer 是否可用
if (!this.videoLayer) {
@@ -80,7 +86,7 @@ export class GirlDetailPanel extends li_BaseView {
} else {
console.log("GirlDetailPanel: 成功获取 SceneBgVideoLayer 实例");
// 初始化视频显示位置
this.initVideoPosition();
//this.initVideoPosition();
}
this.refresh(this.id);
@@ -182,16 +188,30 @@ export class GirlDetailPanel extends li_BaseView {
? new Size(this.videoArea.width, this.videoArea.height)
: new Size(600, 800);
// 获取 videoArea 的中心位置作为参考节点(使用世界坐标
const videoAreaPos = this.videoArea
? this.videoArea.node.getWorldPosition()
: undefined;
// 获取 videoArea 的中心位置,确保使用正确的世界坐标
const videoAreaPos = this.videoAreaPos;
if (this.videoArea) {
// 获取 videoArea 的世界坐标
this.videoAreaPos = this.videoArea.node.getWorldPosition();
// 也检查 videoLayer 的父节点信息,确保坐标系统一致
if (this.videoLayer && this.videoLayer.getVideoPlayer()) {
const videoPlayerParent =
this.videoLayer.getVideoPlayer().node.parent;
if (videoPlayerParent) {
const parentWorldPos = videoPlayerParent.getWorldPosition();
console.log(
`GirlDetailPanel: VideoPlayer 父节点世界位置: (${parentWorldPos.x}, ${parentWorldPos.y}, ${parentWorldPos.z})`
);
}
}
}
// 使用新的统一 playVideo 方法
this.videoLayer.playVideo(firstPath, {
loop: true,
size: targetSize,
position: videoAreaPos
position: videoAreaPos,
});
}
@@ -266,7 +286,7 @@ export class GirlDetailPanel extends li_BaseView {
// 使用 videoArea 的世界坐标位置
const videoAreaWorldPos = this.videoArea.node.getWorldPosition();
// 视频位置和缩放已在 playVideo 中处理
this.videoLayer.setVideoScale(1);
//this.videoLayer.setVideoScale(1);
console.log(
`GirlDetailPanel: 初始化视频世界位置到 (${videoAreaWorldPos.x}, ${videoAreaWorldPos.y})`
);
@@ -9,6 +9,7 @@ import { DataManager, DataId } from "../../data/DataManager";
import { GirlData } from "../../data/GirlData";
import proto from "db://assets/Scripts/proto/proto.pb.js";
import GameRootUI from "../../../Main/Common/GameRootUI";
import { UITransitionHelper } from "../../utils/UITransitionHelper";
const { ccclass, property } = _decorator;
@@ -23,7 +24,13 @@ export class GirlListPanel extends li_BaseView {
category: number;
openUIDataCT(data) {
this.category = data;
// 支持新的数据格式:既可以是单纯的数字,也可以是包含过渡动画信息的对象
this.category =
typeof data === "object" && data.category !== undefined
? data.category
: data;
const withAnimation = typeof data === "object" && data.withTransition;
}
onLoadCT() {
Utils.parseNode(this.node, this._nodeTab);
@@ -69,7 +76,37 @@ export class GirlListPanel extends li_BaseView {
GButton.BandClick(this._nodeTab.ReturnBtn, this.Return, this);
}
Return() {
this.onClose();
GameRootUI.I.showDefaultView();
console.log("GirlListPanel: Return button clicked");
// 执行同步的过渡动画:GirlListPanel向右滑出,ThemePanel从左滑入
const duration = 0.1;
let animationsCompleted = 0;
const totalAnimations = 2;
const onAnimationComplete = (source: string) => {
console.log(`GirlListPanel: Animation completed from ${source}`);
animationsCompleted++;
console.log(
`GirlListPanel: ${animationsCompleted}/${totalAnimations} animations completed`
);
if (animationsCompleted >= totalAnimations) {
console.log("GirlListPanel: All animations completed, closing panel");
// 所有动画完成后关闭当前面板
this.onClose();
}
};
// 1. GirlListPanel向右滑出
console.log("GirlListPanel: Starting slideOutToRight animation");
UITransitionHelper.slideOutToRight(this.node, duration, () =>
onAnimationComplete("slideOutToRight")
);
// 2. ThemePanel从左侧滑入
console.log("GirlListPanel: Starting ThemePanel slide_left animation");
GameRootUI.I.showDefaultViewWithTransition("slide_left", duration, () =>
onAnimationComplete("ThemePanel_slide_left")
);
}
}
@@ -7,6 +7,8 @@ import {
View,
Sprite,
UITransform,
Vec3,
tween,
} from "cc";
import li_BaseView from "../../../Main/Common/li_BaseView";
import Utils from "../../../Main/Common/Utils";
@@ -43,6 +45,9 @@ export class GirlListPopupPanel extends li_BaseView {
this.base = data.base;
}
onLoadCT() {
this.node.scale = Vec3.ZERO;
tween(this.node).to(0.3, { scale: Vec3.ONE }).start();
Utils.parseNode(this.node, this._nodeTab);
this.img = this._nodeTab.img.getComponent(Sprite);
this.uitransform = this._nodeTab.Img_Frame.getComponent(UITransform);
@@ -1,4 +1,13 @@
import { _decorator, Component, Node, Label, UITransform, Sprite } from "cc";
import {
_decorator,
Component,
Node,
Label,
UITransform,
Sprite,
tween,
Vec3,
} from "cc";
import li_BaseView from "../../../Main/Common/li_BaseView";
import { GButton } from "../../../Main/Common/GButton";
import Utils from "../../../Main/Common/Utils";
@@ -36,6 +45,8 @@ export class PopupGirlDetailPanel extends li_BaseView {
this.type = data.type;
}
onLoadCT() {
this.node.scale = Vec3.ZERO;
tween(this.node).to(0.3, { scale: Vec3.ONE }).start();
Utils.parseNode(this.node, this._nodeTab);
this.desc = this._nodeTab.content.getComponent(Label);
this.uitransform = this._nodeTab.Img_Frame.getComponent(UITransform);
@@ -12,7 +12,7 @@ import Utils from "db://assets/Scripts/Main/Common/Utils";
import { GButton } from "db://assets/Scripts/Main/Common/GButton";
import ResManager from "db://assets/Scripts/Main/Manager/ResManager";
import { ThemeItem } from "../../uiitems/ThemeItem";
import { NavigationManager } from "../../manager/NavigationManager";
import { NavigationManager, PageTransitionType } from "../../manager/NavigationManager";
import { GirlListItem } from "../../uiitems/GirlListItem";
import LanguageUtils from "../../../Main/Common/LanguageUtils";
import { ViewManager } from "../../../Main/Manager/ViewManager";
@@ -90,7 +90,7 @@ export class ThemePanel extends li_BaseView {
for (let id of themeIds) {
let newNode = instantiate(this.itemInst.node);
let item = newNode.getComponent(ThemeItem);
item.refresh(id);
item.refresh(id, this);
newNode.active = true;
this.cache.push(item);
this.content.addChild(newNode);
@@ -165,7 +165,8 @@ export class ThemePanel extends li_BaseView {
this.recId
) == 0;
if (isRelease || isFree) {
NavigationManager.Instance.navigateToGirlDetail(this.recId);
const transitionConfig = NavigationManager.getTransitionPresets().SLIDE_LEFT_TO_RIGHT;
NavigationManager.Instance.navigateToGirlDetail(this.recId, transitionConfig, this);
GameRootUI.I.hideDefaultView();
} else {
//未解锁
+9 -3
View File
@@ -2,7 +2,7 @@ import { _decorator, Component, Label, Node, Sprite, UITransform } from "cc";
import { GButton } from "db://assets/Scripts/Main/Common/GButton";
import ResManager from "db://assets/Scripts/Main/Manager/ResManager";
import Utils from "db://assets/Scripts/Main/Common/Utils";
import { NavigationManager } from "../manager/NavigationManager";
import { NavigationManager, PageTransitionType } from "../manager/NavigationManager";
import LanguageUtils from "../../Main/Common/LanguageUtils";
import { InnerMsgCode } from "../../Main/Config/InnerMsgCode";
import { DataManager, DataId } from "../data/DataManager";
@@ -29,6 +29,8 @@ export class ThemeItem extends Component {
private isLocked: boolean;
private parentPanel: any;
key: string;
protected onLoad(): void {
Utils.addInnerEL(InnerMsgCode.LanguageChange, this, () => {
@@ -41,8 +43,9 @@ export class ThemeItem extends Component {
});
}
refresh(themeId: number) {
refresh(themeId: number, parentPanel?: any) {
this.loading.active = true;
this.parentPanel = parentPanel;
// 主题数据
const themeData = DataManager.I.getDataById<ThemeData>(DataId.Theme);
this.lockImg.node.active = this.lockCover.node.active =
@@ -70,6 +73,9 @@ export class ThemeItem extends Component {
if (this.isLocked) {
return;
}
NavigationManager.Instance.navigateToGirlList(this.category);
// 使用预设的过渡动画配置
const transitionConfig = NavigationManager.getTransitionPresets().SLIDE_LEFT_TO_RIGHT;
NavigationManager.Instance.navigateToGirlList(this.category, transitionConfig, this.parentPanel);
}
}
+168 -183
View File
@@ -4,207 +4,192 @@ const { ccclass, property } = _decorator;
/**
* UI过渡动画帮助类
*
*
* 提供常用的UI过渡动画效果,如滑入滑出等
*
*
* @author AI Chat System
* @version 1.0.0
*/
@ccclass("UITransitionHelper")
export class UITransitionHelper {
/**
* 让节点从右侧滑入到原位置
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static slideInFromRight(node: Node, duration: number = 0.3, callback?: () => void): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for slideInFromRight");
return;
}
const screenWidth = view.getVisibleSize().width;
const originalPosition = node.getPosition();
// 设置初始位置为屏幕右侧外
node.setPosition(screenWidth, originalPosition.y, originalPosition.z);
// 执行滑入动画
tween(node)
.to(duration, { position: originalPosition }, {
easing: 'quartOut'
})
.call(() => {
if (callback) {
callback();
}
})
.start();
/**
* 让节点从右侧滑入到原位置
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static slideInFromRight(
node: Node,
duration: number = 0.1,
callback?: Function
): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for slideInFromRight");
return;
}
/**
* 让节点向左滑出到屏幕外
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static slideOutToLeft(node: Node, duration: number = 0.3, callback?: () => void): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for slideOutToLeft");
return;
}
const screenWidth = view.getVisibleSize().width;
const originalPosition = node.getPosition();
const screenWidth = view.getVisibleSize().width;
const currentPosition = node.getPosition();
const targetPosition = new Vec3(-screenWidth, currentPosition.y, currentPosition.z);
// 执行滑出动画
tween(node)
.to(duration, { position: targetPosition }, {
easing: 'quartOut'
})
.call(() => {
if (callback) {
callback();
}
})
.start();
// 设置初始位置为屏幕右侧外
node.setPosition(screenWidth, originalPosition.y, originalPosition.z);
// 执行滑入动画
tween(node)
.to(
duration,
{ position: originalPosition },
{
easing: "linear",
onUpdate: () => {
console.log(node.position);
},
}
)
.call(() => {
if (callback) {
callback();
}
})
.start();
}
/**
* 让节点向左滑出到屏幕外
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static slideOutToLeft(
node: Node,
duration: number = 0.1,
callback?: Function
): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for slideOutToLeft");
return;
}
/**
* 让节点从左侧滑入到原位置
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static slideInFromLeft(node: Node, duration: number = 0.3, callback?: () => void): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for slideInFromLeft");
return;
}
const screenWidth = view.getVisibleSize().width;
const currentPosition = node.getPosition();
const targetPosition = new Vec3(
-screenWidth,
currentPosition.y,
currentPosition.z
);
const screenWidth = view.getVisibleSize().width;
const originalPosition = node.getPosition();
// 设置初始位置为屏幕左侧外
node.setPosition(-screenWidth, originalPosition.y, originalPosition.z);
// 执行滑入动画
tween(node)
.to(duration, { position: originalPosition }, {
easing: 'quartOut'
})
.call(() => {
if (callback) {
callback();
}
})
.start();
// 执行滑出动画
tween(node)
.to(
duration,
{ position: targetPosition },
{
easing: "linear",
}
)
.call(() => {
if (callback) {
callback();
}
})
.start();
}
/**
* 让节点从左侧滑入到原位置
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static slideInFromLeft(
node: Node,
duration: number = 0.1,
callback?: Function
): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for slideInFromLeft");
return;
}
/**
* 让节点向右滑出到屏幕外
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static slideOutToRight(node: Node, duration: number = 0.3, callback?: () => void): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for slideOutToRight");
return;
}
const screenWidth = view.getVisibleSize().width;
const originalPosition = node.getPosition();
const screenWidth = view.getVisibleSize().width;
const currentPosition = node.getPosition();
const targetPosition = new Vec3(screenWidth, currentPosition.y, currentPosition.z);
// 执行滑出动画
tween(node)
.to(duration, { position: targetPosition }, {
easing: 'quartOut'
})
.call(() => {
if (callback) {
callback();
}
})
.start();
// 设置初始位置为屏幕左侧外
node.setPosition(-screenWidth, originalPosition.y, originalPosition.z);
// 执行滑入动画
tween(node)
.to(
duration,
{ position: originalPosition },
{
easing: "linear",
}
)
.call(() => {
if (callback) {
callback();
}
})
.start();
}
/**
* 让节点向右滑出到屏幕外
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static slideOutToRight(
node: Node,
duration: number = 0.1,
callback?: Function
): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for slideOutToRight");
return;
}
/**
* 淡入动画
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static fadeIn(node: Node, duration: number = 0.3, callback?: () => void): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for fadeIn");
return;
}
const screenWidth = view.getVisibleSize().width;
const currentPosition = node.getPosition();
const targetPosition = new Vec3(
screenWidth,
currentPosition.y,
currentPosition.z
);
// 设置初始透明度为0
if (node.setOpacity) {
node.setOpacity(0);
// 执行滑出动画
tween(node)
.to(
duration,
{ position: targetPosition },
{
easing: "linear",
}
// 执行淡入动画
tween(node)
.to(duration, { opacity: 255 }, {
easing: 'quartOut'
})
.call(() => {
if (callback) {
callback();
}
})
.start();
)
.call(() => {
if (callback) {
callback();
}
})
.start();
}
/**
* 停止节点上的所有动画
*
* @param {Node} node - 要停止动画的节点
*/
public static stopAllTweens(node: Node): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for stopAllTweens");
return;
}
/**
* 淡出动画
*
* @param {Node} node - 要执行动画的节点
* @param {number} duration - 动画持续时间(秒),默认0.3秒
* @param {Function} callback - 动画完成后的回调函数
*/
public static fadeOut(node: Node, duration: number = 0.3, callback?: () => void): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for fadeOut");
return;
}
// 执行淡出动画
tween(node)
.to(duration, { opacity: 0 }, {
easing: 'quartOut'
})
.call(() => {
if (callback) {
callback();
}
})
.start();
}
/**
* 停止节点上的所有动画
*
* @param {Node} node - 要停止动画的节点
*/
public static stopAllTweens(node: Node): void {
if (!node || !node.isValid) {
console.warn("UITransitionHelper: Invalid node for stopAllTweens");
return;
}
tween(node).stop();
}
}
tween(node).stop();
}
}