NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/GameWrapper/ScrollRect_Adsorb.cs

204 lines
5.9 KiB
C#

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// 滑动列表扩展(吸附功能)
/// </summary>
public sealed class ScrollRect_Adsorb : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
/// <summary>
/// 吸附的速度阈值
/// </summary>
private const float SNAP_VELOCITY_THRESHOLD = 50f;
/// <summary>
/// 吸附的平滑速度
/// </summary>
private const float SNAP_SPEED = 50f;
/// <summary>
/// 吸附目标
/// </summary>
public Transform TsSnapTarget;
private ScrollRect scrollRect;
private RectTransform rtsContentPanel;
/// <summary>
/// 当前拖动状态
/// </summary>
private bool isDragging = false;
/// <summary>
/// 是否正在吸附
/// </summary>
private bool isSnapping = false;
private bool isSnap = false;
private int curIndex;
/// <summary>
/// 索引变化
/// </summary>
public Action<int> IndexChange;
private void Awake()
{
scrollRect = GetComponent<ScrollRect>();
rtsContentPanel = scrollRect.content;
isDragging = false;
curIndex = 0;
}
private void Update()
{
CheckChange();
// 如果用户没有在拖动,且滚动速度已低于阈值,则开始吸附
if (isSnap &&!isDragging && !isSnapping && scrollRect.velocity != Vector2.zero && Mathf.Abs(scrollRect.velocity.y) < SNAP_VELOCITY_THRESHOLD)
{
isSnap = false;
if (Vector2.zero != scrollRect.velocity)
scrollRect.velocity = Vector2.zero; // 立即停止惯性滚动
StartCoroutine(SnapToPosition());
}
// 如果用户开始拖动,则停止当前的吸附过程
else if (isDragging && isSnapping)
{
StopAllCoroutines();
isSnapping = false;
}
}
#region API
/// <summary>
/// 将指定索引的 cell 移动到 TsSnapTarget 的位置(平滑过渡)
/// </summary>
/// <param name="index">cell 的索引</param>
public void SnapToIndex(int index)
{
if (null == rtsContentPanel)
return;
if (null == TsSnapTarget)
return;
if (index < 0 || index >= rtsContentPanel.childCount)
return;
if (isSnapping)
{
StopAllCoroutines();
isSnapping = false;
}
StartCoroutine(SnapToIndexCoroutine(index));
}
#endregion
private void CheckChange()
{
if (Vector2.zero == scrollRect.velocity)
return;
if (null == TsSnapTarget)
return;
Transform tsSelect = null;
float minYDis = float.MaxValue;
int index = -1;
float targetLocalY = scrollRect.viewport.InverseTransformPoint(TsSnapTarget.position).y;
for (int i = 0; i < rtsContentPanel.childCount; ++i)
{
Transform tsCell = rtsContentPanel.GetChild(i);
float cellLocalY = scrollRect.viewport.InverseTransformPoint(tsCell.position).y;
float dis = cellLocalY - targetLocalY;
if (Mathf.Abs(dis) < Mathf.Abs(minYDis))
{
minYDis = dis;
tsSelect = tsCell;
index = i;
}
}
if (null != tsSelect)
{
curIndex = index;
IndexChange?.Invoke(curIndex);
}
}
private IEnumerator SnapToIndexCoroutine(int index)
{
isSnapping = true;
Transform tsCell = rtsContentPanel.GetChild(index);
float targetLocalY = scrollRect.viewport.InverseTransformPoint(TsSnapTarget.position).y;
float cellLocalY = scrollRect.viewport.InverseTransformPoint(tsCell.position).y;
float dis = cellLocalY - targetLocalY;
float dt = 0f;
Vector2 targetPosition = rtsContentPanel.anchoredPosition + new Vector2(0f, -dis);
while (dt < 0.2f && Vector2.Distance(rtsContentPanel.anchoredPosition, targetPosition) > 0.1f)
{
dt += Time.deltaTime;
rtsContentPanel.anchoredPosition = Vector2.Lerp(rtsContentPanel.anchoredPosition, targetPosition, Time.deltaTime * SNAP_SPEED);
yield return null;
}
rtsContentPanel.anchoredPosition = targetPosition;
isSnapping = false;
}
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
{
isDragging = true;
isSnap = true;
}
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{
isDragging = false;
}
IEnumerator SnapToPosition()
{
isSnapping = true;
if (null == TsSnapTarget)
{
isSnapping = false;
yield break;
}
Transform tsSelect = null;
float minYDis = float.MaxValue;
float targetLocalY = scrollRect.viewport.InverseTransformPoint(TsSnapTarget.position).y;
for (int i = 0; i < rtsContentPanel.childCount; ++i)
{
Transform tsCell = rtsContentPanel.GetChild(i);
float cellLocalY = scrollRect.viewport.InverseTransformPoint(tsCell.position).y;
float dis = cellLocalY - targetLocalY;
if (Mathf.Abs(dis) < Mathf.Abs(minYDis))
{
minYDis = dis;
tsSelect = tsCell;
}
}
if (null != tsSelect)
{
float dt = 0f;
Vector2 targetPosition = rtsContentPanel.anchoredPosition + new Vector2(0f, -minYDis);
while (dt < 0.2f && Vector2.Distance(rtsContentPanel.anchoredPosition, targetPosition) > 0.1f)
{
dt += Time.deltaTime;
rtsContentPanel.anchoredPosition = Vector2.Lerp(rtsContentPanel.anchoredPosition, targetPosition, Time.deltaTime * SNAP_SPEED);
yield return null;
}
rtsContentPanel.anchoredPosition = targetPosition;
}
isSnapping = false;
}
}