ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置
程序员文章站
2022-05-30 17:38:53
...
效果如下:
接下贴各个gameobject
核心代码
接下贴各个gameobject
public Toggle _CaptureToggle;
public Toggle _RecordToggle;
public RectTransform _ParentTranform;
public LayoutElement _CaptureLayoutElement;
public LayoutElement _RecordLayoutElement;
protected ScrollSnap _ScrollSnap;
void Awake()
{
_ScrollSnap = GetComponentInChildren<ScrollSnap>();
TimerManager.Instance.StartCoroutine(WaitForSetLayout());
}
//初始化并定位每个gameobject的中心位置
IEnumerator WaitForSetLayout()
{
//yield return new WaitForSeconds(0.5f);
yield return new WaitForEndOfFrame();
if (_ParentTranform.rect.width != _CaptureLayoutElement.preferredWidth ||
_ParentTranform.rect.height != _CaptureLayoutElement.preferredHeight)
{
gameObject.SetActive(false);
if (_CaptureLayoutElement.gameObject)
{
_CaptureLayoutElement.preferredWidth = _ParentTranform.rect.width;
_CaptureLayoutElement.preferredHeight = _ParentTranform.rect.height;
}
if (_RecordLayoutElement.gameObject)
{
_RecordLayoutElement.preferredWidth = _ParentTranform.rect.width;
_RecordLayoutElement.preferredHeight = _ParentTranform.rect.height;
}
gameObject.SetActive(true);
}
}
void OnEnable()
{
_CaptureToggle.onValueChanged.AddListener(OnCaptureToggle);
_RecordToggle.onValueChanged.AddListener(OnRecordToggle);
_ScrollSnap.onCenter += OnSnapOver;
_ScrollSnap.onInit += OnSnapInit;
}
void OnDisable()
{
_CaptureToggle.onValueChanged.RemoveListener(OnCaptureToggle);
_RecordToggle.onValueChanged.RemoveListener(OnRecordToggle);
_ScrollSnap.onCenter -= OnSnapOver;
_ScrollSnap.onInit -= OnSnapInit;
}
void OnCaptureToggle(bool b)
{
_ScrollSnap.SetCenterIndex(0);
}
void OnRecordToggle(bool b)
{
_ScrollSnap.SetCenterIndex(1);
}
public void OnSnapInit()
{
_ScrollSnap.SetCenterIndex(0);//初始化时的位置
}
//移动监测某个gameobject时停靠的位置
protected void OnSnapOver(GameObject centerObj, int index)
{
if (centerObj == _CaptureLayoutElement.gameObject)
{
_ScrollSnap.SetCenterIndex(0);
}
else if (centerObj == _RecordLayoutElement.gameObject)
{
_ScrollSnap.SetCenterIndex(1);
}
}
核心代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
///
/// 拖动ScrollRect结束时始终让一个子物体位于中心位置。
///
/// </summary>
public class ScrollSnap : MonoBehaviour, IEndDragHandler, IDragHandler
{
//将子物体拉到中心位置时的速度
public float centerSpeed = 9f;
public float minDistance = 1.0f;
public float scrollSensitive = 0f;
public ScrollRect.MovementType setMovementType = ScrollRect.MovementType.Unrestricted;
public ScrollCurveSize _CurveSize;
//注册该事件获取当拖动结束时位于中心位置的子物体
public delegate void OnCenterHandler(GameObject centerChild,int index);
public event OnCenterHandler onCenter;
public delegate void OnInitHandler();
public event OnInitHandler onInit;
private ScrollRect _scrollView;
private RectTransform _container;
private List<float> _childrenPos = new List<float>();
private float _targetPos;
private bool _centering = false;
public bool _bChangeUI = false;
float _MinPos;
float _MaxPos;
public static ScrollSnap _instance;
void Awake()
{
_instance = this;
_scrollView = GetComponent<ScrollRect>();
_CurveSize = GetComponentInChildren<ScrollCurveSize>();
if (_scrollView == null)
{
Debug.LogError("CenterOnChild: No ScrollRect");
return;
}
_container = _scrollView.content.gameObject.GetComponent<RectTransform>();
HorizontalLayoutGroup layout;
layout = _container.GetComponent<HorizontalLayoutGroup>();
if (layout == null)
{
Debug.LogError("CenterOnChild: No GridLayoutGroup on the ScrollRect's content");
return;
}
_scrollView.movementType = setMovementType;
}
void Start()
{
StartCoroutine(Init());
}
IEnumerator Init()
{
yield return new WaitForEndOfFrame();
_childrenPos.Clear();
HorizontalLayoutGroup layout = _container.GetComponent<HorizontalLayoutGroup>();
while(layout.preferredWidth == 0)
{
yield return 0;
}
//计算第一个子物体位于中心时的位置
Rect rc = _scrollView.GetComponent<RectTransform>().rect;
float childPosX = layout.preferredWidth * 0.5f;
_childrenPos.Add(childPosX);
//缓存所有子物体位于中心时的位置
_MinPos = float.MaxValue;
_MaxPos = float.MinValue;
for (int i = 0; i < _container.childCount - 1; i++)
{
childPosX -= layout.preferredWidth / _container.childCount + layout.spacing;
_childrenPos.Add(childPosX);
_MinPos = Mathf.Min(childPosX, _MinPos);
_MaxPos = Mathf.Max(childPosX, _MinPos);
}
_centering = true;
gameObject.GetComponent<ScrollRect>().horizontalNormalizedPosition = 0;
if(onInit != null)
{
onInit();
}
}
protected void OnEnable()
{
//StartCoroutine(Init()); //由于每次刷新会初始化,需要屏蔽这个,改为手动调用
}
public void OnInitScroll() {
TimerManager.Instance.StartCoroutine(Init());
}
void Update()
{
if (_centering)
{
Vector2 pos = _container.anchoredPosition;
pos.x = Mathf.Lerp(_container.anchoredPosition.x, _targetPos, Mathf.Clamp01(centerSpeed * Time.deltaTime));
_container.anchoredPosition = pos;
//ATrace.Log("Centering: " + _targetPos + ":" + _container.localPosition.x);
if (Mathf.Abs(_container.localPosition.x - _targetPos) < minDistance)
{
Vector2 apos = _container.anchoredPosition;
apos.x = _targetPos;
_container.anchoredPosition = apos;
gameObject.GetComponent<ScrollRect>().velocity = Vector2.zero;
_centering = false;
}
}
else if(_bChangeUI)
{
Vector2 pos = _container.anchoredPosition;
if (pos.x > 120 || pos.x < 0)
{
}
else
{
_CurveSize.ProcessScale();
}
pos.x = Mathf.Clamp(pos.x, 0, 120);
_container.anchoredPosition = pos;
}
}
public void OnEndDrag(PointerEventData eventData)
{
_centering = true;
bool isLeftScroll = _targetPos < _container.anchoredPosition.x;
_targetPos = FindClosestPos(isLeftScroll ? _container.anchoredPosition.x + scrollSensitive : _container.anchoredPosition.x - scrollSensitive);
}
public void OnDrag(PointerEventData eventData)
{
_centering = false;
}
public void SetCenterIndex(int index)
{
if(_childrenPos.Count > index)
{
_targetPos = _childrenPos[index];
_centering = true;
}
//Debug.LogFormat(this, "setCenterIndex() -> posCount:{0} index:{1}", _childrenPos.Count, index);
}
private float FindClosestPos(float currentPos)
{
int childIndex = 0;
float closest = 0;
float distance = Mathf.Infinity;
for (int i = 0; i < _childrenPos.Count; i++)
{
float p = _childrenPos[i];
float d = Mathf.Abs(p - currentPos);
if (d < distance)
{
distance = d;
closest = p;
childIndex = i;
}
}
GameObject centerChild = _container.GetChild(childIndex).gameObject;
Debug.Log(centerChild.gameObject.name);
if (onCenter != null)
onCenter(centerChild, childIndex);
return closest;
}
}
上一篇: 浅谈PHP第四弹---递归函数