218 lines
6.8 KiB
C#
218 lines
6.8 KiB
C#
using UnityEngine;
|
||
|
||
public class CustomScrollView : MonoBehaviour
|
||
{
|
||
public RectTransform rectStopUp;
|
||
public RectTransform rectStopDown;
|
||
public RectTransform rectTitle;
|
||
public RectTransform rectCenter;
|
||
|
||
public RectTransform rectCenterLimitUp;
|
||
public RectTransform rectCenterLimitDown;
|
||
public RectTransform rectCenterSubUp;
|
||
public RectTransform rectCenterSubDown;
|
||
|
||
// 缓冲效果参数
|
||
private float damping = 0.9f; // 阻尼系数,值越小缓冲越快
|
||
private float minVelocity = 0.1f; // 最小速度阈值
|
||
|
||
private Vector3 vecStart;
|
||
private Vector3 vecStartCenter;
|
||
private Vector3 vecStartTitle;
|
||
private Vector3 velocity = Vector3.zero; // 当前速度
|
||
private bool isDragging = false;
|
||
private float lastMouseY;
|
||
private bool shouldStopScrollUp = false; // 是否应该停止向上滑动
|
||
private bool shouldStopScrollDown = false; // 是否应该停止向下滑动
|
||
|
||
void Update()
|
||
{
|
||
if (Input.GetMouseButtonDown(0))
|
||
{
|
||
StartDragging();
|
||
}
|
||
|
||
if (Input.GetMouseButtonUp(0))
|
||
{
|
||
StopDragging();
|
||
}
|
||
|
||
if (Input.GetMouseButton(0))
|
||
{
|
||
HandleDragging();
|
||
}
|
||
|
||
// 应用缓冲效果
|
||
ApplyInertia();
|
||
}
|
||
|
||
private void StartDragging()
|
||
{
|
||
vecStart = Input.mousePosition;
|
||
vecStartCenter = rectCenter.position;
|
||
vecStartTitle = rectTitle.position;
|
||
lastMouseY = Input.mousePosition.y;
|
||
velocity = Vector3.zero;
|
||
isDragging = true;
|
||
shouldStopScrollUp = false;
|
||
shouldStopScrollDown = false;
|
||
}
|
||
|
||
private void StopDragging()
|
||
{
|
||
isDragging = false;
|
||
}
|
||
|
||
private void HandleDragging()
|
||
{
|
||
if (!isDragging) return;
|
||
|
||
Vector3 currentMousePos = Input.mousePosition;
|
||
|
||
// 计算基于鼠标Y值移动量的滑动
|
||
float deltaY = currentMousePos.y - vecStart.y;
|
||
Vector3 newPosition = vecStartCenter + new Vector3(0, deltaY, 0);
|
||
|
||
// 检查边界限制
|
||
CheckScrollLimits(deltaY);
|
||
|
||
// 根据边界限制决定是否更新rectCenter位置
|
||
if (!(deltaY > 0 && shouldStopScrollDown) && !(deltaY < 0 && shouldStopScrollUp))
|
||
{
|
||
// 直接设置rectCenter的位置
|
||
rectCenter.position = newPosition;
|
||
|
||
// 计算rectTitle应该移动到的位置
|
||
Vector3 newTitlePosition = vecStartTitle + new Vector3(0, deltaY, 0);
|
||
|
||
// 限制rectTitle的位置,不能超过上下边界
|
||
float minY = rectStopDown.position.y;
|
||
float maxY = rectStopUp.position.y;
|
||
newTitlePosition.y = Mathf.Clamp(newTitlePosition.y, minY, maxY);
|
||
|
||
// 设置rectTitle的位置
|
||
rectTitle.position = newTitlePosition;
|
||
}
|
||
|
||
// 计算当前速度(用于缓冲效果)
|
||
float currentDeltaY = currentMousePos.y - lastMouseY;
|
||
velocity = new Vector3(0, currentDeltaY / Time.deltaTime, 0);
|
||
lastMouseY = currentMousePos.y;
|
||
}
|
||
|
||
private void ApplyInertia()
|
||
{
|
||
if (!isDragging && velocity.magnitude > minVelocity)
|
||
{
|
||
// 检查边界限制
|
||
CheckScrollLimits(velocity.y * Time.deltaTime);
|
||
|
||
// 检查是否应该停止惯性滑动
|
||
bool shouldStopInertia = (velocity.y > 0 && shouldStopScrollDown) || (velocity.y < 0 && shouldStopScrollUp);
|
||
|
||
if (!shouldStopInertia)
|
||
{
|
||
// 使用指数衰减的缓冲
|
||
float deceleration = Mathf.Pow(damping, Time.deltaTime * 60f);
|
||
velocity *= deceleration;
|
||
|
||
// rectCenter继续移动
|
||
rectCenter.position += velocity * Time.deltaTime;
|
||
|
||
// rectTitle跟随rectCenter移动,但受边界限制
|
||
Vector3 newTitlePosition = rectTitle.position + velocity * Time.deltaTime;
|
||
float minY = rectStopDown.position.y;
|
||
float maxY = rectStopUp.position.y;
|
||
newTitlePosition.y = Mathf.Clamp(newTitlePosition.y, minY, maxY);
|
||
rectTitle.position = newTitlePosition;
|
||
}
|
||
else
|
||
{
|
||
// 如果应该停止,则快速减速
|
||
velocity *= 0.5f;
|
||
}
|
||
|
||
if (velocity.magnitude <= minVelocity)
|
||
{
|
||
velocity = Vector3.zero;
|
||
}
|
||
}
|
||
else if (!isDragging && velocity.magnitude <= minVelocity)
|
||
{
|
||
// 当速度很小时,确保rectTitle仍在边界内
|
||
Vector3 currentTitlePos = rectTitle.position;
|
||
float minY = rectStopDown.position.y;
|
||
float maxY = rectStopUp.position.y;
|
||
currentTitlePos.y = Mathf.Clamp(currentTitlePos.y, minY, maxY);
|
||
rectTitle.position = currentTitlePos;
|
||
}
|
||
}
|
||
|
||
// 检查滑动边界限制
|
||
private void CheckScrollLimits(float deltaY)
|
||
{
|
||
// 重置标志
|
||
shouldStopScrollUp = false;
|
||
shouldStopScrollDown = false;
|
||
|
||
// 检查向下滑动时rectCenterSubUp不能低于rectCenterLimitUp
|
||
if (deltaY < 0) // 向上滑动(rectCenter向下移动)
|
||
{
|
||
if (rectCenterSubUp != null && rectCenterLimitUp != null)
|
||
{
|
||
// 检查rectCenterSubUp的底部是否低于rectCenterLimitUp的顶部
|
||
float subUpBottom = rectCenterSubUp.position.y - rectCenterSubUp.rect.height / 2;
|
||
float limitUpTop = rectCenterLimitUp.position.y + rectCenterLimitUp.rect.height / 2;
|
||
|
||
if (subUpBottom < limitUpTop)
|
||
{
|
||
shouldStopScrollUp = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查向上滑动时rectCenterSubDown不能高于rectCenterLimitDown
|
||
if (deltaY > 0) // 向下滑动(rectCenter向上移动)
|
||
{
|
||
if (rectCenterSubDown != null && rectCenterLimitDown != null)
|
||
{
|
||
// 检查rectCenterSubDown的顶部是否高于rectCenterLimitDown的底部
|
||
float subDownTop = rectCenterSubDown.position.y + rectCenterSubDown.rect.height / 2;
|
||
float limitDownBottom = rectCenterLimitDown.position.y - rectCenterLimitDown.rect.height / 2;
|
||
|
||
if (subDownTop > limitDownBottom)
|
||
{
|
||
shouldStopScrollDown = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 外部可以调用这个方法来添加额外的速度(例如反弹效果)
|
||
public void AddVelocity(Vector3 additionalVelocity)
|
||
{
|
||
velocity += additionalVelocity;
|
||
}
|
||
|
||
// 设置缓冲参数
|
||
public void SetDamping(float newDamping)
|
||
{
|
||
damping = Mathf.Clamp(newDamping, 0.1f, 0.99f);
|
||
}
|
||
|
||
// 获取当前速度
|
||
public Vector3 GetCurrentVelocity()
|
||
{
|
||
return velocity;
|
||
}
|
||
|
||
// 新增:强制更新rectTitle位置到边界内(可以在必要时调用)
|
||
public void ClampTitlePosition()
|
||
{
|
||
Vector3 currentTitlePos = rectTitle.position;
|
||
float minY = rectStopDown.position.y;
|
||
float maxY = rectStopUp.position.y;
|
||
currentTitlePos.y = Mathf.Clamp(currentTitlePos.y, minY, maxY);
|
||
rectTitle.position = currentTitlePos;
|
||
}
|
||
} |