|
@@ -1,79 +1,150 @@
|
|
|
-import { Node, Sprite, tween, UIOpacity, Vec3, Widget } from "cc";
|
|
|
+import { Node, Sprite, Tween, tween, UIOpacity, Vec3, view, Widget } from "cc";
|
|
|
import { gui } from "../../core/ui/ui";
|
|
|
|
|
|
//单个节点进场动画,退场动画
|
|
|
export function toggleSlideOutAndBack(t: Node, offset: Vec3, duration: number = 0.5, onFinish?: () => void) {
|
|
|
if (!t['__originPos']) {
|
|
|
- t['__originPos'] = t.position.clone();
|
|
|
+ t['__originPos'] = t.position.clone(); // 初始位置
|
|
|
t['__isHidden'] = false;
|
|
|
}
|
|
|
|
|
|
const originPos: Vec3 = t['__originPos'];
|
|
|
- const isHidden: boolean = t['__isHidden'];
|
|
|
|
|
|
- const targetPos = isHidden ? originPos.clone() : originPos.clone().add(offset);
|
|
|
+ if (t['__targetHidden'] === undefined) {
|
|
|
+ t['__targetHidden'] = t['__isHidden'];
|
|
|
+ }
|
|
|
|
|
|
- tween(t)
|
|
|
- .to(duration, { position: targetPos }, { easing: "quadInOut" })
|
|
|
+ const isHidden = t['__isHidden'];
|
|
|
+ const targetHidden = !t['__targetHidden']; // 翻转目标
|
|
|
+ t['__targetHidden'] = targetHidden; // 标记目标
|
|
|
+
|
|
|
+ const targetPos = targetHidden ? originPos.clone().add(offset) : originPos.clone();
|
|
|
+
|
|
|
+ // 停止旧动画(彻底打断)
|
|
|
+ if (t['__slideTween']) {
|
|
|
+ t['__slideTween'].stop();
|
|
|
+ t['__slideTween'] = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentPos = t.position.clone();
|
|
|
+ const posDelta = targetPos.clone().subtract(currentPos).length();
|
|
|
+ const isReversing = posDelta > 1 && isHidden !== targetHidden;
|
|
|
+ const actualDuration = isReversing ? duration * 0.4 : duration;
|
|
|
+
|
|
|
+ const tweenInst = tween(t)
|
|
|
+ .to(actualDuration, { position: new Vec3().set(targetPos) }, { easing: "quadInOut" })
|
|
|
.call(() => {
|
|
|
- t['__isHidden'] = !isHidden;
|
|
|
- if (onFinish) onFinish();
|
|
|
- })
|
|
|
- .start();
|
|
|
+ // 动画未被中断才更新最终状态
|
|
|
+ if (t['__targetHidden'] === targetHidden) {
|
|
|
+ t['__isHidden'] = targetHidden;
|
|
|
+ t['__slideTween'] = null;
|
|
|
+ onFinish?.();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ t['__slideTween'] = tweenInst;
|
|
|
+ tweenInst.start();
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
//节点下所有子节点进场动画,退场动画
|
|
|
export function toggleSlideOutAndBack_Nodes(t: Node, offset: Vec3, duration: number = 0.5, onFinish?: () => void) {
|
|
|
- t.children.forEach(node => {
|
|
|
- toggleSlideOutAndBack(node, offset, duration, onFinish);
|
|
|
+ const children = t.children;
|
|
|
+ if (children.length === 0) {
|
|
|
+ onFinish?.();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let completedCount = 0;
|
|
|
+
|
|
|
+ children.forEach((node) => {
|
|
|
+ toggleSlideOutAndBack(node, offset, duration, () => {
|
|
|
+ completedCount++;
|
|
|
+ if (completedCount === children.length) {
|
|
|
+ onFinish?.();
|
|
|
+ }
|
|
|
+ });
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-//界面过渡动画,淡入淡出以及滑动感
|
|
|
+
|
|
|
export function toggleSlideInAndOut(t: Node, duration: number = 0.5, onFinish?: () => void) {
|
|
|
if (!t['__originPos']) {
|
|
|
t['__originPos'] = t.position.clone();
|
|
|
}
|
|
|
|
|
|
+ console.log(view.getVisibleSize())
|
|
|
const originPos: Vec3 = t['__originPos'];
|
|
|
- const isHidden: boolean = t['__isHidden'] ?? false;
|
|
|
- const targetPos = isHidden ? originPos : originPos.clone().add(new Vec3(0, 1334, 0));
|
|
|
+ const showPos = originPos.clone().add(new Vec3(0, 1334, 0));
|
|
|
|
|
|
let uiOpacity = t.getComponent(UIOpacity);
|
|
|
if (!uiOpacity) {
|
|
|
uiOpacity = t.addComponent(UIOpacity);
|
|
|
+ uiOpacity.opacity = 0;
|
|
|
}
|
|
|
|
|
|
- const startOpacity = isHidden ? 255 : 0;
|
|
|
- const endOpacity = isHidden ? 0 : 255;
|
|
|
+ // 初始化状态记录
|
|
|
+ if (t['__isHidden'] === undefined) {
|
|
|
+ t['__isHidden'] = true;
|
|
|
+ }
|
|
|
+ if (t['__targetHidden'] === undefined) {
|
|
|
+ t['__targetHidden'] = t['__isHidden'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算新的目标状态(翻转)
|
|
|
+ const nextTargetHidden = !t['__targetHidden'];
|
|
|
+ t['__targetHidden'] = nextTargetHidden;
|
|
|
+
|
|
|
+ const targetPos = nextTargetHidden ? originPos : showPos;
|
|
|
+ const targetOpacity = nextTargetHidden ? 0 : 255;
|
|
|
|
|
|
- uiOpacity.opacity = startOpacity;
|
|
|
+ // 当前状态
|
|
|
+ const currentPos = t.position.clone();
|
|
|
+ const currentOpacity = uiOpacity.opacity;
|
|
|
+ const posDiff = targetPos.clone().subtract(currentPos).length();
|
|
|
+ const isMidTween = currentOpacity > 0 && currentOpacity < 255 && posDiff > 0;
|
|
|
+ const actualDuration = isMidTween ? duration * 0.4 : duration;
|
|
|
+
|
|
|
+ // 停止旧动画
|
|
|
+ Tween.stopAllByTarget(t);
|
|
|
+ Tween.stopAllByTarget(uiOpacity);
|
|
|
|
|
|
tween(t)
|
|
|
.parallel(
|
|
|
- tween(t).to(duration, { position: targetPos }, { easing: "quadOut" }),
|
|
|
- tween(uiOpacity).to(duration, { opacity: endOpacity }, { easing: "quadOut" })
|
|
|
+ tween(t).to(actualDuration, { position: targetPos.clone() }, { easing: 'quadInOut' }),
|
|
|
+ tween(uiOpacity).to(actualDuration, { opacity: targetOpacity }, { easing: 'quadInOut' })
|
|
|
)
|
|
|
.call(() => {
|
|
|
- t['__isHidden'] = !isHidden;
|
|
|
- if (onFinish) onFinish();
|
|
|
+ // 只有目标状态未被再次更改时,才确认最终状态
|
|
|
+ if (t['__targetHidden'] === nextTargetHidden) {
|
|
|
+ t['__isHidden'] = nextTargetHidden;
|
|
|
+ onFinish?.();
|
|
|
+ }
|
|
|
})
|
|
|
.start();
|
|
|
}
|
|
|
|
|
|
+
|
|
|
//呼吸动画
|
|
|
export function toggleBreath(t: Node, duration: number = 0.5, onFinish?: () => void) {
|
|
|
+ if (!t['__originalScale']) {
|
|
|
+ t['__originalScale'] = t.scale.clone(); // 缓存初始缩放
|
|
|
+ }
|
|
|
+ const originalScale: Vec3 = t['__originalScale'];
|
|
|
+
|
|
|
+ //停止之前的 tween,并还原缩放
|
|
|
if (t['__breathTween']) {
|
|
|
t['__breathTween'].stop();
|
|
|
+ t.scale = originalScale.clone(); //强制还原到初始 scale
|
|
|
}
|
|
|
|
|
|
- const originalScale = t.scale.clone();
|
|
|
const scaleUp = originalScale.clone().multiplyScalar(1.1);
|
|
|
const scaleDown = originalScale;
|
|
|
|
|
|
const breathTween = tween(t)
|
|
|
.repeatForever(
|
|
|
- tween(t) // ✅ 一整段循环体
|
|
|
+ tween(t)
|
|
|
.to(duration, { scale: scaleUp }, { easing: 'sineInOut' })
|
|
|
.to(duration, { scale: scaleDown }, { easing: 'sineInOut' })
|
|
|
.call(() => {
|
|
@@ -86,6 +157,7 @@ export function toggleBreath(t: Node, duration: number = 0.5, onFinish?: () => v
|
|
|
breathTween.start();
|
|
|
}
|
|
|
|
|
|
+
|
|
|
//提醒动画
|
|
|
export function toggleRemind(t: Node, duration: number = 0.2, delay: number = 1.5, onFinish?: () => void) {
|
|
|
if (!t['__originScale']) {
|
|
@@ -147,7 +219,7 @@ export function toggleMoveBy(t: Node, offset: Vec3, duration: number = 0.5, onFi
|
|
|
|
|
|
if (t['__moveTween']) {
|
|
|
t['__moveTween'].stop();
|
|
|
- t['__moveTween'] = null;
|
|
|
+ t['__moveTween'] = null;
|
|
|
}
|
|
|
|
|
|
const originPos: Vec3 = t['__originPos'];
|
|
@@ -162,14 +234,14 @@ export function toggleMoveBy(t: Node, offset: Vec3, duration: number = 0.5, onFi
|
|
|
});
|
|
|
|
|
|
t['__moveTween'] = moveTween;
|
|
|
- moveTween.start();
|
|
|
+ moveTween.start();
|
|
|
}
|
|
|
|
|
|
//图片填充动画 未完善
|
|
|
-export function fillImage(t: Node, duration: number, target:number,onFinish?: Function) {
|
|
|
+export function fillImage(t: Node, duration: number, target: number, onFinish?: Function) {
|
|
|
let sprite = t.getComponent(Sprite);
|
|
|
tween(sprite)
|
|
|
- .to(duration, { fillRange: target },{ easing: 'quadOut' })
|
|
|
+ .to(duration, { fillRange: target }, { easing: 'quadOut' })
|
|
|
.start();
|
|
|
}
|
|
|
|