1. 程式人生 > >UGUI ScrollRect滑動居中CenterOnChild實現(修改)

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軸相等