前言:让一个滚动列表自动定位到对应位置是很多项目中非常常见的一个功能,比如:关卡选择页滑动到对应的关卡、任务列表页滑动到目前的任务、排行榜滑动到玩家自身位置...等等。
为了实现这个功能,在网上搜寻了半天,文章挺多的,但是实现起来效果总是会出问题,其实并不是那些大佬写的代码不对,而是你的设置不对,很多原因都可能导致问题出现:
1.Canvas 渲染模式:如果你的 Canvas 是设置为Screen space - Overlay那么其坐标系和 UI 元素的坐标系可能不一致,这可能导致你在计算位置时得出了错误的结果
2.Anchor 和 Pivot 设置:你的Scroll组件和Content的 RectTransform 的锚点和锚点偏移设置不正确,不当的 Anchors 或 Pivots 可能导致 UI 元素在文件中看起来正常,但在运行时显示不对。
3.Size Fitter 和 Layout Group:Content中是否有Content Size fitter或Layout Group组件,这可能会影响内容的实际展示和计算。
总之导致问题的原因有很多,我们要解决问题的方式是,需要保证两个前提,
第一:确保content中的layout Group组件和content SizeFitter组件的设置如下:
到这里可能有很多人就都不适应了,因为你的项目中可能必须保证每个子项都有固定的间隔,那么我告诉你一种实现方法,你可以给子项添加一个图片类型的父物体,并将这个图片设置为透明,然后增加父物体的高度以实现看似每个子项都有间隔的效果。
第二:确保每个子项的高度是固定的并且一致。
这里我们以排行榜为例,示例图片:
在这里我的每个子项的高度都为130,并且是没有任何间隔的。
保证这两个前提之后,接下来请直接看代码:
private void Start()
{
StartCoroutine(ScrollToIndex(40));
}
private IEnumerator ScrollToIndex(int index)
{
// 计算目标位置
float itemHeight = 130f; // 每个条目的高度
float targetPosition = (index * itemHeight) - (scroll.GetComponent<RectTransform>().rect.height / 2) + (itemHeight / 2);
targetPosition += 320;
// 获取当前的Content位置
RectTransform contentRect = scroll.content;
float startPosition = contentRect.anchoredPosition.y;
// 计算滚动的起始和结束位置
float timeElapsed = 0f;
while (timeElapsed < 2)
{
// 平滑插值
float newPos = Mathf.Lerp(startPosition, targetPosition, timeElapsed / 2);
contentRect.anchoredPosition = new Vector2(contentRect.anchoredPosition.x, newPos);
timeElapsed += Time.deltaTime;
yield return null;
}
// 确保最终位置正确
contentRect.anchoredPosition = new Vector2(contentRect.anchoredPosition.x, targetPosition);
//Debug.Log("目标Y值:" + targetPosition);
}
代码解释:
首先我们计算目标位置,(index * itemHeight)这是计算目标Y位置的基础,index
是你想要滚动到的项目的索引(例如,40 表示第 40 个子项),而 itemHeight
是每个项目的高度(在我的代码例子中是 130)。所以这个部分的计算结果是目标子项顶部的 Y 坐标(从内容顶部向下计算)。
scroll.GetComponent<RectTransform>().rect.height / 2
这是通过 scroll
获取的 Scroll Rect
的高度的一半。目的是计算滚动区域的中心位置,因为你想要把指定的项滚动到中心位置。如果不考虑 Scroll Rect
的高度,直接使用 index * itemHeight
的计算可能会导致目标项被移动到视口的顶部,而不是中心。
+ (itemHeight / 2)
:这个是为了调整计算的目标位置,使目标项的中心位置对齐到视口的中心。子项的 Y 坐标是从顶部往下计算的,而为了确保指定的项目在中心位置显示,你需要把子项高度的一半加上去,确保目标项的中心对齐。
至于targetPosition += 320; 这一行代码放到后面说。接着我们就用常用的差值算法计算出content的起始y值,然后平滑移动到目标y值。
这个320是怎么算出来的呢?首先进入编辑器界面,然后看随便一个子项的Y值,这里我以我的第40个子项为例:
它的Y值是-5135,接着我们将滚动列表滑动到5135的位置:
可以看到正好第40个子项在中间,那么是不是只要将content的Y值 设置为 对应子项的Y值(去掉负号)就可以了?
那么这样就简单了,我们直接注释掉targetPosition += 320;这一行代码:
然后运行游戏,直接看content的Y值:
content的Y值为4815,我们只要将content的Y值改为第40个子项也就是5135的位置就可以了,5135-4815 = 320 这就是targetPosition += 320; 这行代码的由来。取消这行代码的注释,再次运行游戏:
运行结果为:
content的Y值变为5135了,非常的正确。
看到这里是不是觉得原理非常简单,当然在你的实际项目中这个差值一定不是320,但是根据我的思路来计算出这个差值就可以实现了,只要保证子项的高度一致,并且没有空隙就可以实现这种效果。
制作不易,点个赞吧~
可以关注我的公众号,获取更多内容和Unity精美资产分享