Unity SnapScrollRect 滚动 匹配 列表 整页

展示效果


原理:

当停止滑动时

判断Contet的horizontalNormalizedPosition

与子Item的缓存值 相减,并得到最小值,然后将Content  horizontalNormalizedPosition滚动过去

使用方式:

直接将脚本挂到ScrollRect上

注意:在创建Content子物体时 或子物体数量变更,需要调用Refresh

代码:

namespace ShangShangQian.Component
{
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.EventSystems;
    using System.Linq;
    using UnityEngine.Events;

    [RequireComponent(typeof(ScrollRect))]
    public class SnapScrollRect : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
    {
        private RectTransform content;
        private ScrollRect rect;

        /// <summary>
        /// content 的位置应该滚到什么
        /// </summary>
        public float target = 1;

        /// <summary>
        /// 回滚的速度 0-1 越小越快
        /// </summary>
        public float smooting = 0.25f;
        public float currentVelocity;

        public float horizontalNormalizedPosition;

        public List<float> values = new List<float>();
        public List<float> distances = new List<float>();
        public List<RectTransform> items = new List<RectTransform>();

        /// <summary>
        /// 是否拖拽中
        /// </summary>
        public bool isDrag = false;

        /// <summary>
        /// 到最近item的距离
        /// </summary>
        public float distanceMin;

        /// <summary>
        /// 到最近item的索引
        /// </summary>
        public int selectIndex;

        /// <summary>
        /// 是否匹配滑动中
        /// </summary>
        public bool Snaping = false;

        /// <summary>
        /// 回滚完毕后调用
        /// </summary>
        public UnityEvent<int> OnSnap;

        void Start()
        {
            rect = GetComponent<ScrollRect>();
            content = rect.content;
        }

        /// <summary>
        /// 刷新数据,content的子物体数量变更时需要调用此函数
        /// </summary>
        public void Refresh()
        {
            items.Clear();
            values.Clear();
            for (int i = 0; i < content.childCount; i++)
            {
                if (content.GetChild(i).gameObject.activeInHierarchy)
                {
                    items.Add(content.GetChild(i).GetComponent<RectTransform>());
                }
            }

            //累加的变量
            values.Add(0);
            //每一个格子的所占比多少  
            float v = 1f / (items.Count - 1);
            for (int i = 1; i < items.Count; i++)
            {
                values.Add(i * v);
            }

            //不同子元素数量 item 对应 的content  horizontalNormalizedPosition 数值
            //1  0  
            //2  0 1   
            //3  0 0.5 1
            //4  0 0.33 0.66 1
        }

        void Update()
        {
#if UNITY_EDITOR
            if (Input.GetKeyDown(KeyCode.A))
            {
                Refresh();
            }
#endif
        }

        void FixedUpdate()
        {
            horizontalNormalizedPosition = rect.horizontalNormalizedPosition;

            if (isDrag)
            {
                return;
            }

            if (Snaping)
            {
                rect.horizontalNormalizedPosition = Mathf.SmoothDamp(rect.horizontalNormalizedPosition, target, ref currentVelocity, smooting);
                if (Mathf.Abs(rect.horizontalNormalizedPosition - values[selectIndex]) < 0.001f)
                {
                    Snaping = false;
                    Debug.Log("回滚:" + selectIndex);
                    OnSnap.Invoke(selectIndex);
                }
            }

        }

        public void OnBeginDrag(PointerEventData eventData)
        {
            isDrag = true;
        }

        public void OnDrag(PointerEventData eventData)
        {
            isDrag = true;
        }

        public void OnEndDrag(PointerEventData eventData)
        {
            isDrag = false;

            Snaping = true;

            distances.Clear();

            //当松手后判断那个距离最近
            for (int i = 0; i < values.Count; i++)
            {
                distances.Add(Mathf.Abs(rect.horizontalNormalizedPosition - values[i]));
            }

            distanceMin = distances.Min();

            selectIndex = distances.FindIndex(b => b == distanceMin);
            target = values[selectIndex];
            rect.StopMovement();
        }


        /// <summary>
        /// 滚到到指定位置
        /// </summary>
        /// <param name="index"></param>
        public void SnapToIndex(int index)
        {
            Snaping = true;
            selectIndex = index;
            target = values[selectIndex];
        }
    }
}

相关推荐

  1. React 列表实现

    2024-01-22 01:58:02       41 阅读
  2. uView ScrollList 横向滚动列表

    2024-01-22 01:58:02       24 阅读
  3. 列表滚动单条案例

    2024-01-22 01:58:02       13 阅读
  4. React 实现列表列表详情功能

    2024-01-22 01:58:02       35 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-22 01:58:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-22 01:58:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-22 01:58:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-22 01:58:02       20 阅读

热门阅读

  1. 第七章 迭代器与生成器

    2024-01-22 01:58:02       28 阅读
  2. 周同学文章汇总

    2024-01-22 01:58:02       29 阅读
  3. Mysql:5.1升级5.6遇到的问题及解决方式

    2024-01-22 01:58:02       31 阅读
  4. mysql事务日志和MVCC

    2024-01-22 01:58:02       34 阅读
  5. 代码随想录算法训练营第二十四天| 77. 组合

    2024-01-22 01:58:02       37 阅读
  6. Code Review是什么?怎么进行代码审查?

    2024-01-22 01:58:02       30 阅读
  7. 【小白向】MMDeploy安装部署|暗坑标注版

    2024-01-22 01:58:02       43 阅读
  8. 第五章:大模型的数据

    2024-01-22 01:58:02       37 阅读
  9. CloudCompare 二次开发(26)——RANSAC分割多个平面

    2024-01-22 01:58:02       39 阅读
  10. QT基础篇(12)QT5多线程

    2024-01-22 01:58:02       29 阅读