滑动列表近大远小吸附功能

using HiplayGame;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
 
/// <summary>
/// 自动吸附的滑动列表
/// </summary>
[RequireComponent(typeof(ScrollRect))]
public class ScrollRect_AutoAdsorption : UIBase
{
    [Header("滑动列表类型")]
    public ScrollRectType scrollRectType;
    [Header("能否滑动多页")]
    public bool canMultiPageSlide;
    [Header("是否使用动态缩放")]
    public bool useDynamicScale;
    [HideInInspector]
    public float scaleCoe = 0.25f;
    [Header("横向滑动系数")]
    public float h_lerp = 0.2f;
    [Header("纵向滑动系数")]
    public float v_lerp = 0.2f;

    int totalPage;//总页数
    int curPage;//当前页
    float[] eachPageNormalizedPos;//每页坐标
 
    ScrollRect scrollRect;//滑动区域
    RectTransform content;//Content
    RectTransform viewport;//viewport
    GridLayoutGroup layout;//布局
 
    private void Awake()
    {
        scrollRect = GetComponent<ScrollRect>();
        content = scrollRect.content;
        viewport = scrollRect.viewport;
        layout = content.GetComponent<GridLayoutGroup>();
        if (layout == null)
        {
            Debug.LogError("Content下找不到GridLayoutGroup组件");
        }
 
        layout.constraint = scrollRectType == ScrollRectType.Horizontal
            ? GridLayoutGroup.Constraint.FixedRowCount
            : GridLayoutGroup.Constraint.FixedColumnCount;
        layout.constraintCount = 1;
        scrollRect.horizontal = scrollRectType == ScrollRectType.Horizontal;
        scrollRect.vertical = scrollRectType == ScrollRectType.Vertical;
        Init();
    }
 
    /// <summary>
    /// 初始化
    /// </summary>
    public void Init()
    {
        curPage = 1;
        SetButtonState();
        totalPage = content.childCount;
 
        //计算每页的归一化坐标
        eachPageNormalizedPos = new float[totalPage];
        if (scrollRectType == ScrollRectType.Horizontal)
        {
            float tempNormalizedPos = 0;
            for (int i = 0; i < eachPageNormalizedPos.Length; i++)
            {
                eachPageNormalizedPos[i] = tempNormalizedPos;
                tempNormalizedPos += 1f / (totalPage - 1);
            }
        }
        else if (scrollRectType == ScrollRectType.Vertical)
        {
            float tempNormalizedPos = 1;
            for (int i = 0; i < eachPageNormalizedPos.Length; i++)
            {
                eachPageNormalizedPos[i] = tempNormalizedPos;
                tempNormalizedPos -= 1f / (totalPage - 1);
            }
        }
 
        //设置Content
        content.sizeDelta = scrollRectType == ScrollRectType.Horizontal
            ? new Vector2((totalPage - 1) * (layout.cellSize.x + layout.spacing.x), viewport.rect.height)
            : new Vector2(0, viewport.rect.height + (totalPage - 1) * (layout.cellSize.y + layout.spacing.y));
 
        scrollRect.horizontalNormalizedPosition = 0;
        scrollRect.verticalNormalizedPosition = 1;
    }
 
    bool inDrag;//是否在拖拽中
    float beginDragPos;//开始拖拽的位置
    float endDragPos;//结束拖拽的位置
 
    public void OnBeginDrag()
    {
        inDrag = true;
 
        beginDragPos = scrollRectType == ScrollRectType.Horizontal
            ? Input.mousePosition.x
            : Input.mousePosition.y;
    }
 
    public void OnEndDrag()
    {
        inDrag = false;
 
        endDragPos = scrollRectType == ScrollRectType.Horizontal
            ? Input.mousePosition.x
            : Input.mousePosition.y;
        float curNormalizedPos = scrollRectType == ScrollRectType.Horizontal
            ? scrollRect.horizontalNormalizedPosition
            : scrollRect.verticalNormalizedPosition;
 
        float offset = endDragPos - beginDragPos;
        //多页滑动
        if (canMultiPageSlide)
        {
            curPage = FindNearlyPage(curNormalizedPos);
            SetButtonState();
        }
        //单页滑动
        else
        {
            int nearlyPage = FindNearlyPage(curNormalizedPos);
            if (nearlyPage != curPage)
            {
                if (offset > 0)
                {
                    curPage = scrollRectType == ScrollRectType.Horizontal
                        ? curPage - 1 < 1 ? 1 : curPage - 1
                        : curPage + 1 > totalPage ? curPage : curPage + 1;
                    SetButtonState();
                }
                else if (offset < 0)
                {
                    curPage = scrollRectType == ScrollRectType.Horizontal
                        ? curPage + 1 > totalPage ? curPage : curPage + 1
                        : curPage - 1 < 1 ? 1 : curPage - 1;
                    SetButtonState();
                }
            }
        }
    }
 
    private void Update()
    {
        if (layout == null) return;
 
        if (!inDrag)
        {
            scrollRect.normalizedPosition = scrollRectType == ScrollRectType.Horizontal
                ? new Vector2(Mathf.Lerp(scrollRect.horizontalNormalizedPosition, eachPageNormalizedPos[curPage - 1], h_lerp), 0)
                : new Vector2(0, Mathf.Lerp(scrollRect.verticalNormalizedPosition, eachPageNormalizedPos[curPage - 1], v_lerp));
        }
 
        if (useDynamicScale)
        {
            UpdateScale();
        }
    }

    private void SetButtonState()
    {
        if (curPage == 1)
        {
            SendMessageUpwards("SetLeftButtonState",false);
            SendMessageUpwards("SetRightButtonState",true);
        }
        else if(curPage == totalPage)
        {
            SendMessageUpwards("SetLeftButtonState",true);
            SendMessageUpwards("SetRightButtonState",false);
        }
        else
        {
            SendMessageUpwards("SetLeftButtonState",true);
            SendMessageUpwards("SetRightButtonState",true);
        }
    }

    /// <summary>
    /// 更新缩放
    /// </summary>
    void UpdateScale()
    {
        Vector2 centerPos = transform.position;
 
        //得到所有项
        GameObject[] items = new GameObject[content.childCount];
        for (int i = 0; i < items.Length; i++)
        {
            items[i] = content.GetChild(i).gameObject;
        }
 
        foreach (var item in items)
        {
            Vector2 itemPos = item.transform.position;
            if (Mathf.Abs(itemPos.x - centerPos.x) <= 390f)
            {
                var offsetX = 1 - Mathf.Abs(itemPos.x - centerPos.x)/390f * scaleCoe;
                item.LocalScale(offsetX);

                if (Mathf.Abs(offsetX - 1f) < 0.05f)
                {
                    SetActive(item, true, "Image1");
                    SetActive(item, false, "Image2");
                    SetActive(item, false,"BlackMask");
                }
                else
                {
                    SetActive(item, false, "Image1");
                    SetActive(item, true, "Image2");
                    SetActive(item, true,"BlackMask");
                }
            }
        }
    }
 
    /// <summary>
    /// 查找最近的页
    /// </summary>
    int FindNearlyPage(float curNormalizedPos)
    {
        int tempPage = 1;
        float tempMinOffset = Mathf.Abs(curNormalizedPos - eachPageNormalizedPos[0]);
        for (int i = 1; i <= totalPage; i++)
        {
            float tempOffset = Mathf.Abs(curNormalizedPos - eachPageNormalizedPos[i - 1]);
            if (tempOffset < tempMinOffset)
            {
                tempMinOffset = tempOffset;
                tempPage = i;
            }
        }
        return tempPage;
    }
 
    #region interface
 
    /// <summary>
    /// 设置页数
    /// </summary>
    public void SetPage(int page)
    {
        curPage = page;
        SetButtonState();
    }

    public int GetPage()
    {
        return curPage;
    }

    #endregion
}
 
/// <summary>
/// 滑动列表类型
/// </summary>
public enum ScrollRectType
{
    Horizontal,
    Vertical,
}
 
#if UNITY_EDITOR
 
/// <summary>
/// 自动吸附的滑动列表Inspector扩展
/// </summary>
[UnityEditor.CustomEditor(typeof(ScrollRect_AutoAdsorption))]
public class ScrollRect_AutoAdsorption_Editor : UnityEditor.Editor
{
    ScrollRect_AutoAdsorption t;
 
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
 
        t = target as ScrollRect_AutoAdsorption;
 
        if (t.useDynamicScale)
        {
            t.scaleCoe = UnityEditor.EditorGUILayout.FloatField("缩放系数", t.scaleCoe);
        }
    }
}
 
#endif

 

复写滑动列表的拖拽方法

public class AutoScrollRect : ScrollRect
{
    private ScrollRect_AutoAdsorption autoAdsorption;
    protected void Awake()
    {
        autoAdsorption = GetComponent<ScrollRect_AutoAdsorption>();
    }

    public override void OnBeginDrag(PointerEventData eventData)
    {
        autoAdsorption.OnBeginDrag();
        base.OnBeginDrag(eventData);
    }

    public override void OnEndDrag(PointerEventData eventData)
    {
        TimerManager.Add(0.1f, id =>
        {
            autoAdsorption.OnEndDrag();
        });
        base.OnEndDrag(eventData);
    }
    
}

  

posted @ 2022-12-20 18:21  田丰  阅读(34)  评论(0编辑  收藏  举报