最近在做一个ui循环滚动的功能,网上找了半天脚本感觉都和我实际需求不太符合,自己花费一些时间完成了这个功能记录一下。下面开始正题
我是采用unity自带组件Scroll View来完成,首先设置Scroll View如下图
首先是竖形循环
把这两个必备物体放进去,中心点创建一个空物体即可
这个地方按照顺序添加item 就可以
如何是横向的循环列表只需要修改脚本的枚举类即可 其他设置一样,如下图
然后开始编写代码,我就直接贴了,不懂可以看一下注释。
using DG.Tweening;
using QFramework;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public enum HVType
{
Vertical,
Horizontal
}
public class LoopScroll : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
{
public HVType tempHVType;
Vector2 StartPos;
[Header("中心点")]
public Transform Centre;
[Header("UI父级")]
public Transform Content;
[Header("间隔距离")]
public float SpacingDistance = 100;
[Header("缩放倍数")]
public float Scale = 1;
[Header("居中吸附速度")]
public float Speed = 10;
[Header("当前居中物体名称")]
public Text tempCentreName;
[Header("左右切换按钮")]
public Button Left, Right;
[Header("居中后UI缩放系数")]
public Vector3 CentreScale = Vector3.one * 1.5f;
public List<Transform> itemList;
Transform tempCentre;//当前居中物体
bool isDrag = false, isAdsorption, isScale, isLRBtn;//拖拽更换当前索引,控制吸附居中,控制缩放,控制左右按钮
float MaxDis, MinDis;//最大最小距离
private Vector3 startPos, endPos, BtnPos;//左边限制位置,右边限制位置
float itemDis;
private void Awake() { }
void Start()
{
ApplyEqualSpacing();//根据默认第一个位置根据设置刷新一遍坐标
isScale = true;
isAdsorption = true;
tempCentre = itemList[0];
if (tempCentreName) { tempCentreName.text = tempCentre.name; }
if (tempHVType == HVType.Horizontal)
{
startPos = itemList[0].position;
startPos.x -= itemList[0].GetComponent<RectTransform>().rect.width;
endPos = itemList[itemList.Count - 1].position;
endPos.x += itemList[itemList.Count - 1].GetComponent<RectTransform>().rect.width;
}
else
{
startPos = itemList[0].position;
startPos.y += itemList[0].GetComponent<RectTransform>().rect.height;
endPos = itemList[itemList.Count - 1].position;
endPos.y -= itemList[itemList.Count - 1].GetComponent<RectTransform>().rect.height;
}
//设置第一个坐标与最后一个坐标位置
//求出最远距离
MinDis = Vector3.Distance(itemList[0].position, Centre.position);
for (int i = 0; i < itemList.Count; i++)
{
var dis = Vector3.Distance(itemList[i].position, Centre.position);
if (dis > MaxDis)
{
MaxDis = dis;
}
else if (dis < MinDis) { MinDis = dis; }
}
if (Left)
{
Left.onClick.AddListener(LBtn);
Right.onClick.AddListener(RBtn);
}
isDrag = true;
}
float current = 0, current2;//当前最后一位的坐标
void ApplyEqualSpacing()
{
// 计算总宽度
float totalWidth = 0f;
foreach (RectTransform element in itemList)
{
if (tempHVType == HVType.Horizontal)
totalWidth += element.sizeDelta.x;
else
{
totalWidth += element.sizeDelta.y;
}
}
// 计算间隔的总宽度
float totalSpacing = (itemList.Count - 1) * SpacingDistance;
// 计算每个元素的平均宽度
float averageWidth = (totalWidth + totalSpacing) / itemList.Count;
// 设置每个元素的位置
if (tempHVType == HVType.Horizontal)
{
current = itemList[0].GetComponent<RectTransform>().anchoredPosition.x;
current2 = itemList[0].GetComponent<RectTransform>().anchoredPosition.x;
}
else
{
current = itemList[0].GetComponent<RectTransform>().anchoredPosition.y;
current2 = itemList[0].GetComponent<RectTransform>().anchoredPosition.y;
}
for (int i = 0; i < itemList.Count; i++)
{
RectTransform element = itemList[i].GetComponent<RectTransform>();
if (tempHVType == HVType.Horizontal)
{
float elementWidth = element.sizeDelta.x;
// 设置元素的位置
element.anchoredPosition = new Vector2(current + elementWidth / 2f, element.anchoredPosition.y);
// 更新下一个元素的X坐标
current += elementWidth + SpacingDistance;
}
else
{
float elementWidth = element.sizeDelta.y;
// 设置元素的位置
element.anchoredPosition = new Vector2(element.anchoredPosition.x, current + elementWidth / 2f);
// 更新下一个元素的X坐标
current -= elementWidth + SpacingDistance;
}
}
}
void Islimit() //设置坐标切换与列表内元素与面板层级切换 保证层级与列表内数据同步
{
if (isDrag)
{
for (int i = 0; i < itemList.Count; i++)
{
var currentItem = itemList[i];
if (tempHVType == HVType.Horizontal)
{
var elementWidth = currentItem.GetComponent<RectTransform>().sizeDelta.x;
if (currentItem.position.x < startPos.x)
{
currentItem.GetComponent<RectTransform>().anchoredPosition = new Vector3(current + elementWidth / 2, 0, 0);
current += elementWidth + SpacingDistance;
current2 += SpacingDistance + elementWidth;
}
else
if (currentItem.position.x > endPos.x)
{
current2 -= SpacingDistance + elementWidth;
current -= elementWidth + SpacingDistance;
currentItem.GetComponent<RectTransform>().anchoredPosition = new Vector3((current2 + elementWidth / 2), 0, 0);
}
}
else
{
var elementWidth = currentItem.GetComponent<RectTransform>().sizeDelta.y;
if (currentItem.position.y > startPos.y)//向下托 当前坐标比初始坐标高
{
currentItem.GetComponent<RectTransform>().anchoredPosition = new Vector3(currentItem.GetComponent<RectTransform>().anchoredPosition.x, (current + elementWidth / 2), 0);
current -= elementWidth + SpacingDistance;
current2 -= SpacingDistance + elementWidth;
}
else
if (currentItem.position.y < endPos.y)//向下托 当前坐标比终点坐标高
{
current2 += SpacingDistance + elementWidth;
current += elementWidth + SpacingDistance;
currentItem.GetComponent<RectTransform>().anchoredPosition = new Vector3(currentItem.GetComponent<RectTransform>().anchoredPosition.x, (current2 + elementWidth / 2), 0);
}
}
}
}
}
void ScaleDistance()//根据百分比设置缩放动画
{
if (isScale)
{
if (tempCentre)
{
for (int i = 0; i < itemList.Count; i++)
{
if (tempCentre == itemList[i])
{
var scale = Vector3.Lerp(itemList[i].localScale, CentreScale, Speed * Time.deltaTime);
tempCentre.localScale = scale;
if (Vector3.Distance(tempCentre.localScale, CentreScale) < 0.01f)
{
isLRBtn = false;
isScale = false;
}
}
else
{
var scale = Vector3.Lerp(itemList[i].localScale, Vector3.one, Speed * Time.deltaTime);
itemList[i].localScale = scale;
}
}
}
}
}
void Adsorption() //停止滑动进行吸附
{
if (isAdsorption)
{
if (tempCentre)
{
var dis = Centre.position - tempCentre.position;
if (tempHVType == HVType.Horizontal)
{
var pos = Vector3.Lerp(Content.position, new Vector3(Content.position.x + dis.x,
Content.position.y, Content.position.z), Speed * Time.deltaTime);
Content.position = pos;
if (Vector3.Distance(new Vector3(Content.position.x + dis.x,
Content.position.y, Content.position.z), Content.position) < 0.01f)
{
isAdsorption = false;
}
}
else
{
var pos = Vector3.Lerp(Content.position, new Vector3(Content.position.x,
Content.position.y + dis.y, Content.position.z), Speed * Time.deltaTime);
Content.position = pos;
if (Vector3.Distance(new Vector3(Content.position.x,
Content.position.y + dis.y, Content.position.z), Content.position) < 0.01f)
{
isAdsorption = false;
}
}
}
}
}
public void LBtn()
{
if (!isLRBtn)
{
if (tempHVType == HVType.Horizontal)
{
itemDis = tempCentre.GetComponent<RectTransform>().rect.width + SpacingDistance;
BtnPos = new Vector3(Content.position.x + itemDis,
Content.position.y, Content.position.z);
}
else
{
itemDis = tempCentre.GetComponent<RectTransform>().rect.height + SpacingDistance;
BtnPos = new Vector3(Content.position.x,
Content.position.y + itemDis, Content.position.z);
}
Content.DOMove(BtnPos, 0.5f).OnComplete(() =>
{
isDrag = true;
FindMinDis();
isAdsorption = true;
isScale = true;
});
isLRBtn = true;
}
}
public void RBtn()
{
if (!isLRBtn)
{
if (tempHVType == HVType.Horizontal)
{
itemDis = tempCentre.GetComponent<RectTransform>().rect.width + SpacingDistance;
BtnPos = new Vector3(Content.position.x - itemDis,
Content.position.y, Content.position.z);
}
else
{
itemDis = tempCentre.GetComponent<RectTransform>().rect.height + SpacingDistance;
BtnPos = new Vector3(Content.position.x,
Content.position.y - itemDis, Content.position.z);
}
Content.DOMove(BtnPos, 0.5f).OnComplete(() =>
{
isDrag = true;
FindMinDis();
isAdsorption = true;
isScale = true;
});
isLRBtn = true;
}
}
void FindMinDis() //找出距离中心点最近的
{
for (int i = 0; i < itemList.Count; i++)
{
float dis = Vector3.Distance(itemList[i].position, Centre.position);
if (dis < MinDis)
{
tempCentre = itemList[i];
if (tempCentreName)
{
tempCentreName.text = itemList[i].name;
}
MinDis = dis;
}
}
MinDis = 1000;
}
void Update()
{
Islimit();
Adsorption();
ScaleDistance();
}
public void OnBeginDrag(PointerEventData eventData)
{
StartPos = eventData.position;
isAdsorption = false;
isScale = false;
}
public void OnEndDrag(PointerEventData eventData)
{
transform.GetComponent<ScrollRect>().velocity = Vector3.one * 0.5f;
FindMinDis();
isAdsorption = true;
isScale = true;
isDrag = false;
}
public void OnDrag(PointerEventData eventData)
{
isDrag = true;
}
}
最后说下使用了Dotween插件,完成上面的设置就可以畅快玩耍了~~