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; } }