Unity 实现滑页效果 始终保持比例


前言

必须要有DOTween插件  这个是他们官网的链接 DOTween - Downloads


一、结果

展示1  这个是有间隔的

展示2   全屏无间隔  可以通过group的spacing(物体间隙) 调整元素 美观

二、实现


1.该脚本目标只支持横向

2.处理

Viewport 和Content的锚点都是自适应于父物体 , 关闭垂直控制, 确保添加了脚本   可以通过group的spacing(物体间隙) 调整元素

三、教学

多看几遍肯定懂了

四、代码

只需要在Scroll View  挂载当前脚本 

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using DG.Tweening;

namespace DGFramework
{
    /// <summary>
    /// 左右滑动器脚本  支持多滑
    /// </summary>
    public class ScrollViewMoreSlide : MonoBehaviour, IBeginDragHandler, IEndDragHandler
    {
        private ScrollRect scrollRect;
        public ScrollRect ScrollRect => scrollRect;

        private float contentLength;        //容器总长度
        private float beginMousePostionX;   //开始鼠标位置
        private float endMousePositionX;    //结束鼠标位置
        private float lastProportion;       //单元格最后所在的百分比例

        private Vector2 contentInitSize;    //容器的初始大小
        private float leftOffset;           //左偏移量     变更脚本需要变更
        private float cellWidth;            //一个滑动物体宽度 一个单元格的宽度
        private float spaceBetween;         // 物体间距  一个单元格间距 

        private float upperLimit;           //上限值
        private float lowerLimit;           //下限值

        private float firstItemLength;      //移动第一个单元格的距离
        private float oneItemLength;        //滑动一个单元格需要的距离
        private float oneItemProportion;    //滑动一个单元格所占百分比

        private int totalItemNum;           //总共有几个单元格
        private int currentIndex;           //当前单元格下标索引

        private void Awake()
        {
            scrollRect = this.GetComponent<ScrollRect>();
            RectTransform content = scrollRect.content;
            GridLayoutGroup group = content.GetComponent<GridLayoutGroup>();
            contentInitSize = content.sizeDelta;

            totalItemNum = scrollRect.content.childCount;     //所有子物体数量  
            leftOffset = group.padding.left;                  //左偏移量
            cellWidth = group.cellSize.x;                     //物体宽度
            spaceBetween = group.spacing.x;                   //物体间隙

            contentLength = GetContentLength(totalItemNum, cellWidth, spaceBetween);  //计算出需要的长度
            content.sizeDelta = new Vector2(contentLength, content.sizeDelta.y);      //设置content长度 也是宽度

            firstItemLength = cellWidth / 2 + leftOffset;      //第一个单元格的长度
            oneItemLength = cellWidth + spaceBetween;          // 每个单元格的长度(除去第一个) = 单元格宽 + 单元格间隔
            oneItemProportion = oneItemLength / contentLength; //每个单元格的滑动比例
            upperLimit = 1 - firstItemLength / contentLength;  // 上限值  最前面的
            lowerLimit = firstItemLength / contentLength;      //下限值   最后面的

            currentIndex = 1;                                  //当前索引
            scrollRect.horizontalNormalizedPosition = 0;       //让单元格在单位化之后的最开始的位置    
        }

        /// <summary>
        ///  通过拖拽的事件 发生值改变事件
        /// </summary>
        public void OnEndDrag(PointerEventData eventData)
        {
            endMousePositionX = Input.mousePosition.x;    //获取鼠标结束位置
            float offSetX = (beginMousePostionX - endMousePositionX) * 2;   //偏移量

            if (Mathf.Abs(offSetX) > firstItemLength)     //执行滑动动作的前提是要大于第一个需要滑动的距离
            {
                if (offSetX > 0)                          //右滑
                {
                    if (currentIndex >= totalItemNum)     //如果当前下标已经是最大的了
                    {
                        return;
                    }
                    int moveCount = (int)((offSetX - firstItemLength) / oneItemLength) + 1;//当次可以移动的格子数目
                    currentIndex += moveCount;

                    if (currentIndex >= totalItemNum)     //如果滑动后已经是最大的数量
                    {
                        currentIndex = totalItemNum;
                    }
                    //当次需要移动的比例:上一次已经存在的单元格位置
                    //的比例加上这一次需要去移动的比例
                    lastProportion += oneItemProportion * moveCount;
                    if (lastProportion >= upperLimit)
                    {
                        lastProportion = 1;
                    }
                }
                else                                       //左滑
                {
                    if (currentIndex <= 1)                 //如果当前下标已经是最小的了
                    {
                        return;
                    }

                    int moveCount = (int)((offSetX + firstItemLength) / oneItemLength) - 1;//当次可以移动的格子数目
                    currentIndex += moveCount;

                    if (currentIndex <= 1)                 //如果滑动后已经是最小的数量
                    {
                        currentIndex = 1;
                    }
                    //当次需要移动的比例:上一次已经存在的单元格位置
                    //的比例加上这一次需要去移动的比例
                    lastProportion += oneItemProportion * moveCount;

                    if (lastProportion <= lowerLimit)
                    {
                        lastProportion = 0;
                    }
                }

            }
            DOTween.To(() => scrollRect.horizontalNormalizedPosition, lerpValue =>
            scrollRect.horizontalNormalizedPosition = lerpValue, lastProportion, 0.5f).SetEase(Ease.OutQuint);
        }

        /// <summary>前往下一页往右边 Button按钮事件</summary>
        public void ToNextPage()
        {
            float offSetX = 0;

            if (currentIndex >= totalItemNum)  //已经是最大了
            {
                return;
            }

            int moveCount = (int)((offSetX - firstItemLength) / oneItemLength) + 1;//当次可以移动的格子数目
            currentIndex += moveCount;

            if (currentIndex >= totalItemNum) //如果滑动后已经是最大的数量
            {
                currentIndex = totalItemNum;
            }
            //当次需要移动的比例:上一次已经存在的单元格位置
            //的比例加上这一次需要去移动的比例

            lastProportion += oneItemProportion * moveCount;
            if (lastProportion >= upperLimit)
            {
                lastProportion = 1;
            }

            DOTween.To(() => scrollRect.horizontalNormalizedPosition, lerpValue =>
            scrollRect.horizontalNormalizedPosition = lerpValue, lastProportion, 0.5f).SetEase(Ease.OutQuint);
        }

        /// <summary>前往上一页往左边 Button按钮</summary>
        public void ToLastPage()
        {
            float offSetX = 0;

            if (currentIndex <= 1)  //已经是最小了
            {
                return;
            }

            int moveCount = (int)((offSetX - firstItemLength) / oneItemLength) - 1;//当次可以移动的格子数目
            currentIndex += moveCount;

            if (currentIndex >= totalItemNum) //如果滑动后已经是最大的数量
            {
                currentIndex = totalItemNum;
            }
            //当次需要移动的比例:上一次已经存在的单元格位置
            //的比例加上这一次需要去移动的比例
            //需要在翻页的时候更新一下逻辑处理  然后发送消息给到我们的父物体对象  往上遍历一直二叉树形式查找目标  查找到后我们只可以执行这个方法(State)  接受一下信息

            lastProportion += oneItemProportion * moveCount;
            if (lastProportion >= upperLimit)
            {
                lastProportion = 1;
            }

            DOTween.To(() => scrollRect.horizontalNormalizedPosition, lerpValue =>
            scrollRect.horizontalNormalizedPosition = lerpValue, lastProportion, 0.5f).SetEase(Ease.OutQuint);
        }


        public void OnBeginDrag(PointerEventData eventData)
        {
            beginMousePostionX = Input.mousePosition.x;  //获取到拖拽结束后 三维空间下的屏幕鼠标的Position位置
        }

        //得到content总长度
        private float GetContentLength(int number, float width, float spaceBetween)
        {
            if (number - 1 <= 0) { return 0; };

            number--;

            return width * number + spaceBetween * number;
        }

        /// <summary>
        /// 修改容器总长度   有时候我们创建对象会比较晚  但是我们的初始化长度已经确定了  我们可以手动的修改一下
        /// </summary>
        /// <param name="num">子物体数量</param>
        public void ModifyContentLength(int childNum)
        {
            contentLength = GetContentLength(childNum, cellWidth, spaceBetween);  //计算出需要的长度
            scrollRect.content.sizeDelta = new Vector2(contentLength, scrollRect.content.sizeDelta.y);  //设置长度
            totalItemNum = childNum;  //把当前的总长度也设置一下

            firstItemLength = cellWidth / 2 + leftOffset;   //第一个单元格的长度
            oneItemLength = cellWidth + spaceBetween;   // 每个单元格的长度(除去第一个) = 单元格宽 + 单元格间隔
            oneItemProportion = oneItemLength / contentLength;   //每个单元格的滑动比例
            upperLimit = 1 - firstItemLength / contentLength;    // 上限值  最前面的
            lowerLimit = firstItemLength / contentLength;        //下限值   最后面的

            //currentIndex = 1;  //当前索引
            //scrollRect.horizontalNormalizedPosition = 0; //让单元格在单位化之后的最开始的位置        
        }

        ///<summary> 初始化ContentLength初始化  归零 </summary>>
        public void InitContentLenght()
        {
            scrollRect.content.sizeDelta = contentInitSize;
            //totalItemNum = 0; 
        }
    }
}

五、扩展

如果说需要动态生成元素添加到Content里面去的话   每次生成完元素之后    结束后代码调用一下修改元素方法

ModifyContentLength(conent的子物体的数量)方法即可

需要实现2个按钮点击 上一个或者下一个的话  可以通过监听 ToNextPage() ToLastPage()  2个方法实现

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值