前言
必须要有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个方法实现