123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- import {
- _decorator,
- Animation,
- Button,
- Component,
- EditBox,
- editorExtrasTag,
- find,
- isValid,
- js,
- Label,
- Layout,
- Node,
- ProgressBar,
- Quat,
- Slider,
- sp,
- Sprite,
- SpriteFrame,
- tween,
- UIOpacity,
- UITransform,
- Vec3,
- } from "cc";
- import { ScrollView } from "cc";
- import { Prefab } from "cc";
- import { instantiate } from "cc";
- import BaseUI from "./BaseUI";
- export default class UIUtils {
- static destroyNodeDelay(new_prop: Node, arg1: number) {
- setTimeout(() => {
- if (isValid(new_prop)) {
- new_prop.destroy();
- }
- }, arg1);
- }
- static removeEmptyNode(content: Node) {
- let emptyNode = content.getChildByName("EmptyNode");
- if (emptyNode) {
- emptyNode.destroy();
- }
- }
- static moveToNewParentAndAnim(parent: Node, animNode: Node, cb: Function) {
- UIUtils.moveToNewParent(parent, animNode);
- UIUtils.AnimBezierMoveAndScale(
- animNode,
- new Vec3(0, 0, 0),
- 0.2,
- 0.8,
- () => {
- cb();
- }
- );
- }
- static hideAnim(node: Node, arg1: () => void) {
- tween(node)
- .to(0.2, { scale: new Vec3(0, 0, 0) })
- .call(() => {
- node.active = false;
- node.setScale(new Vec3(1, 1, 1));
- arg1();
- })
- .start();
- }
- static closeUI(node: Node) {
- if (!isValid(node)) {
- return;
- }
- node.active = false;
- node.parent = null;
- node.destroy();
- }
- public static DeepFindChildByName(parentNode: Node, name: string): Node {
- if (!isValid(parentNode)) {
- return null;
- }
- return UIUtils._findInChildren(parentNode, name);
- }
- // 将一个node节点移动到新的父节点
- static moveToNewParent(parent: Node, child: Node) {
- // 先转换child的世界坐标位置
- let cp = child.getWorldPosition(new Vec3());
- // 更换父节点
- child.setParent(parent, false);
- // 转换世界坐标到新父节点的本地坐标系
- const parentUITrans = parent.getComponent(UITransform);
- // 设置新的本地坐标,使其屏幕位置不变
- const newP = parentUITrans.convertToNodeSpaceAR(cp);
- child.setPosition(newP);
- }
- /**
- * Layout子项目依次播放滑入动画
- * @param layout 布局节点
- * @param options 动画选项
- * @param callback 所有动画完成后的回调
- */
- static AnimateLayoutItems(
- layout: Node,
- options: {
- offsetX?: number;
- offsetY?: number;
- delayInterval?: number;
- duration?: number;
- scale?: number;
- easing?: string;
- staggerDirection?: "forward" | "reverse" | "random";
- onItemComplete?: (index: number, node: Node) => void;
- } = {},
- callback?: () => void
- ): void {
- if (!isValid(layout) || !layout.children || layout.children.length === 0) {
- if (callback) callback();
- return;
- }
- const { onItemComplete } = options;
- const children = [...layout.children];
- let completedCount = 0;
- const totalItems = children.length;
- // 先让所有子元素激活,让布局系统计算最终位置
- children.forEach((child) => {
- if (isValid(child)) {
- child.active = true;
- }
- });
- // 强制更新布局
- const layoutComp = layout.getComponent(Layout);
- if (layoutComp) {
- layoutComp.updateLayout();
- }
- // 等待布局更新完成后再开始动画
- setTimeout(() => {
- this.startSlideInAnimations(
- children,
- options,
- callback,
- completedCount,
- totalItems,
- onItemComplete
- );
- }, 0);
- }
- /**
- * 开始滑入动画
- */
- private static startSlideInAnimations(
- children: Node[],
- options: any,
- callback?: () => void,
- completedCount: number = 0,
- totalItems: number = 0,
- onItemComplete?: (index: number, node: Node) => void
- ): void {
- const {
- offsetX,
- offsetY,
- delayInterval,
- duration = 0.3,
- scale = 1,
- } = options;
- // 初始化所有子节点
- children.forEach((child, index) => {
- if (!isValid(child)) return;
- // 获取布局计算后的位置作为目标位置
- const targetPos = child.getPosition().clone();
- if (index < 8) {
- // 设置初始偏移位置,增加偏移量使动画更明显
- const startPos = new Vec3(
- targetPos.x + offsetX,
- targetPos.y +800,
- targetPos.z
- );
- child.setPosition(startPos);
- child.setScale(0.8, 0.8, 0.8);
- // 使用原始的DelayBezierMoveAndScale方法确保位置正确
- UIUtils.DelayBezierMoveAndScale(
- child,
- targetPos,
- scale,
- duration,
- delayInterval * index
- ).then(() => {
- completedCount++;
- if (onItemComplete) onItemComplete(index, child);
- if (completedCount >= totalItems && callback) {
- callback();
- }
- });
- } else {
- child.setPosition(targetPos);
- child.setScale(1, 1, 1);
- child.active = true;
- if (onItemComplete) onItemComplete(index, child);
- if (completedCount >= totalItems && callback) {
- callback();
- }
- }
- });
- }
- static AnimScaleShake(
- node: Node,
- scale: number = 1.2,
- duration: number = 0.6,
- repeatForever: boolean = true,
- callback?: () => void
- ) {
- const originalScale = node.getScale().clone();
- const scaleUp = Vec3.multiplyScalar(new Vec3(), originalScale, scale);
- const animation = tween(node)
- .to(
- duration / 2,
- { scale: originalScale.clone().multiplyScalar(scale) },
- { easing: "quadOut" }
- )
- .to(duration / 2, { scale: originalScale }, { easing: "quadIn" });
- if (repeatForever) {
- animation.repeatForever().start();
- } else {
- animation
- .call(() => {
- if (callback) callback();
- })
- .start();
- }
- }
- static async AnimShakeUpAndDownByTimes(node: Node, duration: number) {
- // 根据动画的次数,计算出动画的间隔时间
- return new Promise((resolve, reject) => {
- tween(node)
- .to(
- duration / 2,
- { position: new Vec3(0, 30, 0) },
- { easing: "quadOut" }
- )
- .to(
- duration / 2,
- { position: new Vec3(0, 150, 0) },
- { easing: "quadIn" }
- )
- .call(() => {
- resolve(node);
- })
- .start();
- });
- }
- static MoveXY(
- node: Node,
- moveX: number,
- moveY: number,
- duration: number,
- delay: number
- ) {
- let toPosition = node.getPosition();
- node.setPosition(toPosition.x + moveX, toPosition.y + moveY, toPosition.z);
- this.DelayBezierMoveAndScale(node, toPosition, 1, duration, delay);
- }
- static async DelayBezierMoveAndScale(
- node: Node,
- targetPos: Vec3,
- scale: number,
- duration: number,
- delay: number
- ) {
- await new Promise((resolve) => setTimeout(resolve, delay));
- return new Promise((resolve, reject) => {
- this.AnimBezierMoveAndScale(node, targetPos, scale, duration, () => {
- resolve(node);
- });
- });
- }
- static fadeIn(child: Node, duration: number) {
- if (!isValid(child)) {
- return;
- }
- let opacityComp = child.getComponent(UIOpacity);
- if (!opacityComp) {
- opacityComp = child.addComponent(UIOpacity);
- }
- child.setScale(0, 0);
- tween({ t: 0 })
- .to(
- duration,
- { t: 1 },
- {
- easing: "quadInOut",
- onUpdate: (obj) => {
- if (!isValid(child)) {
- return;
- }
- child.setScale(1 * obj.t, 1 * obj.t);
- opacityComp.opacity = 255 * obj.t;
- },
- }
- )
- .start();
- }
- static AnimBezierMoveAndTranslate(
- node: Node,
- targetPos: Vec3,
- scale: number,
- duration: number,
- callback?: Function
- ) {
- if (!isValid(node)) {
- return;
- }
- let opacityComp = node.getComponent(UIOpacity);
- if (!opacityComp) {
- opacityComp = node.addComponent(UIOpacity);
- }
- const startPos = node.getPosition();
- const startScale = node.scale.clone();
- const ctrl = new Vec3(
- (startPos.x + targetPos.x) / 2,
- Math.max(startPos.y, targetPos.y) + 150,
- (startPos.z + targetPos.z) / 2
- );
- const tempPos = new Vec3();
- const tempScale = new Vec3();
- tween({ t: 0 })
- .to(
- duration,
- { t: 1 },
- {
- easing: "quadInOut",
- onUpdate: (obj) => {
- if (!isValid(node)) {
- return;
- }
- const t = obj.t;
- const oneMinusT = 1 - t;
- // 贝塞尔曲线计算
- tempPos.x =
- oneMinusT * oneMinusT * startPos.x +
- 2 * t * oneMinusT * ctrl.x +
- t * t * targetPos.x;
- tempPos.y =
- oneMinusT * oneMinusT * startPos.y +
- 2 * t * oneMinusT * ctrl.y +
- t * t * targetPos.y;
- tempPos.z =
- oneMinusT * oneMinusT * startPos.z +
- 2 * t * oneMinusT * ctrl.z +
- t * t * targetPos.z;
- node.setPosition(tempPos);
- // 缩放插值计算
- tempScale.set(
- startScale.x + (scale - startScale.x) * t,
- startScale.y + (scale - startScale.y) * t,
- startScale.z + (scale - startScale.z) * t
- );
- node.setScale(tempScale);
- //设置透明度,越来越透明
- opacityComp.opacity = 255 * (1 - t);
- },
- }
- )
- .call(() => {
- if (callback) callback();
- })
- .start();
- }
- // 贝塞尔曲线实现移动和缩放动画
- static AnimBezierMoveAndScale(
- node: Node,
- targetPos: Vec3,
- scale: number,
- duration: number,
- callback?: Function,
- rotate?: number
- ) {
- if (!isValid(node)) {
- return;
- }
- const startPos = node.getPosition();
- const startScale = node.scale.clone();
- const ctrl = new Vec3(
- (startPos.x + targetPos.x) / 2,
- Math.max(startPos.y, targetPos.y) + 150,
- (startPos.z + targetPos.z) / 2
- );
- const tempPos = new Vec3();
- const tempScale = new Vec3();
- tween({ t: 0 })
- .to(
- duration,
- { t: 1 },
- {
- easing: "quadInOut",
- onUpdate: (obj) => {
- if (!isValid(node)) {
- return;
- }
- const t = obj.t;
- const oneMinusT = 1 - t;
- // 贝塞尔曲线计算
- tempPos.x =
- oneMinusT * oneMinusT * startPos.x +
- 2 * t * oneMinusT * ctrl.x +
- t * t * targetPos.x;
- tempPos.y =
- oneMinusT * oneMinusT * startPos.y +
- 2 * t * oneMinusT * ctrl.y +
- t * t * targetPos.y;
- tempPos.z =
- oneMinusT * oneMinusT * startPos.z +
- 2 * t * oneMinusT * ctrl.z +
- t * t * targetPos.z;
- node.setPosition(tempPos);
- // 缩放插值计算
- tempScale.set(
- startScale.x + (scale - startScale.x) * t,
- startScale.y + (scale - startScale.y) * t,
- startScale.z + (scale - startScale.z) * t
- );
- node.setScale(tempScale);
- if (rotate) {
- node.setRotationFromEuler(new Vec3(0, 0, rotate * t));
- }
- },
- }
- )
- .call(() => {
- if (callback) callback();
- })
- .start();
- }
- public static _findInChildren(node: Node, name: string): Node {
- var x = node.getChildByName(name);
- if (x) return x;
- if (node.children.length == 0) return null;
- for (var i = 0; i < node.children.length; ++i) {
- var tmp = this._findInChildren(node.children[i], name);
- if (tmp) return tmp;
- }
- return null;
- }
- }
- /*
- 使用示例:
- // 基本用法 - 滑入动画
- UIUtils.AnimateLayoutItems(layoutNode);
- // 自定义选项
- UIUtils.AnimateLayoutItems(layoutNode, {
- duration: 0.5,
- delayInterval: 0.15,
- scale: 1.1,
- easing: 'backOut',
- staggerDirection: 'reverse'
- }, () => {
- console.log('所有动画完成');
- });
- // 带单个项目完成回调
- UIUtils.AnimateLayoutItems(layoutNode, {
- onItemComplete: (index, node) => {
- console.log(`第${index}个项目动画完成`);
- }
- }, () => {
- console.log('所有项目动画完成');
- });
- */
|