最近在做一个ui循环滚动的功能,网上找了半天脚本感觉都和我实际需求不太符合,自己花费一些时间完成了这个功能记录一下。下面开始正题
我是采用unity自带组件Scroll View来完成,首先设置Scroll View如下图
面板层级结构如下
然后开始编写代码,我就直接贴了,不懂可以看一下注释。
using DG.Tweening;
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 != null ) { tempCentreName.text = tempCentre.name; }
if (tempHVType == HVType.Horizontal)
{
startPos = itemList[0].position;
startPos.x -= itemList[0].GetComponent<RectTransform>().rect.width + itemList[0].GetComponent<RectTransform>().rect.width / 2;
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 + itemList[0].GetComponent<RectTransform>().rect.height / 2;
endPos = itemList[itemList.Count - 1].position;
endPos.y -= itemList[itemList.Count - 1].GetComponent<RectTransform>().rect.height;
//print(endPos);
}
//设置第一个坐标与最后一个坐标位置
//求出最远距离
for (int i = 0; i < itemList.Count; i++)
{
var dis = Vector3.Distance(itemList[i].position, Centre.position);
if (dis > MaxDis)
{
MaxDis = dis;
MinDis = dis;
}
}
if (Left)
{
Left.onClick.AddListener(LBtn);
Right.onClick.AddListener(RBtn);
}
isDrag = true;
}
float currentX = 0, currentX2;//当前最后一位的坐标
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;
// 设置每个元素的位置
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(currentX + elementWidth / 2f, element.anchoredPosition.y);
// 更新下一个元素的X坐标
currentX += elementWidth + SpacingDistance;
}
else
{
float elementWidth = element.sizeDelta.y;
// 设置元素的位置
element.anchoredPosition = new Vector2(element.anchoredPosition.x, currentX + elementWidth / 2f);
// 更新下一个元素的X坐标
currentX -= 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(currentX + elementWidth / 2, 0, 0);
currentX += elementWidth + SpacingDistance;
currentX2 += SpacingDistance + elementWidth;
}
else
if (currentItem.position.x > endPos.x)
{
currentX2 -= SpacingDistance + elementWidth;
currentX -= elementWidth + SpacingDistance;
currentItem.GetComponent<RectTransform>().anchoredPosition = new Vector3((currentX2 + elementWidth / 2), 0, 0);
}
}
else
{
var elementWidth = currentItem.GetComponent<RectTransform>().sizeDelta.y;
if (currentItem.position.y > startPos.y)//向下托 当前坐标比初始坐标高
{
currentItem.GetComponent<RectTransform>().anchoredPosition = new Vector3(0, (currentX + elementWidth / 2), 0);
currentX -= elementWidth + SpacingDistance;
currentX2 -= SpacingDistance + elementWidth;
}
else
if (currentItem.position.y < endPos.y)//向下托 当前坐标比终点坐标高
{
currentX2 += SpacingDistance + elementWidth;
currentX += elementWidth + SpacingDistance;
currentItem.GetComponent<RectTransform>().anchoredPosition = new Vector3(0, (currentX2 + 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)
{
Controller.JTtime = 0;
Controller.JianT.gameObject.SetActive(false);
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)
{
Controller.JTtime = 0;
Controller.JianT.gameObject.SetActive(false);
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();
ScaleDistance();
Adsorption();
}
public void OnBeginDrag(PointerEventData eventData)
{
StartPos = eventData.position;
isAdsorption = false;
isScale = false;
Controller.JTtime = 0;
Controller.JianT.gameObject.SetActive(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;
}
}
下面这个是脚本设置,主要关注的就是公开的变量我都有注释,结合上面的面板图很轻易就能看出来,间隔距离是两个图片之间的距离,缩放倍数是中间最大是多少倍,根据你工程设置,默认设置1就可以了,最后面的text是显示居中ui的名字,名字的设置是根据图片名称来的
最后说下使用了Dotween插件,完成上面的设置就可以畅快玩耍了~~