ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置

效果如下:

接下贴各个gameobject

ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置_第1张图片
ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置_第2张图片
ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置_第3张图片
ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置_第4张图片
    public Toggle _CaptureToggle;
    public Toggle _RecordToggle;

    public RectTransform _ParentTranform;
    public LayoutElement _CaptureLayoutElement;
    public LayoutElement _RecordLayoutElement;

    protected ScrollSnap _ScrollSnap;

    void Awake()
    {

        _ScrollSnap = GetComponentInChildren();
        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;
/// 
/// 
/// 拖动ScrollRect结束时始终让一个子物体位于中心位置。
/// 
/// 
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 _childrenPos = new List();
    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();
        _CurveSize = GetComponentInChildren();
        if (_scrollView == null)
        {
            Debug.LogError("CenterOnChild: No ScrollRect");
            return;
        }
        _container = _scrollView.content.gameObject.GetComponent();

        HorizontalLayoutGroup layout;
        layout = _container.GetComponent();
        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();

        while(layout.preferredWidth == 0)
        {
            yield return 0;
        }



        //计算第一个子物体位于中心时的位置
        Rect rc = _scrollView.GetComponent().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().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().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;
    }
}



你可能感兴趣的:(Unity)