文章目录
- 一篇一句
- 前言
- 素材
- 一、无限滚动视图
- 1. 绘制视图
- 2. Content Size Fitter是布局控件
- (1)在文本框中使用
- (2)控制Scroll View(Scroll Rect组件)控件下Content的大小
- 3. 控制视图无限滚动
- 4. 向右拉无限滚动
- 5. 修复滚动视图一卡一卡的问题
- 二、滚动选中视图
- 1. 和前面差不多 添加滚动视图
- 2. 挂载代码并配置参数
- 源码
- 完结
一篇一句
我们总喜欢拿顺其自然,敷衍人生道路上的荆棘坎坷,却很少承认,真正的顺其自然,其实是竭尽所能之后的不强求,而非两手一摊的不作为。 ——瑞卡斯
前言
先来看看最终实现效果
无限滚动视图
滚动选中视图
素材
链接:https://pan.baidu.com/s/159PuQjxYA0jdSLQ6y65mYw?pwd=qy5q
提取码:qy5q
一、无限滚动视图
1. 绘制视图
新增滚动视图,并禁止垂直滚动
添加布局组件和内容大小控制容器(设置为水平首选)
为什么使用Content Size Fitter是布局控件下面会介绍
添加遮罩组件Rect Mask 2D
添加字体
最终效果
2. Content Size Fitter是布局控件
这里我觉得有必要解释一下Content Size Fitter是布局控件的作用
(1)在文本框中使用
在文本框中添加这个Content Size Fitter组件,并设置为Preferred Size之后
文本框就会跟随文字的大小自由变化了,也是一个小技巧,但是就不可以再自定义控制文本框的大小了
(2)控制Scroll View(Scroll Rect组件)控件下Content的大小
- 在使用Scroll View组件的时候,一般会设置Content的大小来调节现实的内容
- 如果Content下的东西太多,就会拖不到最后面的模块了,所以这个时候给Content添加一个Content Size Fitter组件,将Vertical Fit的值设置为Preferred Size,那我们就不需要关心Content的Heigh高度了,这个时候就不怕子物体的多少了,都会正常显示出来。
演示一下
未使用Content Size Fitter组件的情况:
使用Content Size Fitter组件的情况:
可以看到未添加Content Size Fitter组件时,因为Content的大小我并没有手动调节到一个合适的大小,导致下面的拖不到
即使鼠标拖过去了,松开的时候也会返回到原来的位置
所以这个时候添加Content Size Fitter组件后,我们就可以达到一个理想的效果了
不用在考虑Content的大小调节了!
3. 控制视图无限滚动
using UnityEngine;
using UnityEngine.UI;
public class InfiniteScroll : MonoBehaviour
{
public ScrollRect scrollRect; // 滚动视图组件,用于控制滚动行为
public RectTransform viewPortTransform; // 可视区域的RectTransform组件,用于获取可视区域的大小
public RectTransform contentPanelTransform; // 内容面板的RectTransform组件,用于放置项的容器
public HorizontalLayoutGroup HLG; // 水平布局组件,用于计算项之间的间距和对齐方式
public RectTransform[] ItemList; // 项的列表,包含了所有可能的项
void Start()
{
// 按照空白间距(spacing)和项的宽度(width),计算可视区域所需的项数
int ItemsToAdd = Mathf.CeilToInt(viewPortTransform.rect.width / (ItemList[0].rect.width + HLG.spacing));
Debug.Log(ItemsToAdd);
// 根据上面计算出来的项数,创建轮播的初始项
for (int i = 0; i < ItemsToAdd; i++)
{
// 创建首批项并放置在内容面板的末尾
RectTransform RT = Instantiate(ItemList[i % ItemList.Length], contentPanelTransform);
//将新创建的项放置在内容面板的末尾,确保它们按顺序排列
RT.SetAsLastSibling();
}
// 创建轮播的补位项,确保用户向左或向右滚动时都有相应的项可供显示
for (int i = 0; i < ItemsToAdd; i++)
{
// 计算下一批项在ItemList中的索引
int num = ItemList.Length - i - 1;
while (num < 0)
{
// 对索引进行循环处理,确保不超过ItemList的长度
num += ItemList.Length;
}
// 创建更多的项并放置在内容面板的开头
RectTransform RT = Instantiate(ItemList[num], contentPanelTransform);
//将新创建的项放置在内容面板的开头,确保它们按顺序排列
RT.SetAsFirstSibling();
}
// 计算并设置内容面板的初始位置,使得第一批轮播项的左侧与可视区域的左侧对齐
contentPanelTransform.localPosition = new Vector3(
(0 - (ItemList[0].rect.width + HLG.spacing) * ItemsToAdd), // 需要向左偏移的距离
contentPanelTransform.localPosition.y, // 不需要上下偏移
contentPanelTransform.localPosition.z // 不需要前后偏移
);
}
}
挂载脚本,配置好参数
效果
4. 向右拉无限滚动
void Update()
{
// 如果内容面板偏移到可视区域左侧之外,则将其向右偏移一个完整的项的宽度
if (contentPanelTransform.localPosition.x > 0)
{
// 强制更新画布,确保UI显示正确
Canvas.ForceUpdateCanvases();
contentPanelTransform.localPosition -= new Vector3(ItemList.Length * (ItemList[0].rect.width + HLG.spacing), 0, 0);
}
// 如果内容面板偏移到可视区域右侧之外,则将其向左偏移一个完整的项的宽度
if (contentPanelTransform.localPosition.x < 0 - (ItemList.Length * (ItemList[0].rect.width + HLG.spacing)))
{
// 强制更新画布,确保UI显示正确
Canvas.ForceUpdateCanvases();
contentPanelTransform.localPosition += new Vector3(ItemList.Length * (ItemList[0].rect.width + HLG.spacing), 0, 0);
}
}
效果
5. 修复滚动视图一卡一卡的问题
内容面板的速度会出现奇怪的行为,出现这种行为是因为当我们更改内容面板的位置时,它会影响速度计算,为了解决这个问题,当我们重置内容面板的位置时,我们需要忽略帧上的这些计算
修改代码
Vector2 Oldvelocity;// 上一帧的滚动速度
bool isUpdated;// 是否需要更新滚动速度
void Start()
{
isUpdated = false;
Oldvelocity = Vector2.zero;
//。。。
}
void Update()
{
// 如果需要更新滚动速度,则将当前速度设置为上一帧的速度
if(isUpdated){
isUpdated = false;
scrollRect.velocity = Oldvelocity;
}
if (contentPanelTransform.localPosition.x > 0)
{
// 。。。
// 更新滚动速度
Oldvelocity = scrollRect.velocity;
isUpdated = true;
}
// 如果内容面板偏移到可视区域右侧之外,则将其向左偏移一个完整的项的宽度
if (contentPanelTransform.localPosition.x < 0 - (ItemList.Length * (ItemList[0].rect.width + HLG.spacing)))
{
//。。。
// 更新滚动速度
Oldvelocity = scrollRect.velocity;
isUpdated = true;
}
}
效果,滚动就很平滑了
二、滚动选中视图
类似CSGO的开箱抽奖功能
1. 和前面差不多 添加滚动视图
效果
添加代码控制
x取整获得物品序号;ScrollRect.velocity判断滚动状态;Mathf.MoveTowards做平滑吸附;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class SnapToItem : MonoBehaviour
{
public ScrollRect scrollRect; // 滚动视图组件
public RectTransform contentPanel; // 内容面板组件
public RectTransform sampleListItem; // 样本列表项组件
public HorizontalLayoutGroup HLG; // 水平布局组件
public TMP_Text NameLabel; // 显示当前选中项的标签
public string[] ItemNames; // 列表项的名称数组
bool isSnapped; // 是否已经对齐到了一个物品
public float snapForce; // 对齐时的强度
float snapSpeed; // 对齐时的速度
void Start()
{
isSnapped = false;
}
void Update()
{
// 当前选中项的索引
int currentItem = Mathf.RoundToInt((0 - contentPanel.localPosition.x) / (sampleListItem.rect.width + HLG.spacing));
Debug.Log(currentItem);
// 如果滚动速度小于200且没有对齐到一个物品,则进行对齐操作
if (scrollRect.velocity.magnitude < 200 && !isSnapped)
{
scrollRect.velocity = Vector2.zero;
snapSpeed += snapForce * Time.deltaTime;
contentPanel.localPosition = new Vector3(
Mathf.MoveTowards(contentPanel.localPosition.x, 0 - (currentItem * (sampleListItem.rect.width + HLG.spacing)), snapSpeed),
contentPanel.localPosition.y,
contentPanel.localPosition.z);
// 更新当前选中项的标签
if (currentItem >= 0 && currentItem < ItemNames.Length)
{
NameLabel.text = ItemNames[currentItem];
}
else
{
NameLabel.text = "_____";
}
// 如果已经对齐到了一个物品,则停止对齐
if (contentPanel.localPosition.x == 0 - (currentItem * (sampleListItem.rect.width + HLG.spacing)))
{
isSnapped = true;
}
}
// 如果滚动速度大于200,则重置对齐状态
if(scrollRect.velocity.magnitude > 200)
{
NameLabel.text = "_____";
isSnapped = false;
snapSpeed = 0;
}
}
}
2. 挂载代码并配置参数
效果
源码
为了防止大家变懒,源码就不提供了,大家直接可以照着文章思路进行学习
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,以便我第一时间收到反馈,你的每一次支持
都是我不断创作的最大动力。点赞越多,更新越快哦!当然,如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~