TH1/Unity/Assets/Scripts/UI/Common/ChooseUISnapScrollRect.cs
2025-07-17 18:26:28 +08:00

124 lines
4.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
using Logic.CrashSight;
namespace UI.Common
{
[RequireComponent(typeof(ScrollRect))]
public class ChooseUISnapScrollRect : MonoBehaviour, IEndDragHandler
{
public RectTransform content; // 自动寻找 Viewport/List
public float snapSpeed = 10f; // 平滑速度
public float snapThreshold = 0.1f; // 靠近这个范围就停止
public float itemHeight = 60f; // 每项高度(固定)
private ScrollRect scrollRect;
private bool isSnapping = false;
private int itemCount = 0;
public int selectedIndex = -1;
void Start()
{
scrollRect = GetComponent<ScrollRect>();
scrollRect.horizontal = false;
scrollRect.vertical = true;
// 自动寻找 Viewport/List
if (content == null)
{
Transform viewport = transform.Find("Viewport");
if (viewport != null)
{
Transform list = viewport.Find("List");
if (list != null)
{
content = list.GetComponent<RectTransform>();
scrollRect.content = content;
}
else
{
LogSystem.LogError($"{name}: 找不到 Viewport/List");
}
}
else
{
LogSystem.LogError($"{name}: 找不到 Viewport");
}
}
if (content != null)
{
itemCount = content.childCount;
}
}
public void OnEndDrag(PointerEventData eventData)
{
if (!isSnapping && content != null)
{
StartCoroutine(SnapToClosestItem());
}
}
IEnumerator SnapToClosestItem()
{
isSnapping = true;
float currentY = content.anchoredPosition.y;
int closestIndex = Mathf.RoundToInt(currentY / itemHeight);
closestIndex = Mathf.Clamp(closestIndex, 0, itemCount - 1);
selectedIndex = closestIndex;
float targetY = closestIndex * itemHeight;
while (Mathf.Abs(content.anchoredPosition.y - targetY) > snapThreshold)
{
float newY = Mathf.Lerp(content.anchoredPosition.y, targetY, Time.deltaTime * snapSpeed);
content.anchoredPosition = new Vector2(content.anchoredPosition.x, newY);
yield return null;
}
content.anchoredPosition = new Vector2(content.anchoredPosition.x, targetY);
HighlightSelectedItem(closestIndex);
isSnapping = false;
}
private void HighlightSelectedItem(int selected)
{
for (int i = 0; i < itemCount; i++)
{
var option = content.GetChild(i);
var tmp = option.GetComponent<TMPro.TextMeshProUGUI>();
if (tmp != null)
{
tmp.color = (i == selected)
? new Color32(0xFF, 0xFF, 0xFF, 255) // #FFFFFF
: new Color32(0x78, 0x78, 0x78, 255); // #787878
continue;
}
var uiText = option.GetComponent<Text>();
if (uiText != null)
{
uiText.color = (i == selected)
? new Color32(0xFF, 0xFF, 0xFF, 255)
: new Color32(0x78, 0x78, 0x78, 255);
}
}
}
// 可手动吸附到某一项
public void SnapToIndex(int index)
{
index = Mathf.Clamp(index, 0, itemCount - 1);
float targetY = index * itemHeight;
content.anchoredPosition = new Vector2(content.anchoredPosition.x, targetY);
selectedIndex = index;
HighlightSelectedItem(index);
}
}
}