展示效果
原理:
当停止滑动时
判断Contet的horizontalNormalizedPosition
与子Item的缓存值 相减,并得到最小值,然后将Content horizontalNormalizedPosition滚动过去
使用方式:
直接将脚本挂到ScrollRect上
注意:在创建Content子物体时 或子物体数量变更,需要调用Refresh
代码:
namespace ShangShangQian.Component
{
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Linq;
using UnityEngine.Events;
[RequireComponent(typeof(ScrollRect))]
public class SnapScrollRect : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
private RectTransform content;
private ScrollRect rect;
/// <summary>
/// content 的位置应该滚到什么
/// </summary>
public float target = 1;
/// <summary>
/// 回滚的速度 0-1 越小越快
/// </summary>
public float smooting = 0.25f;
public float currentVelocity;
public float horizontalNormalizedPosition;
public List<float> values = new List<float>();
public List<float> distances = new List<float>();
public List<RectTransform> items = new List<RectTransform>();
/// <summary>
/// 是否拖拽中
/// </summary>
public bool isDrag = false;
/// <summary>
/// 到最近item的距离
/// </summary>
public float distanceMin;
/// <summary>
/// 到最近item的索引
/// </summary>
public int selectIndex;
/// <summary>
/// 是否匹配滑动中
/// </summary>
public bool Snaping = false;
/// <summary>
/// 回滚完毕后调用
/// </summary>
public UnityEvent<int> OnSnap;
void Start()
{
rect = GetComponent<ScrollRect>();
content = rect.content;
}
/// <summary>
/// 刷新数据,content的子物体数量变更时需要调用此函数
/// </summary>
public void Refresh()
{
items.Clear();
values.Clear();
for (int i = 0; i < content.childCount; i++)
{
if (content.GetChild(i).gameObject.activeInHierarchy)
{
items.Add(content.GetChild(i).GetComponent<RectTransform>());
}
}
//累加的变量
values.Add(0);
//每一个格子的所占比多少
float v = 1f / (items.Count - 1);
for (int i = 1; i < items.Count; i++)
{
values.Add(i * v);
}
//不同子元素数量 item 对应 的content horizontalNormalizedPosition 数值
//1 0
//2 0 1
//3 0 0.5 1
//4 0 0.33 0.66 1
}
void Update()
{
#if UNITY_EDITOR
if (Input.GetKeyDown(KeyCode.A))
{
Refresh();
}
#endif
}
void FixedUpdate()
{
horizontalNormalizedPosition = rect.horizontalNormalizedPosition;
if (isDrag)
{
return;
}
if (Snaping)
{
rect.horizontalNormalizedPosition = Mathf.SmoothDamp(rect.horizontalNormalizedPosition, target, ref currentVelocity, smooting);
if (Mathf.Abs(rect.horizontalNormalizedPosition - values[selectIndex]) < 0.001f)
{
Snaping = false;
Debug.Log("回滚:" + selectIndex);
OnSnap.Invoke(selectIndex);
}
}
}
public void OnBeginDrag(PointerEventData eventData)
{
isDrag = true;
}
public void OnDrag(PointerEventData eventData)
{
isDrag = true;
}
public void OnEndDrag(PointerEventData eventData)
{
isDrag = false;
Snaping = true;
distances.Clear();
//当松手后判断那个距离最近
for (int i = 0; i < values.Count; i++)
{
distances.Add(Mathf.Abs(rect.horizontalNormalizedPosition - values[i]));
}
distanceMin = distances.Min();
selectIndex = distances.FindIndex(b => b == distanceMin);
target = values[selectIndex];
rect.StopMovement();
}
/// <summary>
/// 滚到到指定位置
/// </summary>
/// <param name="index"></param>
public void SnapToIndex(int index)
{
Snaping = true;
selectIndex = index;
target = values[selectIndex];
}
}
}