吸附式滚动列表

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

/// <summary>
/// 吸附式滚动列表
/// </summary>
public class PageAdsorbScrollview : MonoBehaviour, IEndDragHandler, IBeginDragHandler
{
    public enum ScrollType
    {
        Horizontal,
        Vertical
    }

    private ScrollRect scrollRect;
    private RectTransform content;
    private GridLayoutGroup gridLayoutGroup;

    private int pageCount;
    private float[] pagesData; //位置

    private float timer = 0; //吸附计时器
    private float startMovePos; //开始移动的位置
    private Vector2 startMovePosV2; //开始移动的位置
    private int currentIndex = 0; //当前所处页数
    private bool isMoving = false; //是否正在吸附中
    private bool isDraging = false; //是否在拖动中
    private Vector2 beginDragPointerPos; //开始拖动时的位置
    private float autotimer = 0; //自动计时器
    private float finalPosOffsetBackup = 0f;

    private Vector2 originalCellSize;


    [Header("滑动类型")]
    public ScrollType scrollType = ScrollType.Vertical;

    [Header("吸附速度"), Range(0f, 50f)]
    public float moveSpeed = 5f;
    [Header("终点位置偏移(0为最终吸附到顶部)")]
    public float finalPosOffset = 0f;
    [Header("界限偏移(0为五五分判定,>0更容易拖动,<0更难拖动)")]
    public float distanceOffset = 0f;

    [Header("是否自动滚动")]
    public bool isAutoScroll = true; //是否自动滚动
    [Header("自动滚动时间间隔"), Range(0f, 60f)]
    public float AutoTime = 1f; //自动滚动时间间隔


    private void Awake()
    {
        scrollRect = GetComponent<ScrollRect>();
        if (scrollRect == null)
        {
            Debug.LogError("不存在组件ScrollRect");
        }
        else
        {
            content = scrollRect.content;
            if (content == null)
            {
                Debug.LogError("不存在节点ScrollRect->Content");
            }
            else
            {
                gridLayoutGroup = content.GetComponent<GridLayoutGroup>();
                if (gridLayoutGroup == null)
                {
                    Debug.LogError("不存在组件ScrollRect->Content->GridLayoutGroup");
                }
                originalCellSize = gridLayoutGroup.cellSize;
            }
        }
        finalPosOffsetBackup = finalPosOffset;
    }

    private void Start()
    {
        Init();
    }

    private void Update()
    {
        CheckMove();
        CheckAutoScroll();

        if (finalPosOffsetBackup != finalPosOffset)
        {
            finalPosOffsetBackup = finalPosOffset;
            Init();
            ScrollTo(currentIndex);
        }
    }

    private void Init()
    {
        pageCount = content.childCount; //子节点数量
        pagesData = new float[pageCount]; //初始化

        for (int i = 0; i < pagesData.Length; i++)
        {
            switch (scrollType)
            {
                case ScrollType.Horizontal:
                    pagesData[i] = i * (1f / (float) (pageCount - 1)); //给位置的数组赋值
                    break;
                case ScrollType.Vertical:
                    pagesData[i] = CalculateTopPosition(i);
                    break;
            }

            //DELETEME
            if (content.GetChild(i).Find("Text") != null)
                content.GetChild(i).Find("Text").GetComponent<Text>().text = pagesData[i].ToString();
        }
    }

    private float CalculateTopPosition(int childIndex)
    {
        if (childIndex <= 0) return 0f  +  finalPosOffset;
        return gridLayoutGroup.padding.top + childIndex * originalCellSize.y + (childIndex - 1) * gridLayoutGroup.spacing.y  +  finalPosOffset;
    }

    private void CheckMove()
    {
        if (isMoving)
        {
            timer += Time.unscaledDeltaTime * moveSpeed;

            switch (scrollType)
            {
                case ScrollType.Horizontal:
                    scrollRect.horizontalNormalizedPosition = Mathf.Lerp(startMovePos, pagesData[currentIndex], timer);
                    break;
                case ScrollType.Vertical:
                    content.anchoredPosition = Vector2.Lerp(startMovePosV2, new Vector2(startMovePosV2.x, pagesData[currentIndex]), timer);
                    break;
            }

            if (timer > 1)
            {
                isMoving = false;
                scrollRect.StopMovement();
            }
        }
        else
        {
            scrollRect.StopMovement();
        }
    }

    private void CheckAutoScroll() //自动滑动
    {
        if (isDraging)
        {
            return;
        }
        if (isAutoScroll)
        {
            autotimer += Time.deltaTime;
            if (autotimer > AutoTime)
            {
                autotimer = 0;
                currentIndex++;
                currentIndex %= pageCount; //取余形成循环
                ScrollTo(currentIndex);
            }
        }
    }

    private void ScrollTo(int index)
    {
        currentIndex = index;
        timer = 0;
        isMoving = true;

        switch (scrollType)
        {
            case ScrollType.Horizontal:
                startMovePos = scrollRect.horizontalNormalizedPosition;
                break;
            case ScrollType.Vertical:
                startMovePosV2 = content.anchoredPosition;
                break;
        }
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        isDraging = true;
        beginDragPointerPos = eventData.position;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        int minIndex = 0;
        //计算当最近的一页是哪个
        switch (scrollType)
        {
            case ScrollType.Horizontal:
                for (int i = 0; i < pagesData.Length; i++)
                {
                    if (Mathf.Abs(pagesData[i] - scrollRect.horizontalNormalizedPosition) < Mathf.Abs(pagesData[minIndex] - scrollRect.horizontalNormalizedPosition))
                    {
                        minIndex = i;
                    }
                }
                break;
            case ScrollType.Vertical:
                var curY = content.anchoredPosition.y;
                var directionY = Mathf.Sign(eventData.position.y - beginDragPointerPos.y);
                for (int i = 0; i < pagesData.Length; i++)
                {
                    if (Mathf.Abs(pagesData[i] - curY) - directionY * distanceOffset < Mathf.Abs(pagesData[minIndex] - curY) + directionY * distanceOffset)
                    {
                        minIndex = i;
                    }
                }
                break;
        }
        ScrollTo(minIndex);
        isDraging = false;
        autotimer = 0;
    }
}

备注:Vertical可用,Horizontal未写完,若使用后者请修改相应switch语句。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值