Files
2026-08-17 18:18:13 +08:00

126 lines
4.8 KiB
C#

#nullable enable
using System;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace ReplacedPerson.Runtime
{
[RequireComponent(typeof(RectTransform), typeof(Button))]
public sealed class ReplacedCardDragHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
private Canvas? _canvas;
private RectTransform? _ghost;
private ReplacedCardMotion? _motion;
private Action<Vector2>? _dropAction;
private Vector2 _dragStart;
private bool _hasDragStart;
public void Configure(Action<Vector2> dropAction)
{
_dropAction = dropAction;
}
public void OnBeginDrag(PointerEventData eventData)
{
CleanupGhost();
_dragStart = eventData.position;
_hasDragStart = true;
_canvas = GetComponentInParent<Canvas>()?.rootCanvas;
if (_canvas == null) return;
_motion = GetComponent<ReplacedCardMotion>();
_motion?.SetDragging(true);
_ghost = CreateGhost(_canvas.transform);
UpdateGhostPosition(eventData);
}
public void OnDrag(PointerEventData eventData)
{
UpdateGhostPosition(eventData);
}
public void OnEndDrag(PointerEventData eventData)
{
var moved = _hasDragStart && (eventData.position - _dragStart).sqrMagnitude >= 100f;
_motion?.SetDragging(false);
_motion = null;
CleanupGhost();
_hasDragStart = false;
if (moved && _dropAction != null) _dropAction(eventData.position);
else GetComponent<Button>().onClick.Invoke();
}
private RectTransform CreateGhost(Transform canvasRoot)
{
var sourceRect = (RectTransform)transform;
var sourceImage = GetComponent<Image>();
var sourceLabel = transform.Find("Label")?.GetComponent<TMP_Text>();
var ghostObject = new GameObject("CardDragGhost", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(CanvasGroup));
var ghostRect = ghostObject.GetComponent<RectTransform>();
ghostRect.SetParent(canvasRoot, false);
ghostRect.anchorMin = ghostRect.anchorMax = new Vector2(.5f, .5f);
ghostRect.pivot = sourceRect.pivot;
ghostRect.sizeDelta = sourceRect.rect.size;
ghostRect.localScale = Vector3.one * 1.06f;
ghostRect.SetAsLastSibling();
var ghostImage = ghostObject.GetComponent<Image>();
ghostImage.sprite = sourceImage.sprite;
ghostImage.type = sourceImage.type;
ghostImage.color = sourceImage.color;
ghostImage.material = sourceImage.material;
ghostImage.raycastTarget = false;
var group = ghostObject.GetComponent<CanvasGroup>();
group.alpha = .94f;
group.blocksRaycasts = false;
group.interactable = false;
if (sourceLabel != null)
{
var labelObject = new GameObject("Label", typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
var labelRect = labelObject.GetComponent<RectTransform>();
labelRect.SetParent(ghostRect, false);
labelRect.anchorMin = Vector2.zero;
labelRect.anchorMax = Vector2.one;
labelRect.offsetMin = new Vector2(8, 4);
labelRect.offsetMax = new Vector2(-8, -4);
var label = labelObject.GetComponent<TextMeshProUGUI>();
label.font = sourceLabel.font;
label.fontSize = sourceLabel.fontSize;
label.alignment = sourceLabel.alignment;
label.color = sourceLabel.color;
label.text = sourceLabel.text;
label.enableWordWrapping = sourceLabel.enableWordWrapping;
label.raycastTarget = false;
}
return ghostRect;
}
private void UpdateGhostPosition(PointerEventData eventData)
{
if (_ghost == null || _canvas == null) return;
var canvasRect = (RectTransform)_canvas.transform;
var camera = _canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : _canvas.worldCamera;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, eventData.position, camera, out var localPoint))
_ghost.anchoredPosition = localPoint;
}
private void OnDisable()
{
_motion?.SetDragging(false);
_motion = null;
_hasDragStart = false;
CleanupGhost();
}
private void CleanupGhost()
{
if (_ghost != null) Destroy(_ghost.gameObject);
_ghost = null;
_canvas = null;
}
}
}