美文网首页UGUI
Unity中的ScrollRect滑动结束后,自动将最靠近中间的

Unity中的ScrollRect滑动结束后,自动将最靠近中间的

作者: 全新的饭 | 来源:发表于2022-04-26 17:05 被阅读0次

思路

在停止滑动(OnEndDrag)时,调整Content的localPosX:使其逐渐变为与其最接近的若干的标准PosX中的那个。

说明

当前只支持横向滑动。

2种模式:

  1. 根据停止滑动时是左滑还是右滑来决定自动滑动方向
  2. 当前哪个元素最靠近中央,就将该元素的位置归到中央

用法

将UIScrollRectAdjustor挂在ScrollRect同级节点上。

可配置

  1. 自动移动时的移动速度
  2. 使用哪种模式寻找标准PosX。

具体实现

UIScrollRectAdjustor.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

[RequireComponent(typeof(ScrollRect))]
public class UIScrollRectAdjustor : MonoBehaviour, IEndDragHandler, IBeginDragHandler
{
    private ScrollRect _scrollRect;
    private RectTransform _contentRectTrans;
    private float _minX;
    private float _baseX;
    private float _xOffset;
    [SerializeField,Header("自动移动时的移动速度")]
    private float _moveSpeed = 3000;
    private IEnumerator _autoMoveCoroutine;
    [SerializeField, Header("方向优先")]
    private bool _shouldBaseOnMoveDir;
    private void Start()
    {
        Init();
    }

    private void OnDestroy()
    {
        Destroy();
    }
    
    public void Init()
    {
        _minX = 0;
        _scrollRect = GetComponent<ScrollRect>();
        _contentRectTrans = _scrollRect.content;
        var gridLayoutGroup = _contentRectTrans.GetComponent<GridLayoutGroup>();
        _baseX = gridLayoutGroup.cellSize.x / 2 + gridLayoutGroup.padding.left;
        _xOffset = gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x;
    }
    
    public void Destroy()
    {
        StopAutoMove();
        _scrollRect = null;
        _contentRectTrans = null;
    }
    public void OnBeginDrag(PointerEventData eventData)
    {
        if (_minX == 0)
        {
            var gridLayoutGroup = _contentRectTrans.GetComponent<GridLayoutGroup>();
            _minX = _contentRectTrans.sizeDelta.x - gridLayoutGroup.padding.right - gridLayoutGroup.cellSize.x - gridLayoutGroup.spacing.x / 2;
            _minX *= -1;
        }
        StopAutoMove();
    }

    private bool _isLeftMove;

    public void OnEndDrag(PointerEventData eventData)
    {
        float curX = _contentRectTrans.localPosition.x;
        if (curX < 0)
        {
            _isLeftMove = _scrollRect.velocity.x < 0;

            float suitableX = GetSuitableX(curX);
            StartAutoMove(curX, suitableX);
        }
    }

    private float GetSuitableX(float curX)
    {
        float suitableX = curX;
        int index = 0;
        float leftX;
        float rightX;
        while (suitableX >= _minX)
        {
            leftX = GetX(index);
            rightX = GetX(index + 1);

            if (leftX >= curX && rightX <= curX)
            {
                var leftXOffset = Mathf.Abs(leftX - curX);
                var rightXOffset = Mathf.Abs(rightX - curX);

                if (_shouldBaseOnMoveDir)
                {
                    suitableX = _isLeftMove ? rightX : leftX;
                }
                else
                {
                    suitableX = leftXOffset < rightXOffset ? leftX : rightX;
                }
                break;
            }
            index++;
        }

        return Mathf.Max(suitableX, _minX);

        float GetX(int i)
        {
            float value = 0;
            if (i > 0)
            {
                value = (_baseX + _xOffset * i) * -1 + _baseX;
            }
            return value;
        }
    }
    

    private void StartAutoMove(float beginX, float endX)
    {
        StopAutoMove();

        _autoMoveCoroutine = AutoMoveCoroutine(beginX, endX);
        StartCoroutine(_autoMoveCoroutine);
    }
    private void StopAutoMove()
    {
        if (_autoMoveCoroutine != null)
        {
            StopCoroutine(_autoMoveCoroutine);
            _autoMoveCoroutine = null;
        }
    }

    private IEnumerator AutoMoveCoroutine(float beginX, float endX)
    {
        float timer = 0f;
        float moveTime = Mathf.Abs(beginX - endX) / _moveSpeed;
        while (timer < moveTime)
        {
            _contentRectTrans.localPosition = new Vector3(Mathf.Lerp(beginX, endX, timer / moveTime),
                                                          _contentRectTrans.localPosition.y,
                                                          _contentRectTrans.localPosition.z);
            timer += Time.deltaTime;
            yield return null;
        }

        _contentRectTrans.localPosition = new Vector3(endX, _contentRectTrans.localPosition.y, _contentRectTrans.localPosition.z);
        _scrollRect.StopMovement();
    }
}

相关文章

网友评论

    本文标题:Unity中的ScrollRect滑动结束后,自动将最靠近中间的

    本文链接:https://www.haomeiwen.com/subject/fjhhyrtx.html