260 lines
7.1 KiB
C#
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|