1029 lines
29 KiB
C#
1029 lines
29 KiB
C#
using LuaInterface;
|
||
using UnityEngine;
|
||
using UnityEngine.EventSystems;
|
||
using UnityEngine.UI;
|
||
using System.Collections.Generic;
|
||
|
||
public class PageSliderManager : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||
{
|
||
[Header("页面设置")]
|
||
[SerializeField] private Transform[] pageTitle;
|
||
[SerializeField] private Animator[] pageAnimator;
|
||
[SerializeField] private Transform[] pages; // 所有页面Transform数组
|
||
[SerializeField] private int currentPageIndex = 0; // 当前页面索引
|
||
|
||
[Header("滑动设置")]
|
||
[SerializeField] private float snapSpeed = 15f; // 吸附速度
|
||
[SerializeField] private float snapThreshold = 0.3f; // 吸附阈值(页面宽度比例)
|
||
|
||
[Header("UI设置")]
|
||
[SerializeField] private Graphic dragArea; // 用于拖动的UI区域
|
||
[SerializeField] private bool requireExactClick = true; // 是否要求点击在Image上
|
||
|
||
// 私有变量
|
||
private float pageSpacing = 0f; // 页面间距
|
||
private bool isDragging = false;
|
||
private bool dragValid = false; // 标记拖拽是否有效
|
||
|
||
// 拖动输入记录
|
||
private Vector3 dragStartPosition; // 拖动开始时的位置
|
||
private Vector2 pageDownPosition; // 页面按下时的位置
|
||
private float totalDragDistance = 0f;
|
||
|
||
private Animator animDown;
|
||
private Animator animLast;
|
||
private Animator animNext;
|
||
private EnumDistance disAnimMove = EnumDistance.None;
|
||
private float animTimeDown;
|
||
private float animTimeLast;
|
||
private float animTimeNext;
|
||
private int recordPage = -1;
|
||
|
||
// 添加速度检测相关变量
|
||
private Vector3 lastDragPosition;
|
||
private float dragSpeed;
|
||
private float dragDirection;
|
||
|
||
// 在私有变量部分添加
|
||
private JumpMode currentJumpMode = JumpMode.Animated;
|
||
private int jumpTargetIndex = -1;
|
||
private float jumpProgress = 0f;
|
||
private float jumpDuration = 0.3f; // 跳转动画持续时间
|
||
private float jumpStartX = 0f; // 添加:记录跳转起始位置
|
||
private float jumpTargetX = 0f; // 添加:记录跳转目标位置
|
||
|
||
// 添加新的跳转模式枚举
|
||
private enum JumpMode
|
||
{
|
||
Direct, // 直接跳转
|
||
Animated // 动画跳转
|
||
}
|
||
|
||
// 滑动状态
|
||
private enum SlideState
|
||
{
|
||
Idle, // 空闲
|
||
Dragging, // 拖动中
|
||
Snapping, // 吸附中
|
||
Momentum // 动量滑动
|
||
}
|
||
private SlideState currentState = SlideState.Idle;
|
||
|
||
// 添加动画完成标记
|
||
private bool isAnimationsComplete = true;
|
||
|
||
private void Start()
|
||
{
|
||
CalculatePageSpacing();
|
||
|
||
// 如果没有设置拖拽区域,使用当前GameObject的Graphic组件
|
||
if (dragArea == null)
|
||
{
|
||
dragArea = GetComponent<Graphic>();
|
||
}
|
||
|
||
// 如果需要,可以在这里添加拖拽区域的设置
|
||
if (dragArea != null)
|
||
{
|
||
dragArea.raycastTarget = true;
|
||
}
|
||
}
|
||
|
||
public void SetTitleSpacing(LuaTable table)
|
||
{
|
||
Transform[] transTemp = new Transform[table.Length + 1];
|
||
pageAnimator = new Animator[table.Length + 1];
|
||
for (int i = 0; i < transTemp.Length; i++)
|
||
{
|
||
transTemp[i] = table.RawGetIndex<Transform>(i);
|
||
pageAnimator[i] = transTemp[i].GetChild(0).GetComponent<Animator>();
|
||
}
|
||
pageTitle = transTemp;
|
||
}
|
||
|
||
private void CalculatePageSpacing()
|
||
{
|
||
if (pages == null || pages.Length < 2)
|
||
{
|
||
pageSpacing = Screen.width;
|
||
return;
|
||
}
|
||
|
||
Vector3 page1Pos = pages[0].localPosition;
|
||
Vector3 page2Pos = pages[1].localPosition;
|
||
pageSpacing = Mathf.Abs(page2Pos.x - page1Pos.x);
|
||
|
||
if (pageSpacing < 1f)
|
||
{
|
||
pageSpacing = Screen.width;
|
||
RearrangePages();
|
||
}
|
||
}
|
||
|
||
private void RearrangePages()
|
||
{
|
||
for (int i = 0; i < pages.Length; i++)
|
||
{
|
||
if (pages[i] == null) continue;
|
||
float xPos = i * pageSpacing;
|
||
pages[i].localPosition = new Vector3(xPos, 0, 0);
|
||
}
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
// 处理直接跳转
|
||
if (currentJumpMode == JumpMode.Direct && jumpTargetIndex >= 0)
|
||
{
|
||
HandleDirectJump();
|
||
return;
|
||
}
|
||
|
||
if (currentState == SlideState.Snapping)
|
||
{
|
||
SmoothToTarget();
|
||
}
|
||
|
||
if (currentState != SlideState.Dragging && currentState != SlideState.Snapping)
|
||
{
|
||
UpdateCurrentPageIndex();
|
||
}
|
||
|
||
// 检查动画是否完成
|
||
CheckAnimationsComplete();
|
||
}
|
||
|
||
// 处理直接跳转的方法
|
||
private void HandleDirectJump()
|
||
{
|
||
if (jumpProgress >= 1f)
|
||
{
|
||
// 跳转完成
|
||
currentJumpMode = JumpMode.Animated;
|
||
jumpTargetIndex = -1;
|
||
currentState = SlideState.Idle;
|
||
|
||
// 确保最终位置准确
|
||
Vector3 tempNewPos = pages[0].localPosition;
|
||
tempNewPos.x = jumpTargetX;
|
||
pages[0].localPosition = tempNewPos;
|
||
|
||
// 同步其他页面
|
||
for (int i = 1; i < pages.Length; i++)
|
||
{
|
||
if (pages[i] == null) continue;
|
||
Vector3 pos = tempNewPos + new Vector3(i * pageSpacing, 0, 0);
|
||
pages[i].localPosition = pos;
|
||
}
|
||
|
||
// 确保动画状态正确
|
||
EnsureFinalAnimationStates();
|
||
|
||
return;
|
||
}
|
||
|
||
// 更新进度
|
||
jumpProgress += Time.deltaTime / jumpDuration;
|
||
jumpProgress = Mathf.Clamp01(jumpProgress);
|
||
|
||
// 使用缓动函数进行平滑过渡
|
||
float t = EaseOutCubic(jumpProgress);
|
||
float currentX = Mathf.Lerp(jumpStartX, jumpTargetX, t);
|
||
|
||
// 应用新位置
|
||
Vector3 newPos = pages[0].localPosition;
|
||
newPos.x = currentX;
|
||
pages[0].localPosition = newPos;
|
||
|
||
// 同步其他页面
|
||
for (int i = 1; i < pages.Length; i++)
|
||
{
|
||
if (pages[i] == null) continue;
|
||
Vector3 pos = newPos + new Vector3(i * pageSpacing, 0, 0);
|
||
pages[i].localPosition = pos;
|
||
}
|
||
}
|
||
|
||
// 缓动函数:三次方缓出
|
||
private float EaseOutCubic(float t)
|
||
{
|
||
t -= 1;
|
||
return t * t * t + 1;
|
||
}
|
||
|
||
// IBeginDragHandler接口实现 - 开始拖动
|
||
public void OnBeginDrag(PointerEventData eventData)
|
||
{
|
||
// 检查是否点击在有效区域内
|
||
if (requireExactClick)
|
||
{
|
||
// 检查点击是否在当前对象的RectTransform内
|
||
if (IsPointerOverGameObject(eventData))
|
||
{
|
||
OnDragStart(eventData.position);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果不需要精确点击,直接开始拖拽
|
||
OnDragStart(eventData.position);
|
||
}
|
||
}
|
||
|
||
// IDragHandler接口实现 - 拖动中
|
||
public void OnDrag(PointerEventData eventData)
|
||
{
|
||
if (!dragValid) return;
|
||
|
||
HandleDragging(eventData.position);
|
||
}
|
||
|
||
// IEndDragHandler接口实现 - 结束拖动
|
||
public void OnEndDrag(PointerEventData eventData)
|
||
{
|
||
if (!dragValid) return;
|
||
|
||
OnDragEnd();
|
||
dragValid = false; // 重置拖拽有效性
|
||
}
|
||
|
||
// 检查指针是否在当前GameObject上
|
||
private bool IsPointerOverGameObject(PointerEventData eventData)
|
||
{
|
||
// 创建一个结果列表来接收射线检测结果
|
||
var results = new List<RaycastResult>();
|
||
|
||
// 执行射线检测
|
||
EventSystem.current.RaycastAll(eventData, results);
|
||
|
||
// 检查是否点击到了当前对象
|
||
foreach (var result in results)
|
||
{
|
||
if (result.gameObject == gameObject)
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// 可选的:手动检查点击位置的方法
|
||
public void CheckPointerClick(Vector2 screenPosition)
|
||
{
|
||
// 检查点击是否在当前UI区域内
|
||
if (IsPointInRectTransform(screenPosition))
|
||
{
|
||
OnDragStart(screenPosition);
|
||
dragValid = true;
|
||
}
|
||
else
|
||
{
|
||
dragValid = false;
|
||
}
|
||
}
|
||
|
||
// 检查点是否在RectTransform内
|
||
private bool IsPointInRectTransform(Vector2 screenPosition)
|
||
{
|
||
if (dragArea == null) return false;
|
||
|
||
// 将屏幕坐标转换为本地坐标
|
||
RectTransform rectTransform = dragArea.rectTransform;
|
||
Vector2 localPoint;
|
||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||
rectTransform,
|
||
screenPosition,
|
||
null,
|
||
out localPoint);
|
||
|
||
// 检查点是否在矩形内
|
||
return rectTransform.rect.Contains(localPoint);
|
||
}
|
||
|
||
// 可选的:添加点击事件处理,用于直接跳转页面
|
||
public void OnPageClick(int pageIndex)
|
||
{
|
||
if (currentState == SlideState.Idle)
|
||
{
|
||
GoToPage(pageIndex, false);
|
||
}
|
||
}
|
||
|
||
private void OnDragStart(Vector2 dragPosition)
|
||
{
|
||
dragValid = true;
|
||
isDragging = true;
|
||
currentState = SlideState.Dragging;
|
||
dragStartPosition = dragPosition;
|
||
lastDragPosition = dragPosition; // 初始化最后拖动位置
|
||
|
||
// 记录第一个页面的初始位置
|
||
if (pages[0] != null)
|
||
{
|
||
pageDownPosition = pages[0].localPosition;
|
||
}
|
||
|
||
totalDragDistance = 0f;
|
||
dragSpeed = 0f;
|
||
dragDirection = 0f;
|
||
|
||
if (currentPageIndex == 0)
|
||
{
|
||
animLast = null;
|
||
animNext = pageAnimator[currentPageIndex + 1];
|
||
}
|
||
else if (currentPageIndex == pages.Length - 1)
|
||
{
|
||
animLast = pageAnimator[currentPageIndex - 1];
|
||
animNext = null;
|
||
}
|
||
else
|
||
{
|
||
animLast = pageAnimator[currentPageIndex - 1];
|
||
animNext = pageAnimator[currentPageIndex + 1];
|
||
}
|
||
animDown = pageAnimator[currentPageIndex];
|
||
recordPage = currentPageIndex;
|
||
|
||
// 重置动画完成标记
|
||
isAnimationsComplete = false;
|
||
}
|
||
|
||
private void HandleDragging(Vector2 currentDragPosition)
|
||
{
|
||
if (currentState != SlideState.Dragging) return;
|
||
|
||
// 计算拖动位移
|
||
Vector3 dragDelta = currentDragPosition - (Vector2)dragStartPosition;
|
||
dragDelta.y = 0;
|
||
|
||
// 计算滑动速度(每帧移动的像素距离)
|
||
float deltaX = currentDragPosition.x - lastDragPosition.x;
|
||
dragSpeed = Mathf.Abs(deltaX) / Time.deltaTime;
|
||
dragDirection = Mathf.Sign(deltaX);
|
||
lastDragPosition = currentDragPosition;
|
||
|
||
// 直接设置第一个页面的位置
|
||
if (pages[0] != null)
|
||
{
|
||
Vector3 newPos = (Vector3)pageDownPosition + dragDelta;
|
||
pages[0].localPosition = newPos;
|
||
}
|
||
|
||
// 同步更新其他页面的位置
|
||
for (int i = 1; i < pages.Length; i++)
|
||
{
|
||
if (pages[i] == null) continue;
|
||
|
||
// 其他页面保持与第一个页面的相对位置
|
||
Vector3 originalRelativePos = new Vector3(i * pageSpacing, 0, 0);
|
||
Vector3 newPos = pages[0].localPosition + originalRelativePos;
|
||
pages[i].localPosition = newPos;
|
||
}
|
||
|
||
// 更新总拖动距离
|
||
totalDragDistance = dragDelta.x;
|
||
|
||
// 限制边界
|
||
float minX = -(pages.Length - 1) * pageSpacing;
|
||
float maxX = 0f;
|
||
|
||
Vector3 clampedPos = pages[0].localPosition;
|
||
clampedPos.x = Mathf.Clamp(clampedPos.x, minX, maxX);
|
||
|
||
if (pages[0] != null)
|
||
{
|
||
pages[0].localPosition = clampedPos;
|
||
|
||
// 同步其他页面
|
||
for (int i = 1; i < pages.Length; i++)
|
||
{
|
||
if (pages[i] == null) continue;
|
||
Vector3 pos = clampedPos + new Vector3(i * pageSpacing, 0, 0);
|
||
pages[i].localPosition = pos;
|
||
}
|
||
}
|
||
|
||
// 计算拖动方向
|
||
if (deltaX > 0)
|
||
{
|
||
disAnimMove = EnumDistance.Right;
|
||
}
|
||
else if (deltaX < 0)
|
||
{
|
||
disAnimMove = EnumDistance.Left;
|
||
}
|
||
|
||
// 处理动画
|
||
if (animDown != null)
|
||
{
|
||
animTimeDown = dragDelta.x / pageSpacing;
|
||
StopAtTime(animDown, "open", 1 - Mathf.Abs(animTimeDown));
|
||
}
|
||
|
||
if (dragDelta.x >= 0)
|
||
{
|
||
if (animLast != null)
|
||
{
|
||
animTimeLast = dragDelta.x / pageSpacing;
|
||
StopAtTime(animLast, "open", Mathf.Abs(animTimeLast));
|
||
}
|
||
animTimeNext = dragDelta.x / pageSpacing;
|
||
}
|
||
else
|
||
{
|
||
if (animNext != null)
|
||
{
|
||
animTimeNext = dragDelta.x / pageSpacing;
|
||
StopAtTime(animNext, "open", Mathf.Abs(animTimeNext));
|
||
}
|
||
animTimeLast = dragDelta.x / pageSpacing;
|
||
}
|
||
}
|
||
|
||
void StopAtTime(Animator anim, string stateName, float targetTime)
|
||
{
|
||
// 先播放到目标时间
|
||
anim.Play(stateName, 0, targetTime);
|
||
|
||
// 然后将动画速度设为0
|
||
anim.speed = 0f;
|
||
}
|
||
|
||
private void OnDragEnd()
|
||
{
|
||
if (!dragValid) return;
|
||
|
||
isDragging = false;
|
||
|
||
// 计算当前页面位置
|
||
float currentX = pages[0] != null ? pages[0].localPosition.x : 0f;
|
||
|
||
// 吸附判断逻辑
|
||
float dragPercent = Mathf.Abs(totalDragDistance) / pageSpacing;
|
||
|
||
int targetIndex = currentPageIndex; // 初始化目标索引为当前页
|
||
|
||
// 判断是否是快速滑动
|
||
bool isQuickSwipe = dragSpeed > 500f; // 设置速度阈值,500像素/秒
|
||
|
||
if (dragPercent > snapThreshold)
|
||
{
|
||
// 超过阈值,切换到下一页或上一页
|
||
if (totalDragDistance < 0) // 向左拖动
|
||
{
|
||
targetIndex = Mathf.Min(currentPageIndex + 1, pages.Length - 1);
|
||
}
|
||
else // 向右拖动
|
||
{
|
||
targetIndex = Mathf.Max(currentPageIndex - 1, 0);
|
||
}
|
||
}
|
||
else if (isQuickSwipe)
|
||
{
|
||
// 快速滑动,根据速度方向切换页面
|
||
if (dragDirection < 0) // 向左快速滑动
|
||
{
|
||
targetIndex = Mathf.Min(currentPageIndex + 1, pages.Length - 1);
|
||
}
|
||
else if (dragDirection > 0) // 向右快速滑动
|
||
{
|
||
targetIndex = Mathf.Max(currentPageIndex - 1, 0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 未超过阈值且不是快速滑动,吸附到最近页面
|
||
float normalizedOffset = -currentX / pageSpacing;
|
||
targetIndex = Mathf.RoundToInt(normalizedOffset);
|
||
targetIndex = Mathf.Clamp(targetIndex, 0, pages.Length - 1);
|
||
}
|
||
|
||
// 判断是否需要切换页面
|
||
bool isSwitchingPage = targetIndex != currentPageIndex;
|
||
|
||
// 处理动画
|
||
if (isQuickSwipe)
|
||
{
|
||
// 快速滑动时使用特殊处理
|
||
HandleQuickSwipeAnimation(targetIndex, isSwitchingPage);
|
||
}
|
||
else
|
||
{
|
||
// 正常速度时使用原始逻辑
|
||
HandleNormalAnimation(targetIndex, isSwitchingPage);
|
||
}
|
||
|
||
// 切换页面
|
||
if (targetIndex != currentPageIndex)
|
||
{
|
||
GoToPage(targetIndex, false);
|
||
}
|
||
else
|
||
{
|
||
// 如果目标页面与当前页面相同,直接吸附到当前页
|
||
currentState = SlideState.Snapping;
|
||
}
|
||
|
||
disAnimMove = EnumDistance.None;
|
||
dragValid = false; // 拖拽结束,重置有效性
|
||
}
|
||
|
||
// 处理正常速度的动画
|
||
private void HandleNormalAnimation(int targetIndex, bool isSwitchingPage)
|
||
{
|
||
if (disAnimMove == EnumDistance.Left)
|
||
{
|
||
if (animDown != null)
|
||
{
|
||
if (isSwitchingPage)
|
||
{
|
||
// 切换页面:当前页播放close动画
|
||
animDown.Play("close", -1, Mathf.Abs(animTimeDown));
|
||
}
|
||
else
|
||
{
|
||
// 不切换页面:当前页回到open状态
|
||
animDown.Play("open", -1, 1 - Mathf.Abs(animTimeDown));
|
||
}
|
||
animDown.speed = 1;
|
||
}
|
||
if (animLast != null)
|
||
{
|
||
if (animTimeLast > 0)
|
||
{
|
||
if (isSwitchingPage && targetIndex == recordPage - 1)
|
||
{
|
||
// 切换到上一页:上一页播放open动画
|
||
animLast.Play("open", -1, Mathf.Abs(animTimeLast));
|
||
}
|
||
else
|
||
{
|
||
// 不切换到上一页:上一页播放close动画
|
||
animLast.Play("close", -1, 1 - Mathf.Abs(animTimeLast));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
animLast.Play("close", -1, 1);
|
||
}
|
||
animLast.speed = 1;
|
||
}
|
||
if (animNext != null)
|
||
{
|
||
if (animTimeNext < 0)
|
||
{
|
||
if (isSwitchingPage && targetIndex == recordPage + 1)
|
||
{
|
||
// 切换到下一页:下一页播放open动画
|
||
animNext.Play("open", -1, Mathf.Abs(animTimeNext));
|
||
}
|
||
else
|
||
{
|
||
// 不切换到下一页:下一页播放close动画
|
||
animNext.Play("close", -1, 1 - Mathf.Abs(animTimeNext));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
animNext.Play("close", -1, 1);
|
||
}
|
||
animNext.speed = 1;
|
||
}
|
||
}
|
||
else if (disAnimMove == EnumDistance.Right)
|
||
{
|
||
if (animDown != null)
|
||
{
|
||
if (isSwitchingPage)
|
||
{
|
||
// 切换页面:当前页播放close动画
|
||
animDown.Play("close", -1, Mathf.Abs(animTimeDown));
|
||
}
|
||
else
|
||
{
|
||
// 不切换页面:当前页回到open状态
|
||
animDown.Play("open", -1, 1 - Mathf.Abs(animTimeDown));
|
||
}
|
||
animDown.speed = 1;
|
||
}
|
||
if (animLast != null)
|
||
{
|
||
if (animTimeLast > 0)
|
||
{
|
||
if (isSwitchingPage && targetIndex == recordPage - 1)
|
||
{
|
||
// 切换到上一页:上一页播放open动画
|
||
animLast.Play("open", -1, Mathf.Abs(animTimeLast));
|
||
}
|
||
else
|
||
{
|
||
// 不切换到上一页:上一页播放close动画
|
||
animLast.Play("close", -1, 1 - Mathf.Abs(animTimeLast));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
animLast.Play("close", -1, 1);
|
||
}
|
||
animLast.speed = 1;
|
||
}
|
||
if (animNext != null)
|
||
{
|
||
if (animTimeNext < 0)
|
||
{
|
||
if (isSwitchingPage && targetIndex == recordPage + 1)
|
||
{
|
||
// 切换到下一页:下一页播放open动画
|
||
animNext.Play("open", -1, Mathf.Abs(animTimeNext));
|
||
}
|
||
else
|
||
{
|
||
// 不切换到下一页:下一页播放close动画
|
||
animNext.Play("close", -1, 1 - Mathf.Abs(animTimeNext));
|
||
}
|
||
}
|
||
animNext.speed = 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 无明确方向时,按照原始代码的逻辑
|
||
if (animDown != null)
|
||
{
|
||
if (isSwitchingPage)
|
||
{
|
||
// 切换页面:当前页播放close动画
|
||
animDown.Play("close", -1, Mathf.Abs(animTimeDown));
|
||
}
|
||
else
|
||
{
|
||
// 不切换页面:当前页回到open状态
|
||
animDown.Play("open", -1, 1 - Mathf.Abs(animTimeDown));
|
||
}
|
||
animDown.speed = 1;
|
||
}
|
||
|
||
// 其他页面根据实际情况处理
|
||
if (isSwitchingPage)
|
||
{
|
||
// 目标页播放open动画
|
||
if (pageAnimator[targetIndex] != null)
|
||
{
|
||
// 根据方向计算开始时间
|
||
float startTime = 0f;
|
||
if (targetIndex < recordPage && animLast != null)
|
||
{
|
||
startTime = Mathf.Abs(animTimeLast);
|
||
}
|
||
else if (targetIndex > recordPage && animNext != null)
|
||
{
|
||
startTime = Mathf.Abs(animTimeNext);
|
||
}
|
||
|
||
startTime = Mathf.Clamp01(startTime);
|
||
pageAnimator[targetIndex].Play("open", -1, startTime);
|
||
pageAnimator[targetIndex].speed = 1;
|
||
}
|
||
}
|
||
|
||
// 其他非活动页面设置为close状态
|
||
for (int i = 0; i < pageAnimator.Length; i++)
|
||
{
|
||
if (pageAnimator[i] == null) continue;
|
||
if (i == recordPage || i == targetIndex) continue;
|
||
|
||
pageAnimator[i].Play("close", -1, 1);
|
||
pageAnimator[i].speed = 0; // 不播放动画
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理快速滑动的动画
|
||
private void HandleQuickSwipeAnimation(int targetIndex, bool isSwitchingPage)
|
||
{
|
||
// 快速滑动时,先确保所有动画都能平滑播放
|
||
if (isSwitchingPage)
|
||
{
|
||
// 当前页播放close动画
|
||
if (animDown != null)
|
||
{
|
||
float startTime = Mathf.Abs(animTimeDown);
|
||
startTime = Mathf.Clamp01(startTime);
|
||
animDown.Play("close", -1, startTime);
|
||
animDown.speed = 1;
|
||
}
|
||
|
||
// 目标页播放open动画
|
||
if (pageAnimator[targetIndex] != null)
|
||
{
|
||
float startTime = 0f;
|
||
if (targetIndex < recordPage && animLast != null)
|
||
{
|
||
startTime = Mathf.Abs(animTimeLast);
|
||
}
|
||
else if (targetIndex > recordPage && animNext != null)
|
||
{
|
||
startTime = Mathf.Abs(animTimeNext);
|
||
}
|
||
|
||
startTime = Mathf.Clamp01(startTime);
|
||
pageAnimator[targetIndex].Play("open", -1, startTime);
|
||
pageAnimator[targetIndex].speed = 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 不切换页面,回到原始状态
|
||
if (animDown != null)
|
||
{
|
||
animDown.Play("open", -1, 1 - Mathf.Abs(animTimeDown));
|
||
animDown.speed = 1;
|
||
}
|
||
}
|
||
|
||
// 确保其他页面重置到正确状态
|
||
ResetAllAnimations(targetIndex, isSwitchingPage);
|
||
}
|
||
|
||
// 重置所有动画状态
|
||
private void ResetAllAnimations(int targetIndex, bool isSwitchingPage)
|
||
{
|
||
// 所有其他页面设置到close状态
|
||
for (int i = 0; i < pageAnimator.Length; i++)
|
||
{
|
||
if (pageAnimator[i] == null) continue;
|
||
|
||
if (isSwitchingPage)
|
||
{
|
||
if (i == recordPage || i == targetIndex) continue;
|
||
}
|
||
else
|
||
{
|
||
if (i == recordPage) continue;
|
||
}
|
||
|
||
// 直接设置到close状态的结束帧,并停止播放
|
||
pageAnimator[i].Play("close", -1, 1);
|
||
pageAnimator[i].speed = 0;
|
||
}
|
||
}
|
||
|
||
// 检查动画是否完成
|
||
private void CheckAnimationsComplete()
|
||
{
|
||
if (isAnimationsComplete) return;
|
||
|
||
bool allComplete = true;
|
||
|
||
// 检查当前页面的动画状态
|
||
if (animDown != null && animDown.speed > 0)
|
||
{
|
||
AnimatorStateInfo stateInfo = animDown.GetCurrentAnimatorStateInfo(0);
|
||
if (!stateInfo.IsName("close") && !stateInfo.IsName("open"))
|
||
{
|
||
allComplete = false;
|
||
}
|
||
}
|
||
|
||
if (allComplete)
|
||
{
|
||
isAnimationsComplete = true;
|
||
// 动画完成后,确保所有页面都处于正确的状态
|
||
EnsureFinalAnimationStates();
|
||
}
|
||
}
|
||
|
||
// 确保动画最终状态正确
|
||
private void EnsureFinalAnimationStates()
|
||
{
|
||
// 当前页面应该是open状态
|
||
if (pageAnimator[currentPageIndex] != null)
|
||
{
|
||
pageAnimator[currentPageIndex].Play("open", -1, 1);
|
||
pageAnimator[currentPageIndex].speed = 0;
|
||
}
|
||
|
||
// 其他所有页面应该是close状态
|
||
for (int i = 0; i < pageAnimator.Length; i++)
|
||
{
|
||
if (i == currentPageIndex || pageAnimator[i] == null) continue;
|
||
pageAnimator[i].Play("close", -1, 1);
|
||
pageAnimator[i].speed = 0;
|
||
}
|
||
}
|
||
|
||
private void SmoothToTarget()
|
||
{
|
||
if (pages[0] == null) return;
|
||
|
||
// 计算当前第一个页面的目标位置
|
||
float targetX = -currentPageIndex * pageSpacing;
|
||
float currentX = pages[0].localPosition.x;
|
||
|
||
// 平滑移动
|
||
float t = Time.deltaTime * snapSpeed;
|
||
float newX = Mathf.Lerp(currentX, targetX, t);
|
||
|
||
// 应用新位置
|
||
Vector3 newPos = pages[0].localPosition;
|
||
newPos.x = newX;
|
||
pages[0].localPosition = newPos;
|
||
|
||
// 同步其他页面
|
||
for (int i = 1; i < pages.Length; i++)
|
||
{
|
||
if (pages[i] == null) continue;
|
||
Vector3 pos = newPos + new Vector3(i * pageSpacing, 0, 0);
|
||
pages[i].localPosition = pos;
|
||
}
|
||
|
||
// 检查是否到达目标
|
||
if (Mathf.Abs(newX - targetX) < 0.5f)
|
||
{
|
||
// 直接设置精确位置
|
||
newPos.x = targetX;
|
||
pages[0].localPosition = newPos;
|
||
|
||
for (int i = 1; i < pages.Length; i++)
|
||
{
|
||
if (pages[i] == null) continue;
|
||
Vector3 pos = newPos + new Vector3(i * pageSpacing, 0, 0);
|
||
pages[i].localPosition = pos;
|
||
}
|
||
|
||
currentState = SlideState.Idle;
|
||
|
||
// 位置到达后,确保动画状态正确
|
||
EnsureFinalAnimationStates();
|
||
}
|
||
}
|
||
|
||
private void UpdateCurrentPageIndex()
|
||
{
|
||
if (pages[0] == null) return;
|
||
|
||
float currentX = pages[0].localPosition.x;
|
||
float normalizedOffset = -currentX / pageSpacing + 0.5f;
|
||
int newIndex = Mathf.FloorToInt(normalizedOffset);
|
||
newIndex = Mathf.Clamp(newIndex, 0, pages.Length - 1);
|
||
|
||
if (newIndex != currentPageIndex)
|
||
{
|
||
currentPageIndex = newIndex;
|
||
}
|
||
}
|
||
|
||
public void GoToPage(int targetIndex, bool directJump = false)
|
||
{
|
||
if (targetIndex < 0 || targetIndex >= pages.Length) return;
|
||
|
||
if (directJump)
|
||
{
|
||
// 使用直接跳转
|
||
JumpToPage(targetIndex);
|
||
}
|
||
else
|
||
{
|
||
// 使用原来的跳转逻辑
|
||
currentPageIndex = targetIndex;
|
||
|
||
if (currentState != SlideState.Dragging)
|
||
{
|
||
// 立即设置位置
|
||
float targetX = -currentPageIndex * pageSpacing;
|
||
|
||
for (int i = 0; i < pages.Length; i++)
|
||
{
|
||
if (pages[i] == null) continue;
|
||
float xPos = targetX + i * pageSpacing;
|
||
pages[i].localPosition = new Vector3(xPos, 0, 0);
|
||
}
|
||
|
||
currentState = SlideState.Idle;
|
||
|
||
// 页面切换后,确保动画状态正确
|
||
EnsureFinalAnimationStates();
|
||
}
|
||
else
|
||
{
|
||
// 如果在拖动中,设置吸附状态
|
||
currentState = SlideState.Snapping;
|
||
}
|
||
}
|
||
}
|
||
|
||
public void NextPage()
|
||
{
|
||
int nextIndex = Mathf.Min(currentPageIndex + 1, pages.Length - 1);
|
||
if (nextIndex != currentPageIndex)
|
||
{
|
||
GoToPage(nextIndex, false);
|
||
}
|
||
}
|
||
|
||
public void PreviousPage()
|
||
{
|
||
int prevIndex = Mathf.Max(currentPageIndex - 1, 0);
|
||
if (prevIndex != currentPageIndex)
|
||
{
|
||
GoToPage(prevIndex, false);
|
||
}
|
||
}
|
||
|
||
public void JumpToPage(int targetIndex)
|
||
{
|
||
if (targetIndex < 0 || targetIndex >= pages.Length) return;
|
||
|
||
// 如果已经在目标页面,直接返回
|
||
if (targetIndex == currentPageIndex) return;
|
||
|
||
// 如果正在拖动中,先停止拖动
|
||
if (currentState == SlideState.Dragging)
|
||
{
|
||
currentState = SlideState.Idle;
|
||
isDragging = false;
|
||
dragValid = false;
|
||
}
|
||
|
||
// 设置跳转参数
|
||
jumpTargetIndex = targetIndex;
|
||
currentJumpMode = JumpMode.Direct;
|
||
jumpProgress = 0f;
|
||
|
||
// 关键修复:记录起始位置和目标位置
|
||
jumpStartX = pages[0].localPosition.x;
|
||
jumpTargetX = -targetIndex * pageSpacing;
|
||
|
||
// 记录跳转前的页面索引
|
||
int previousPageIndex = currentPageIndex;
|
||
|
||
// 直接更新当前页面索引
|
||
currentPageIndex = targetIndex;
|
||
|
||
// 停止所有当前动画
|
||
StopAllAnimations();
|
||
|
||
// 处理页面切换动画
|
||
HandleDirectJumpAnimation(previousPageIndex, targetIndex);
|
||
|
||
}
|
||
|
||
// 添加停止所有动画的方法
|
||
private void StopAllAnimations()
|
||
{
|
||
for (int i = 0; i < pageAnimator.Length; i++)
|
||
{
|
||
if (pageAnimator[i] != null)
|
||
{
|
||
pageAnimator[i].speed = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理直接跳转的动画
|
||
private void HandleDirectJumpAnimation(int fromIndex, int toIndex)
|
||
{
|
||
// 关闭所有页面的动画
|
||
for (int i = 0; i < pageAnimator.Length; i++)
|
||
{
|
||
if (pageAnimator[i] == null) continue;
|
||
|
||
if (i == toIndex)
|
||
{
|
||
// 目标页面直接设置为open状态
|
||
pageAnimator[i].Play("open", -1, 1);
|
||
}
|
||
else if (i == fromIndex)
|
||
{
|
||
// 源页面直接设置为close状态
|
||
pageAnimator[i].Play("close", -1, 1);
|
||
}
|
||
else
|
||
{
|
||
// 其他页面保持原有状态(应该是close状态)
|
||
// 这里我们不改变它们的状态,只是确保它们不播放动画
|
||
if (pageAnimator[i].GetCurrentAnimatorStateInfo(0).IsName("open"))
|
||
{
|
||
pageAnimator[i].Play("close", -1, 1);
|
||
}
|
||
}
|
||
pageAnimator[i].speed = 0;
|
||
}
|
||
|
||
// 如果有标题动画,也进行处理
|
||
if (pageTitle != null && pageTitle.Length > 0)
|
||
{
|
||
// 这里可以添加标题动画的处理逻辑
|
||
// 例如:高亮目标页面的标题
|
||
}
|
||
}
|
||
|
||
enum EnumDistance
|
||
{
|
||
None,
|
||
Left,
|
||
Right
|
||
}
|
||
} |