using System; using System.Collections.Generic; using FairyGUI; using UnityEngine; namespace NBC { public enum UISlideDirection { Left, Right, Top, Bottom } public static class UITweenDynamic { private class TweenContext { public GObject target; public float alpha; public float scaleX; public float scaleY; public Vector3 position; public float rotation; public bool visible; public Action callback; } private static readonly Dictionary _contexts = new Dictionary(); private static TweenContext Capture(GObject target, Action callback) { return new TweenContext { target = target, alpha = target.alpha, scaleX = target.scaleX, scaleY = target.scaleY, position = target.position, rotation = target.rotation, visible = target.visible, callback = callback }; } // 启动新动画:如存在旧动画,先还原并调用旧回调,再杀死旧 tweens,注册新上下文 private static TweenContext StartNewAnimation(GObject target, Action newCallback) { if (target == null) return null; if (_contexts.TryGetValue(target, out var oldCtx)) { // 还原旧动画状态 if (oldCtx.target != null) { oldCtx.target.alpha = oldCtx.alpha; oldCtx.target.SetScale(oldCtx.scaleX, oldCtx.scaleY); oldCtx.target.position = oldCtx.position; oldCtx.target.rotation = oldCtx.rotation; oldCtx.target.visible = oldCtx.visible; } // 调用旧动画的回调 oldCtx.callback?.Invoke(); _contexts.Remove(target); } GTween.Kill(target); var ctx = Capture(target, newCallback); _contexts[target] = ctx; return ctx; } private static void FinishAnimation(GObject target) { if (target == null) return; if (_contexts.TryGetValue(target, out var ctx)) { _contexts.Remove(target); ctx.callback?.Invoke(); } } #region UIPanel UI动效 (wrappers) public static void TweenFadeShow(this T self, float duration = 0.45f, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenFadeShow(duration, callback); } public static void TweenFadeHide(this T self, float duration = 0.3f, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenFadeHide(duration, callback); } public static void TweenScaleShow(this T self, float duration = 0.3f, float fromScale = 0.8f, EaseType ease = EaseType.BackOut, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } // GComponent.TweenScaleShow signature: (duration, callback, fromScale, ease) pane.TweenScaleShow(duration, callback, fromScale, ease); } public static void TweenScaleHide(this T self, float duration = 0.2f, float toScale = 0.8f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } // GComponent.TweenScaleHide signature: (duration, callback, toScale, ease, resetAfter, setInvisible) pane.TweenScaleHide(duration, callback, toScale, ease, resetAfter, setInvisible); } public static void TweenPopShow(this T self, float duration = 0.2f, float fromScale = 0.8f, EaseType ease = EaseType.BackOut, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenPopShow(duration, callback, fromScale, ease); } public static void TweenPopHide(this T self, float duration = 0.2f, Action callback = null, float toScale = 0.8f, EaseType ease = EaseType.QuadIn, bool setInvisible = true) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenPopHide(duration, callback, toScale, ease, setInvisible); } public static void TweenSlideInLeft(this T self, float moveTime = 0.5f, float startFadeTimeRate = 0.42f, float startAlpha = 0, float alphaTime = 0.1f, float extraOffset = 0f, EaseType ease = EaseType.CubicOut, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideInLeft(moveTime, startFadeTimeRate, startAlpha, alphaTime, extraOffset, ease, callback); } public static void TweenSlideOutLeft(this T self, float moveTime = 0.3f, float startFadeTimeRate = 0, float alphaTime = 0.08f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideOutLeft(moveTime, startFadeTimeRate, alphaTime, extraOffset, ease, resetAfter, setInvisible, callback); } public static void TweenSlideInRight(this T self, float moveTime = 0.5f, float startFadeTimeRate = 0.42f, float startAlpha = 0, float alphaTime = 0.1f, float extraOffset = 0f, EaseType ease = EaseType.CubicOut, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideInRight(moveTime, startFadeTimeRate, startAlpha, alphaTime, extraOffset, ease, callback); } public static void TweenSlideOutRight(this T self, float moveTime = 0.3f, float startFadeTimeRate = 0, float alphaTime = 0.08f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideOutRight(moveTime, startFadeTimeRate, alphaTime, extraOffset, ease, resetAfter, setInvisible, callback); } public static void TweenSlideInTop(this T self, float moveTime = 0.35f, float startFadeTimeRate = 0.3f, float startAlpha = 0, float alphaTime = 0.1f, float extraOffset = 0f, EaseType ease = EaseType.CubicOut, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideInTop(moveTime, startFadeTimeRate, startAlpha, alphaTime, extraOffset, ease, callback); } public static void TweenSlideOutTop(this T self, float moveTime = 0.3f, float startFadeTimeRate = 0, float alphaTime = 0.1f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideOutTop(moveTime, startFadeTimeRate, alphaTime, extraOffset, ease, resetAfter, setInvisible, callback); } public static void TweenSlideInBottom(this T self, float moveTime = 0.35f, float startFadeTimeRate = 0.3f, float startAlpha = 0, float alphaTime = 0.1f, float extraOffset = 0f, EaseType ease = EaseType.CubicOut, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideInBottom(moveTime, startFadeTimeRate, startAlpha, alphaTime, extraOffset, ease, callback); } public static void TweenSlideOutBottom(this T self, float moveTime = 0.3f, float startFadeTimeRate = 0, float alphaTime = 0.1f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideOutBottom(moveTime, startFadeTimeRate, alphaTime, extraOffset, ease, resetAfter, setInvisible, callback); } public static void TweenSlideIn(this T self, UISlideDirection direction, float moveTime = 0.35f, float startFadeTimeRate = 0.1f, float startAlpha = 0, float alphaTime = 0.2f, float extraOffset = 0f, EaseType ease = EaseType.QuadOut, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideIn(direction, moveTime, startFadeTimeRate, startAlpha, alphaTime, extraOffset, ease, callback); } public static void TweenSlideOut(this T self, UISlideDirection direction, float moveTime = 0.35f, float startFadeTimeRate = 0.1f, float alphaTime = 0.2f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenSlideOut(direction, moveTime, startFadeTimeRate, alphaTime, extraOffset, ease, resetAfter, setInvisible, callback); } public static void TweenBounceShow(this T self, float duration = 0.4f, Action callback = null, float fromYOffset = -100f) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenBounceShow(duration, callback, fromYOffset); } public static void TweenBounceHide(this T self, float duration = 0.25f, Action callback = null, float toYOffset = 100f, bool resetAfter = true, bool setInvisible = true) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenBounceHide(duration, callback, toYOffset, resetAfter, setInvisible); } public static void TweenRotateShow(this T self, float duration = 0.2f, Action callback = null, float fromRotation = -90f, EaseType ease = EaseType.BackOut) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenRotateShow(duration, callback, fromRotation, ease); } public static void TweenRotateHide(this T self, float duration = 0.2f, Action callback = null, float toRotation = -90f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenRotateHide(duration, callback, toRotation, ease, resetAfter, setInvisible); } public static void TweenShake(this T self, float duration = 0.4f, Action callback = null, float amplitude = 12f, bool horizontal = true, int vibrato = 6) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenShake(duration, callback, amplitude, horizontal, vibrato); } public static void TweenShakeShow(this T self, float durationScale = 0.2f, float ScaleStart = 0.3f, float ScaleEnd = 1f, int shake = 4, float shakeRotation = 18f, float duration = 1, Action callback = null) where T : IUIPanel { var pane = self?.ContentPane; if (pane == null) { callback?.Invoke(); return; } pane.TweenShakeShow(durationScale, ScaleStart, ScaleEnd, shake, shakeRotation, duration, callback); } #endregion #region GComponent UI动效 public static void TweenFadeShow(this GObject self, float duration = 0.45f, Action callback = null) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); self.alpha = 0f; self.TweenFade(1f, duration).OnComplete(() => { FinishAnimation(self); }); } public static void TweenFadeHide(this GObject self, float duration = 0.3f, Action callback = null) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); self.alpha = 1f; self.TweenFade(0f, duration).OnComplete(() => { FinishAnimation(self); }); } public static void TweenScaleShow(this GObject self, float duration = 0.3f, Action callback = null, float fromScale = 0f, EaseType ease = EaseType.BackOut) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); self.SetPivot(0.5f, 0.5f, false); self.SetScale(0, 0); var ox = self.scaleX; var oy = self.scaleY; self.visible = true; self.SetScale(fromScale, fromScale); self.TweenScale(new Vector2(ox, oy), duration).SetEase(ease).OnComplete(() => { FinishAnimation(self); }); } public static void TweenScaleHide(this GObject self, float duration = 0.2f, Action callback = null, float toScale = 0.8f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); self.SetPivot(0.5f, 0.5f, false); self.SetScale(1, 1); var ox = self.scaleX; var oy = self.scaleY; self.TweenScale(new Vector2(toScale, toScale), duration).SetEase(ease).OnComplete(() => { if (resetAfter) self.SetScale(ox, oy); if (setInvisible) self.visible = false; FinishAnimation(self); }); } public static void TweenPopShow(this GObject self, float duration = 0.2f, Action callback = null, float fromScale = 0.8f, EaseType ease = EaseType.BackOut) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); var ox = self.scaleX; var oy = self.scaleY; self.visible = true; self.SetScale(fromScale, fromScale); self.alpha = 0f; int remain = 2; void Done() { if (--remain == 0) FinishAnimation(self); } self.TweenScale(new Vector2(ox, oy), duration).SetEase(ease).OnComplete(Done); self.TweenFade(1f, duration * 0.9f).SetEase(EaseType.QuadOut).OnComplete(Done); } public static void TweenPopHide(this GObject self, float duration = 0.2f, Action callback = null, float toScale = 0.8f, EaseType ease = EaseType.QuadIn, bool setInvisible = true) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); var ox = self.scaleX; var oy = self.scaleY; int remain = 2; void Done() { if (--remain == 0) { self.SetScale(ox, oy); if (setInvisible) self.visible = false; FinishAnimation(self); } } self.TweenScale(new Vector2(toScale, toScale), duration).SetEase(ease).OnComplete(Done); self.TweenFade(0f, duration * 0.9f).SetEase(EaseType.QuadIn).OnComplete(Done); } public static void TweenSlideInLeft(this GObject self, float moveTime = 0.5f, float startFadeTimeRate = 0.42f , float startAlpha = 0, float alphaTime = 0.1f , float extraOffset = 0f, EaseType ease = EaseType.CubicOut, Action callback = null) { TweenSlideIn(self, UISlideDirection.Left, moveTime, startFadeTimeRate, startAlpha, alphaTime, extraOffset, ease, callback); } public static void TweenSlideOutLeft(this GObject self, float moveTime = 0.3f, float startFadeTimeRate = 0 , float alphaTime = 0.08f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) { TweenSlideOut(self, UISlideDirection.Left, moveTime, startFadeTimeRate, alphaTime, extraOffset, ease, resetAfter, setInvisible, callback); } public static void TweenSlideInRight(this GObject self, float moveTime = 0.5f, float startFadeTimeRate = 0.42f , float startAlpha = 0, float alphaTime = 0.1f , float extraOffset = 0f, EaseType ease = EaseType.CubicOut, Action callback = null) { TweenSlideIn(self, UISlideDirection.Right, moveTime, startFadeTimeRate, startAlpha, alphaTime, extraOffset, ease, callback); } public static void TweenSlideOutRight(this GObject self, float moveTime = 0.3f, float startFadeTimeRate = 0 , float alphaTime = 0.08f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) { TweenSlideOut(self, UISlideDirection.Right, moveTime, startFadeTimeRate, alphaTime, extraOffset, ease, resetAfter, setInvisible, callback); } public static void TweenSlideInTop(this GObject self, float moveTime = 0.35f, float startFadeTimeRate = 0.3f , float startAlpha = 0, float alphaTime = 0.1f , float extraOffset = 0f, EaseType ease = EaseType.CubicOut, Action callback = null) { TweenSlideIn(self, UISlideDirection.Top, moveTime, startFadeTimeRate, startAlpha, alphaTime, extraOffset, ease, callback); } public static void TweenSlideOutTop(this GObject self, float moveTime = 0.3f, float startFadeTimeRate = 0 , float alphaTime = 0.1f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) { TweenSlideOut(self, UISlideDirection.Top, moveTime, startFadeTimeRate, alphaTime, extraOffset, ease, resetAfter, setInvisible, callback); } public static void TweenSlideInBottom(this GObject self, float moveTime = 0.35f, float startFadeTimeRate = 0.3f , float startAlpha = 0, float alphaTime = 0.1f , float extraOffset = 0f, EaseType ease = EaseType.CubicOut, Action callback = null) { TweenSlideIn(self, UISlideDirection.Bottom, moveTime, startFadeTimeRate, startAlpha, alphaTime, extraOffset, ease, callback); } public static void TweenSlideOutBottom(this GObject self, float moveTime = 0.3f, float startFadeTimeRate = 0 , float alphaTime = 0.1f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) { TweenSlideOut(self, UISlideDirection.Bottom, moveTime, startFadeTimeRate, alphaTime, extraOffset, ease, resetAfter, setInvisible, callback); } public static void TweenSlideIn(this GObject self, UISlideDirection direction, float moveTime = 0.35f, float startFadeTimeRate = 0.1f , float startAlpha = 0, float alphaTime = 0.2f , float extraOffset = 0f, EaseType ease = EaseType.QuadOut, Action callback = null) { if (self == null || moveTime <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); var ori = self.position; float startX = ori.x; float startY = ori.y; switch (direction) { case UISlideDirection.Left: startX = startX + (-self.width / 2) - Math.Max(0f, extraOffset); break; case UISlideDirection.Right: startX = startX + (self.width / 2) + Math.Max(0f, extraOffset); break; case UISlideDirection.Top: startY = startY + (-self.height / 2) - Math.Max(0f, extraOffset); break; case UISlideDirection.Bottom: startY = startY + (self.height / 2) + Math.Max(0f, extraOffset); break; } self.visible = true; self.alpha = startAlpha; self.position = new Vector3(startX, startY, ori.z); float startFadeTime = moveTime * startFadeTimeRate; int remain = 3; void Done() { if (--remain == 0) FinishAnimation(self); } GTweener moveTweener = (direction == UISlideDirection.Left || direction == UISlideDirection.Right) ? self.TweenMoveX(ori.x, moveTime) : self.TweenMoveY(ori.y, moveTime); moveTweener.SetEase(ease).OnComplete(Done); self.TweenFade(startAlpha, startFadeTime).OnComplete(() => { Done(); self.TweenFade(1, alphaTime).SetEase(EaseType.QuadOut).OnComplete(Done); }); } public static void TweenSlideOut(this GObject self, UISlideDirection direction, float moveTime = 0.35f, float startFadeTimeRate = 0.1f , float alphaTime = 0.2f, float extraOffset = 0f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true, Action callback = null) { if (self == null || moveTime <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); var ori = self.position; float endX = ori.x; float endY = ori.y; switch (direction) { case UISlideDirection.Left: endX = endX + (-self.width / 2) - Math.Max(0f, extraOffset); break; case UISlideDirection.Right: endX = endX + (self.width / 2) + Math.Max(0f, extraOffset); break; case UISlideDirection.Top: endY = endX + (-self.height / 2) - Math.Max(0f, extraOffset); break; case UISlideDirection.Bottom: endY = endX + (self.height / 2) + Math.Max(0f, extraOffset); break; } float startAlpha = self.alpha; float startFadeTime = moveTime * startFadeTimeRate; int remain = 3; void Done() { if (--remain == 0) { if (resetAfter) self.position = ori; if (setInvisible) self.visible = false; FinishAnimation(self); } } GTweener moveTweener = (direction == UISlideDirection.Left || direction == UISlideDirection.Right) ? self.TweenMoveX(endX, moveTime) : self.TweenMoveY(endY, moveTime); moveTweener.SetEase(ease).OnComplete(Done); self.TweenFade(startAlpha, startFadeTime).OnComplete(() => { Done(); self.TweenFade(0, alphaTime).SetEase(EaseType.QuadOut).OnComplete(Done); }); } public static void TweenBounceShow(this GObject self, float duration = 0.4f, Action callback = null, float fromYOffset = -100f) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); var ori = self.position; self.visible = true; self.position = new Vector3(ori.x, ori.y + fromYOffset, ori.z); self.TweenMoveY(ori.y, duration).SetEase(EaseType.BounceOut).OnComplete(() => { FinishAnimation(self); }); } public static void TweenBounceHide(this GObject self, float duration = 0.25f, Action callback = null, float toYOffset = 100f, bool resetAfter = true, bool setInvisible = true) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); var ori = self.position; self.TweenMoveY(ori.y + toYOffset, duration).SetEase(EaseType.QuadIn).OnComplete(() => { if (resetAfter) self.position = ori; if (setInvisible) self.visible = false; FinishAnimation(self); }); } public static void TweenRotateShow(this GObject self, float duration = 0.2f, Action callback = null, float fromRotation = -90f, EaseType ease = EaseType.BackOut) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); float ori = self.rotation; self.visible = true; self.rotation = fromRotation; self.TweenRotate(ori, duration).SetEase(ease).OnComplete(() => { FinishAnimation(self); }); } public static void TweenRotateHide(this GObject self, float duration = 0.2f, Action callback = null, float toRotation = -90f, EaseType ease = EaseType.QuadIn, bool resetAfter = true, bool setInvisible = true) { if (self == null || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); float ori = self.rotation; self.TweenRotate(toRotation, duration).SetEase(ease).OnComplete(() => { if (resetAfter) self.rotation = ori; if (setInvisible) self.visible = false; FinishAnimation(self); }); } public static void TweenShake(this GObject self, float duration = 0.4f, Action callback = null, float amplitude = 12f, bool horizontal = true, int vibrato = 6) { if (self == null || vibrato <= 0 || amplitude <= 0f || duration <= 0f) { callback?.Invoke(); return; } StartNewAnimation(self, callback); var ori = self.position; float single = duration / (vibrato * 2f); var end = horizontal ? new Vector3(ori.x + amplitude, ori.y, ori.z) : new Vector3(ori.x, ori.y + amplitude, ori.z); self.TweenMove(end, single).SetEase(EaseType.Linear).SetRepeat(vibrato * 2, true).OnComplete(() => { self.position = ori; FinishAnimation(self); }); } public static void TweenShakeShow(this GObject self, float durationScale = 0.2f, float ScaleStart = 0.3f, float ScaleEnd = 1f, int shake = 4, float shakeRotation = 18f, float duration = 1, Action callback = null) { if (self == null) { callback?.Invoke(); return; } StartNewAnimation(self, callback); int remain = 4; void Done() { if (--remain == 0) { FinishAnimation(self); } } self.scale = Vector2.one * ScaleStart; self.rotation = -shakeRotation; float scaleEndTime = 0.2f; float rotaEndTime = 0.1f; float allIns = durationScale + scaleEndTime + rotaEndTime; duration = allIns > duration ? allIns : duration; float rotaTime = (duration - durationScale - scaleEndTime - rotaEndTime) / shake; self.TweenScale(Vector2.one * ScaleEnd * 1.1f, durationScale) .OnComplete(() => { FinishAnimation(self); self.TweenRotate(shakeRotation, rotaTime).SetRepeat(shake, true) .OnComplete(() => { FinishAnimation(self); self.TweenRotate(0, rotaEndTime).OnComplete(() => { FinishAnimation(self); self.TweenScale(Vector2.one * ScaleEnd, scaleEndTime).OnComplete(() => { FinishAnimation(self); }); }); }); }); } #endregion } }