Files
Cielonos/Assets/Scripts/MainGame/UI/Common/NotificationItem.cs
SoulliesOfficial 39b43680a9 爆更
2026-07-18 03:16:20 -04:00

260 lines
7.1 KiB
C#

using System;
using DG.Tweening;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Cielonos.MainGame.UI
{
/// <summary>
/// 单条 Notification 的显示组件,只负责内容展示和动画。
/// </summary>
public class NotificationItem : MonoBehaviour
{
[SerializeField] private RectTransform rectTransform;
[SerializeField] private CanvasGroup canvasGroup;
[SerializeField] private TMP_Text titleText;
[SerializeField] private TMP_Text messageText;
[SerializeField] private Image iconImage;
[SerializeField] private Image accentImage;
[SerializeField] private float slideDistance = 180f;
private Sequence positionSequence;
private Sequence fadeSequence;
private Sequence lifetimeSequence;
private Action onExpired;
private float lifetimeDuration = 3f;
private void Awake()
{
CacheReferences();
}
private void OnDestroy()
{
KillAllSequences();
}
public void Setup(NotificationData data, float defaultDuration)
{
CacheReferences();
NormalizeRectTransform();
KillAllSequences();
if (titleText != null)
{
titleText.text = data.title;
}
if (messageText != null)
{
messageText.text = data.message;
}
if (iconImage != null)
{
iconImage.sprite = data.icon;
iconImage.enabled = data.icon != null;
}
if (accentImage != null)
{
accentImage.color = data.accentColor;
}
onExpired = null;
lifetimeDuration = data.duration > 0f ? data.duration : defaultDuration;
}
public void PlayIn(Vector2 target, float duration, Action expiredCallback)
{
onExpired = expiredCallback;
gameObject.SetActive(true);
SetCanvasVisible(false);
SetPosition(target + Vector2.right * slideDistance);
StartLifetime();
FadeTo(1f, duration, null);
MoveTo(target, duration, Ease.OutCubic);
}
public void MoveTo(Vector2 target, float duration)
{
MoveTo(target, duration, Ease.OutCubic);
}
public void SetPosition(Vector2 position)
{
if (rectTransform != null)
{
rectTransform.anchoredPosition = position;
}
}
public void PlayOut(Action completed, float duration)
{
if (!gameObject.activeSelf)
{
completed?.Invoke();
Destroy(gameObject);
return;
}
KillLifetimeSequence();
Vector2 target = rectTransform != null
? rectTransform.anchoredPosition + Vector2.right * slideDistance
: Vector2.right * slideDistance;
bool positionCompleted = rectTransform == null;
bool fadeCompleted = canvasGroup == null;
void TryComplete()
{
if (!positionCompleted || !fadeCompleted)
{
return;
}
completed?.Invoke();
Destroy(gameObject);
}
if (rectTransform != null)
{
MoveTo(target, duration, Ease.InCubic, () =>
{
positionCompleted = true;
TryComplete();
});
}
if (canvasGroup != null)
{
FadeTo(0f, duration, () =>
{
fadeCompleted = true;
TryComplete();
});
}
TryComplete();
}
public void DismissImmediate(Action completed)
{
KillAllSequences();
completed?.Invoke();
Destroy(gameObject);
}
private void StartLifetime()
{
KillLifetimeSequence();
lifetimeSequence = CreateBaseSequence()
.AppendInterval(Mathf.Max(0.1f, lifetimeDuration))
.OnComplete(() =>
{
Action callback = onExpired;
onExpired = null;
callback?.Invoke();
});
lifetimeSequence.Play();
}
private void MoveTo(Vector2 target, float duration, Ease ease, Action completed = null)
{
if (rectTransform == null)
{
completed?.Invoke();
return;
}
KillPositionSequence();
positionSequence = CreateBaseSequence()
.Join(rectTransform.DOAnchorPos(target, duration).SetEase(ease))
.OnComplete(() => completed?.Invoke());
positionSequence.Play();
}
private void FadeTo(float alpha, float duration, Action completed)
{
if (canvasGroup == null)
{
completed?.Invoke();
return;
}
KillFadeSequence();
fadeSequence = CreateBaseSequence()
.Join(canvasGroup.DOFade(alpha, duration))
.OnComplete(() => completed?.Invoke());
fadeSequence.Play();
}
private static Sequence CreateBaseSequence()
{
return DOTween.Sequence()
.SetUpdate(true)
.SetAutoKill(true)
.Pause();
}
private void KillAllSequences()
{
KillPositionSequence();
KillFadeSequence();
KillLifetimeSequence();
}
private void KillPositionSequence()
{
positionSequence?.Kill();
positionSequence = null;
}
private void KillFadeSequence()
{
fadeSequence?.Kill();
fadeSequence = null;
}
private void KillLifetimeSequence()
{
lifetimeSequence?.Kill();
lifetimeSequence = null;
}
private void CacheReferences()
{
rectTransform ??= GetComponent<RectTransform>();
canvasGroup ??= GetComponent<CanvasGroup>();
}
private void SetCanvasVisible(bool visible)
{
if (canvasGroup == null)
{
return;
}
canvasGroup.alpha = visible ? 1f : 0f;
canvasGroup.interactable = false;
canvasGroup.blocksRaycasts = false;
}
private void NormalizeRectTransform()
{
if (rectTransform == null)
{
return;
}
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
rectTransform.pivot = new Vector2(0.5f, 0.5f);
rectTransform.localScale = Vector3.one;
rectTransform.localRotation = Quaternion.identity;
}
}
}