欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-30 17:38:53
...
效果如下:
ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置
接下贴各个gameobject
ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置
ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置
ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置
ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置
ScrollRect滑动优化(三)_拖动ScrollRect结束时停靠在物体的中心位置
    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;
    }
}



相关标签: UGUI