using UnityEngine;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System;
public class ScrollPage : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
ScrollRect rect;
//页面:0,1,2,3 索引从0开始
//每页占的比列:0/3=0 1/3=0.333 2/3=0.6666 3/3=1
//float[] pages = { 0f, 0.333f, 0.6666f, 1f };
List<float> pages = new List<float>();
int currentPageIndex = -1;
//滑动速度
public float smooting = 4;
//滑动的起始坐标
float targethorizontal = 0;
//是否拖拽结束
bool isDrag = false;
float startime = 0f;
float delay = 0.1f;
void Start()
{
rect = transform.GetComponent<ScrollRect>();
startime = Time.time;
}
void Update()
{
if (Time.time < startime + delay) return;
UpdatePages();
//如果不判断。当在拖拽的时候要也会执行插值,所以会出现闪烁的效果
//这里只要在拖动结束的时候。在进行插值
if (!isDrag && pages.Count>0)
{
rect.horizontalNormalizedPosition =
Mathf.Lerp(rect.horizontalNormalizedPosition, targethorizontal, Time.deltaTime * smooting);
}
}
public void OnBeginDrag(PointerEventData eventData)
{
isDrag = true;
}
public void OnEndDrag(PointerEventData eventData)
{
isDrag = false;
float posX = rect.horizontalNormalizedPosition;
int index = 0;
//假设离第一位最近
float offset = Mathf.Abs(pages[index] - posX);
for (int i = 1; i < pages.Count; i++)
{
float temp = Mathf.Abs(pages[i] - posX);
if (temp < offset)
{
index = i;
//保存当前的偏移量
//如果到最后一页。反翻页。所以要保存该值,如果不保存。你试试效果就知道
offset = temp;
}
}
if(index!=currentPageIndex)
{
currentPageIndex = index;
}
targethorizontal = pages[index];
}
void UpdatePages()
{
// 获取子对象的数量
int count = this.rect.content.childCount;
int temp = 0;
for(int i=0; i<count; i++)
{
if(this.rect.content.GetChild(i).gameObject.activeSelf)
{
temp++;
}
}
count = temp;
if (pages.Count!=count)
{
if (count != 0)
{
pages.Clear();
for (int i = 0; i < count; i++)
{
float page = 0;
if(count!=1)
page = i / ((float)(count - 1));
pages.Add(page);
//Debug.Log(i.ToString() + " page:" + page.ToString());
}
}
OnEndDrag(null);
}
}
}
UIElasticScroll.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;
using LuaInterface;
namespace AQ
{
[RequireComponent(typeof(ScrollRect))]
public class UIElasticScroll : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
{
enum Dire
{
Horizontal,
Vertical
}
Vector3 VECTOR_ONE = Vector3.one;
[SerializeField]
float startNormalized;
[SerializeField]
float endNormalized;
bool isDrag;
int targetIndex;
ScrollRect scroll;
[SerializeField]
[Range(0, 100)]
float smoothSpeed = 0.1f; // 弹性停靠的速度
[SerializeField]
[Range(0, 5)]
int expandMoveCountMax = 1; // 边缘最多再移动多少个孩子的距离
[SerializeField]
[Range(-1, 1)]
float oneChildStep = 0.3f; // 只有一个孩子时的滚动最大步长
[SerializeField]
Dire dire = Dire.Horizontal; // 滚动方向
[SerializeField]
AnimationCurve scaleCurve; // 孩子的尺寸大小与滚动中心的距离的关系
LuaFunction dragHandler;
LuaFunction endDragHandler;
private void Start()
{
if (scroll == null)
{
scroll = GetComponent<ScrollRect>();
}
isDrag = false;
}
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
{
isDrag = true;
}
void IDragHandler.OnDrag(PointerEventData eventData)
{
if (dragHandler!=null)
{
var centerIndex = CalTargetIndex();
Debug.LogFormat("dragHandler.CallAndDispose: {0}", targetIndex);
dragHandler.Call(centerIndex);
}
}
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{
isDrag = false;
targetIndex = CalTargetIndex();
if (endDragHandler != null)
{
Debug.LogFormat("endDragHandler.CallAndDispose: {0}", targetIndex);
endDragHandler.Call(targetIndex);
}
}
int CalTargetIndex()
{
var contentChildCount = ContentChildCount;
if (contentChildCount <= 1)
{
return 0;
}
var step = (endNormalized - startNormalized) / (contentChildCount - 1);
var curNormalizedValue = CurNormalized;
if ((endNormalized - startNormalized > 0 && curNormalizedValue < startNormalized)
|| (endNormalized - startNormalized <= 0 && curNormalizedValue > startNormalized))
{
return 0;
}
if ((endNormalized - startNormalized > 0 && curNormalizedValue > endNormalized)
|| (endNormalized - startNormalized <= 0 && curNormalizedValue < endNormalized))
{
return contentChildCount - 1;
}
var criticalDist = Mathf.Abs(step / 2);
for (int index = 0; index < contentChildCount; index++)
{
var dist = (startNormalized + step * index) - curNormalizedValue;
dist = Mathf.Abs(dist);
if (dist <= criticalDist)
{
return index;
}
}
// will not to here
return 0;
}
private void Update()
{
if (isDrag)
{
Constraint();
}
else
{
Elastic();
}
ScaleChild();
}
void Constraint()
{
var contentChildCount = ContentChildCount;
if (contentChildCount < 1)
{
SetNormalized(startNormalized);
return;
}
var curNormalizedValue = CurNormalized;
var step = contentChildCount == 1 ? oneChildStep : (endNormalized - startNormalized) / (contentChildCount - 1);
var startConstraint = startNormalized + step * -1;
var endConstraint = endNormalized + step * 1;
var min = startConstraint <= endConstraint ? startConstraint : endConstraint;
var max = startConstraint <= endConstraint ? endConstraint : startConstraint;
var constraintValue = Mathf.Clamp(curNormalizedValue, min, max);
SetNormalized(constraintValue);
}
void Elastic()
{
var contentChildCount = ContentChildCount;
if (contentChildCount <= 1)
{
SetNormalized(startNormalized);
return;
}
if (targetIndex < 0 || targetIndex > contentChildCount - 1)
{
targetIndex = 0;
}
var step = (endNormalized - startNormalized) / (contentChildCount - 1);
var targetNormalized = startNormalized + step * targetIndex;
var curNormalizedValue = CurNormalized;
var ratio = Mathf.Clamp01(smoothSpeed * Time.deltaTime);
var normalizeValue = Mathf.Lerp(curNormalizedValue, targetNormalized, ratio);
SetNormalized(normalizeValue);
}
void ScaleChild()
{
var contentChildCount = ContentChildCount;
if (scaleCurve == null || contentChildCount == 0)
{
return;
}
var curNormalizedValue = CurNormalized;
if (contentChildCount == 1)
{
var child = GetChild(0);
if (child)
{
child.localScale = VECTOR_ONE * scaleCurve.Evaluate(0f);
}
return;
}
var step = (endNormalized - startNormalized) / (contentChildCount - 1);
for (int childIndex = 0; childIndex < contentChildCount; childIndex++)
{
var child = GetChild(childIndex);
if (child)
{
var childNormalized = startNormalized + step * childIndex;
var dist = Mathf.Abs(childNormalized - curNormalizedValue);
if (step != 0)
{
dist = Mathf.Abs(dist / step);
}
var scaleRatio = scaleCurve.Evaluate(dist);
child.localScale = VECTOR_ONE * scaleRatio;
}
}
}
Transform GetChild(int childIndex)
{
var contentTran = scroll.content.transform;
var allCount = contentTran.childCount;
var curIndex = -1;
for(int i = 0; i < allCount; i++)
{
var child = contentTran.GetChild(i);
if (child.gameObject.activeSelf && ++curIndex==childIndex)
{
return child;
}
}
return null;
}
int ContentChildCount
{
get
{
if (scroll.content)
{
var contentTran = scroll.content.transform;
var allCount = contentTran.childCount;
var realCount = 0;
for(int i = 0; i < allCount; i++)
{
if (contentTran.GetChild(i).gameObject.activeSelf)
{
realCount++;
}
}
return realCount;
}
else
{
return 0;
}
}
}
float CurNormalized
{
get
{
return dire == Dire.Horizontal ? scroll.horizontalNormalizedPosition : scroll.verticalNormalizedPosition;
}
}
void SetNormalized(float value)
{
if (dire == Dire.Horizontal)
{
scroll.horizontalNormalizedPosition = value;
}
else
{
scroll.verticalNormalizedPosition = value;
}
}
public bool CheckIsDraging()
{
return isDrag;
}
public void SetTargetIndex(int value)
{
targetIndex = value;
}
public int GetTargetIndex()
{
return isDrag ? -1 : targetIndex;
}
public void AddDragListener(LuaFunction handler)
{
dragHandler = handler;
}
public void AddEndDragListener(LuaFunction handler)
{
endDragHandler = handler;
}
private void OnDestroy()
{
RemoveListener();
}
public void RemoveListener()
{
if (dragHandler != null)
{
dragHandler.Dispose();
}
if(endDragHandler != null)
{
endDragHandler.Dispose();
}
dragHandler = null;
endDragHandler = null;
}
}
}