TheLudGamer 发表于 2022-4-27 14:22

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

思路

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

当前只支持横向滑动。

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

将UIScrollRectAdjustor挂在ScrollRect同级节点上。

可配置
自动移动时的移动速度使用哪种模式寻找标准PosX。
具体实现

UIScrollRectAdjustor.cs
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;using UnityEngine.EventSystems;public class UIScrollRectAdjustor : MonoBehaviour, IEndDragHandler, IBeginDragHandler{    private ScrollRect _scrollRect;    private RectTransform _contentRectTrans;    private float _minX;    private float _baseX;    private float _xOffset;        private float _moveSpeed = 3000;    private IEnumerator _autoMoveCoroutine;        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();    }}
页: [1]
查看完整版本: Unity中的ScrollRect滑动结束后,自动将最靠近中间的元素居中