Unity 滑动列表近大远小吸附功能
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
/// <summary>
/// 自动吸附的滑动列表
/// </summary>
[RequireComponent(typeof(ScrollRect))]
public class ScrollRect_AutoAdsorption : UIBase
{
[Header("滑动列表类型")]
public ScrollRectType scrollRectType;
[Header("能否滑动多页")]
public bool canMultiPageSlide;
[Header("是否使用动态缩放")]
public bool useDynamicScale;
[HideInInspector]
public float scaleCoe = 0.25f;
[Header("横向滑动系数")]
public float h_lerp = 0.2f;
[Header("纵向滑动系数")]
public float v_lerp = 0.2f;
int totalPage;//总页数
int curPage;//当前页
float[] eachPageNormalizedPos;//每页坐标
ScrollRect scrollRect;//滑动区域
RectTransform content;//Content
RectTransform viewport;//viewport
GridLayoutGroup layout;//布局
private void Awake()
{
scrollRect = GetComponent<ScrollRect>();
content = scrollRect.content;
viewport = scrollRect.viewport;
layout = content.GetComponent<GridLayoutGroup>();
if (layout == null)
{
Debug.LogError("Content下找不到GridLayoutGroup组件");
}
layout.constraint = scrollRectType == ScrollRectType.Horizontal
? GridLayoutGroup.Constraint.FixedRowCount
: GridLayoutGroup.Constraint.FixedColumnCount;
layout.constraintCount = 1;
scrollRect.horizontal = scrollRectType == ScrollRectType.Horizontal;
scrollRect.vertical = scrollRectType == ScrollRectType.Vertical;
Init();
}
/// <summary>
/// 初始化
/// </summary>
public void Init()
{
curPage = 1;
SetButtonState();
totalPage = content.childCount;
//计算每页的归一化坐标
eachPageNormalizedPos = new float[totalPage];
if (scrollRectType == ScrollRectType.Horizontal)
{
float tempNormalizedPos = 0;
for (int i = 0; i < eachPageNormalizedPos.Length; i++)
{
eachPageNormalizedPos[i] = tempNormalizedPos;
tempNormalizedPos += 1f / (totalPage - 1);
}
}
else if (scrollRectType == ScrollRectType.Vertical)
{
float tempNormalizedPos = 1;
for (int i = 0; i < eachPageNormalizedPos.Length; i++)
{
eachPageNormalizedPos[i] = tempNormalizedPos;
tempNormalizedPos -= 1f / (totalPage - 1);
}
}
//设置Content
content.sizeDelta = scrollRectType == ScrollRectType.Horizontal
? new Vector2((totalPage - 1) * (layout.cellSize.x + layout.spacing.x), viewport.rect.height)
: new Vector2(0, viewport.rect.height + (totalPage - 1) * (layout.cellSize.y + layout.spacing.y));
scrollRect.horizontalNormalizedPosition = 0;
scrollRect.verticalNormalizedPosition = 1;
}
bool inDrag;//是否在拖拽中
float beginDragPos;//开始拖拽的位置
float endDragPos;//结束拖拽的位置
public void OnBeginDrag()
{
inDrag = true;
beginDragPos = scrollRectType == ScrollRectType.Horizontal
? Input.mousePosition.x
: Input.mousePosition.y;
}
public void OnEndDrag()
{
inDrag = false;
endDragPos = scrollRectType == ScrollRectType.Horizontal
? Input.mousePosition.x
: Input.mousePosition.y;
float curNormalizedPos = scrollRectType == ScrollRectType.Horizontal
? scrollRect.horizontalNormalizedPosition
: scrollRect.verticalNormalizedPosition;
float offset = endDragPos - beginDragPos;
//多页滑动
if (canMultiPageSlide)
{
curPage = FindNearlyPage(curNormalizedPos);
SetButtonState();
}
//单页滑动
else
{
int nearlyPage = FindNearlyPage(curNormalizedPos);
if (nearlyPage != curPage)
{
if (offset > 0)
{
curPage = scrollRectType == ScrollRectType.Horizontal
? curPage - 1 < 1 ? 1 : curPage - 1
: curPage + 1 > totalPage ? curPage : curPage + 1;
SetButtonState();
}
else if (offset < 0)
{
curPage = scrollRectType == ScrollRectType.Horizontal
? curPage + 1 > totalPage ? curPage : curPage + 1
: curPage - 1 < 1 ? 1 : curPage - 1;
SetButtonState();
}
}
}
}
private void Update()
{
if (layout == null) return;
if (!inDrag)
{
scrollRect.normalizedPosition = scrollRectType == ScrollRectType.Horizontal
? new Vector2(Mathf.Lerp(scrollRect.horizontalNormalizedPosition, eachPageNormalizedPos[curPage - 1], h_lerp), 0)
: new Vector2(0, Mathf.Lerp(scrollRect.verticalNormalizedPosition, eachPageNormalizedPos[curPage - 1], v_lerp));
}
if (useDynamicScale)
{
UpdateScale();
}
}
private void SetButtonState()
{
if (curPage == 1)
{
SendMessageUpwards("SetLeftButtonState",false);
SendMessageUpwards("SetRightButtonState",true);
}
else if(curPage == totalPage)
{
SendMessageUpwards("SetLeftButtonState",true);
SendMessageUpwards("SetRightButtonState",false);
}
else
{
SendMessageUpwards("SetLeftButtonState",true);
SendMessageUpwards("SetRightButtonState",true);
}
}
/// <summary>
/// 更新缩放
/// </summary>
void UpdateScale()
{
Vector2 centerPos = transform.position;
//得到所有项
GameObject[] items = new GameObject[content.childCount];
for (int i = 0; i < items.Length; i++)
{
items[i] = content.GetChild(i).gameObject;
}
foreach (var item in items)
{
Vector2 itemPos = item.transform.position;
if (Mathf.Abs(itemPos.x - centerPos.x) <= 390f)
{
var offsetX = 1 - Mathf.Abs(itemPos.x - centerPos.x)/390f * scaleCoe;
item.LocalScale(offsetX);
if (Mathf.Abs(offsetX - 1f) < 0.05f)
{
SetActive(item, true, "Image1");
SetActive(item, false, "Image2");
SetActive(item, false,"BlackMask");
}
else
{
SetActive(item, false, "Image1");
SetActive(item, true, "Image2");
SetActive(item, true,"BlackMask");
}
}
}
}
/// <summary>
/// 查找最近的页
/// </summary>
int FindNearlyPage(float curNormalizedPos)
{
int tempPage = 1;
float tempMinOffset = Mathf.Abs(curNormalizedPos - eachPageNormalizedPos[0]);
for (int i = 1; i <= totalPage; i++)
{
float tempOffset = Mathf.Abs(curNormalizedPos - eachPageNormalizedPos[i - 1]);
if (tempOffset < tempMinOffset)
{
tempMinOffset = tempOffset;
tempPage = i;
}
}
return tempPage;
}
#region interface
/// <summary>
/// 设置页数
/// </summary>
public void SetPage(int page)
{
curPage = page;
SetButtonState();
}
public int GetPage()
{
return curPage;
}
#endregion
}
/// <summary>
/// 滑动列表类型
/// </summary>
public enum ScrollRectType
{
Horizontal,
Vertical,
}
#if UNITY_EDITOR
/// <summary>
/// 自动吸附的滑动列表Inspector扩展
/// </summary>
[UnityEditor.CustomEditor(typeof(ScrollRect_AutoAdsorption))]
public class ScrollRect_AutoAdsorption_Editor : UnityEditor.Editor
{
ScrollRect_AutoAdsorption t;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
t = target as ScrollRect_AutoAdsorption;
if (t.useDynamicScale)
{
t.scaleCoe = UnityEditor.EditorGUILayout.FloatField("缩放系数", t.scaleCoe);
}
}
}
#endif
复写滑动列表的拖拽方法
public class AutoScrollRect : ScrollRect
{
private ScrollRect_AutoAdsorption autoAdsorption;
protected void Awake()
{
autoAdsorption = GetComponent<ScrollRect_AutoAdsorption>();
}
public override void OnBeginDrag(PointerEventData eventData)
{
autoAdsorption.OnBeginDrag();
base.OnBeginDrag(eventData);
}
public override void OnEndDrag(PointerEventData eventData)
{
TimerManager.Add(0.1f, id =>
{
autoAdsorption.OnEndDrag();
});
base.OnEndDrag(eventData);
}
}

浙公网安备 33010602011771号