UGUI ScrollRect滑動居中CenterOnChild實現(修改)
原帖:http://www.cnblogs.com/suoluo/p/5535420.html
上述文章是昨天專案遇到這種需求無意間搜到的,不過這篇的缺陷是隻能水平實現滑動居中,所以我原基礎上更改了一下。
1. 僅適用於水平方向和垂直方向(=-= 複製修改)拖動的ScrollRect。
2. ScrollRect中的Grid必須使用GridLayoutGroup。(雖然是居中表現 ChildAligenment也【不要】選擇成Center Middle)
3. 由於需要知道ScrollRect的寬度以便計算中心位置,故ScrollRect的Anchors的四個小三角中的上面或者下面的一對角不得分離,不然寬度計算出錯,即需要:Anchors.Min.x == Anchors.Max.x。最好四角合一。(解釋一下,因為不同的Anchors下 content的localPosition座標不同,所以用到的公式不同,這個公式可以根據自己的Anchors需求自己列出
4. 由於是通過設定ScrollRect's content的localPosition實現,故需要將ScrollRect的中心點Pivot與content的中心點均置於自身最左邊(0, 0.5)。(這條內容我沒有嘗試,因為時間緊張還沒有看水平的東西,直接修改成垂直的了)
5. 由於第一個與最後一個子物體需要停留在中間,故ScrollRect的Movement Type需要設定為Unrestricted。該項會在執行時自動設定。(這條不需要說明了,不過表現垂直的時候要在content下新增ContentSizeFitter,並且把Vertical Fit 選擇MinSize)
上面幾條是我個人的理解,也不可能全對,歡迎指出。
下面附上程式碼
附上圖片using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; /// <summary> /// /// 拖動ScrollRect結束時始終讓一個子物體位於中心位置。 /// /// </summary> public class CenterOnChild : MonoBehaviour, IEndDragHandler, IDragHandler { public enum AxisType { Vertical, Horizontal } public AxisType m_AxisType = AxisType.Vertical; //將子物體拉到中心位置時的速度 public float centerSpeed = 40f; //註冊該事件獲取當拖動結束時位於中心位置的子物體 public delegate void OnCenterHandler(GameObject centerChild); public event OnCenterHandler onCenter; public ScrollRect _scrollView; public Transform _container; private List<float> _childrenPos = new List<float>(); private float _targetPos; private bool _centering = false; void Awake() { if (_scrollView == null) { Debug.LogError("CenterOnChild: No ScrollRect"); return; } GridLayoutGroup grid; grid = _container.GetComponent<GridLayoutGroup>(); if (grid == null) { Debug.LogError("CenterOnChild: No GridLayoutGroup on the ScrollRect's content"); return; } _scrollView.movementType = ScrollRect.MovementType.Unrestricted; //計算第一個子物體位於中心時的位置 float childPosY; float childPosX; switch (m_AxisType) { case AxisType.Vertical: childPosY = _container.localPosition.y - (_scrollView.GetComponent<RectTransform>().rect.height * 0.5f - grid.cellSize.y * 0.5f);//垂直的公式 _childrenPos.Add(childPosY); //快取所有子物體位於中心時的位置 for (int i = 0; i < _container.childCount - 1; i++) { childPosY += grid.cellSize.y + grid.spacing.y; _childrenPos.Add(childPosY); } break; case AxisType.Horizontal: childPosX = _scrollView.GetComponent<RectTransform>().rect.width * 0.5f - grid.cellSize.x * 0.5f;//水平的公式 //快取所有子物體位於中心時的位置 for (int i = 0; i < _container.childCount - 1; i++) { childPosX += grid.cellSize.x + grid.spacing.x; _childrenPos.Add(childPosX); } _childrenPos.Add(childPosX); break; } } void Update() { if (_centering) { Vector3 v = _container.localPosition; switch (m_AxisType) { case AxisType.Vertical: v.y = Mathf.Lerp(_container.localPosition.y, _targetPos, centerSpeed * Time.deltaTime); _container.localPosition = v; if (Mathf.Abs(_container.localPosition.y - _targetPos) < 0.01f) { _centering = false; } break; case AxisType.Horizontal: v.x = Mathf.Lerp(_container.localPosition.x, _targetPos, centerSpeed * Time.deltaTime); _container.localPosition = v; if (Mathf.Abs(_container.localPosition.x - _targetPos) < 0.01f) { _centering = false; } break; } } } public void OnEndDrag(PointerEventData eventData) { _centering = true; switch (m_AxisType) { case AxisType.Vertical: _targetPos = FindClosestPos(_container.localPosition.y); break; case AxisType.Horizontal: _targetPos = FindClosestPos(_container.localPosition.x); break; } } public void OnDrag(PointerEventData eventData) { _centering = false; } 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; if (onCenter != null) onCenter(centerChild); return closest; } }
上圖是專案需求的3個垂直Scroll
因為我的表現是垂直的 Anchor的Y軸相等