临时提交

This commit is contained in:
陈思海 2026-04-29 20:25:58 +08:00
parent 255892dd5f
commit db11a4aa1a
9 changed files with 261 additions and 36 deletions

View File

@ -105,6 +105,26 @@ namespace AlicizaX.UI
return -index;
}
public override float GetItemStartPosition(int index)
{
return IndexToPosition(index);
}
public override float GetItemLength(int index)
{
return Mathf.Abs(intervalAngle);
}
public override int GetSnapIndex(float position)
{
if (adapter == null || adapter.GetItemCount() <= 0)
{
return 0;
}
return Mathf.Clamp(PositionToIndex(position), 0, adapter.GetItemCount() - 1);
}
public override void DoItemAnimation()
{
int visibleCount = viewProvider.VisibleCount;

View File

@ -174,5 +174,50 @@ namespace AlicizaX.UI
return index * unit;
}
public override float GetItemStartPosition(int index)
{
if (adapter == null || adapter.GetItemCount() <= 0)
{
return 0f;
}
index = Mathf.Clamp(index, 0, adapter.GetItemCount() - 1);
int row = index / unit;
float len = direction == Direction.Vertical ? cellSize.y + spacing.y : cellSize.x + spacing.x;
return row * len;
}
public override float GetItemLength(int index)
{
return direction == Direction.Vertical ? Mathf.Max(cellSize.y, 0f) : Mathf.Max(cellSize.x, 0f);
}
public override int GetSnapIndex(float position)
{
if (adapter == null || adapter.GetItemCount() <= 0)
{
return 0;
}
float len = direction == Direction.Vertical ? cellSize.y + spacing.y : cellSize.x + spacing.x;
float itemLength = GetItemLength(0);
if (len <= 0f || itemLength <= 0f)
{
return 0;
}
int itemCount = adapter.GetItemCount();
int row = Mathf.FloorToInt(Mathf.Max(position, 0f) / len);
int index = Mathf.Clamp(row * unit, 0, itemCount - 1);
float visibleLength = itemLength - (position - GetItemStartPosition(index));
if (visibleLength < itemLength * 0.5f && index + unit < itemCount)
{
index += unit;
}
return index;
}
}
}

View File

@ -26,6 +26,12 @@ namespace AlicizaX.UI
int PositionToIndex(float position);
float GetItemStartPosition(int index);
float GetItemLength(int index);
int GetSnapIndex(float position);
void DoItemAnimation();
bool IsFullVisibleStart(int index);

View File

@ -142,6 +142,20 @@ namespace AlicizaX.UI
public abstract int PositionToIndex(float position);
public abstract float GetItemStartPosition(int index);
public abstract float GetItemLength(int index);
public virtual int GetSnapIndex(float position)
{
if (adapter == null || adapter.GetItemCount() <= 0)
{
return 0;
}
return Mathf.Clamp(PositionToIndex(position), 0, adapter.GetItemCount() - 1);
}
public virtual void DoItemAnimation() { }
public virtual bool IsFullVisibleStart(int index)

View File

@ -143,5 +143,49 @@ namespace AlicizaX.UI
return index;
}
public override float GetItemStartPosition(int index)
{
if (adapter == null || adapter.GetItemCount() <= 0)
{
return 0f;
}
index = Mathf.Clamp(index, 0, adapter.GetItemCount() - 1);
float len = direction == Direction.Vertical ? lineHeight + spacing.y : lineHeight + spacing.x;
return index * len;
}
public override float GetItemLength(int index)
{
return Mathf.Max(lineHeight, 0f);
}
public override int GetSnapIndex(float position)
{
if (adapter == null || adapter.GetItemCount() <= 0)
{
return 0;
}
float len = direction == Direction.Vertical ? lineHeight + spacing.y : lineHeight + spacing.x;
float itemLength = GetItemLength(0);
if (len <= 0f || itemLength <= 0f)
{
return 0;
}
int itemCount = adapter.GetItemCount();
int index = Mathf.FloorToInt(Mathf.Max(position, 0f) / len);
index = Mathf.Clamp(index, 0, itemCount - 1);
float visibleLength = itemLength - (position - GetItemStartPosition(index));
if (visibleLength < itemLength * 0.5f && index < itemCount - 1)
{
index++;
}
return index;
}
}
}

View File

@ -145,6 +145,62 @@ namespace AlicizaX.UI
return index >= 0 ? index : cachedItemCount - 1;
}
public override float GetItemStartPosition(int index)
{
EnsurePositionCache();
if (cachedItemCount <= 0)
{
return 0f;
}
index = Mathf.Clamp(index, 0, cachedItemCount - 1);
return itemPositions[index];
}
public override float GetItemLength(int index)
{
EnsurePositionCache();
if (cachedItemCount <= 0)
{
return 0f;
}
index = Mathf.Clamp(index, 0, cachedItemCount - 1);
float spacingLength = index < cachedItemCount - 1
? (direction == Direction.Vertical ? spacing.y : spacing.x)
: 0f;
return Mathf.Max(itemLengths[index] - spacingLength, 0f);
}
public override int GetSnapIndex(float position)
{
EnsurePositionCache();
if (cachedItemCount <= 0)
{
return 0;
}
int index = FindFirstItemEndingAfter(Mathf.Max(position, 0f));
if (index < 0)
{
return cachedItemCount - 1;
}
float itemLength = GetItemLength(index);
if (itemLength <= 0f)
{
return index;
}
float visibleLength = itemLength - (position - GetItemStartPosition(index));
if (visibleLength < itemLength * 0.5f && index < cachedItemCount - 1)
{
index++;
}
return index;
}
private void EnsurePositionCache()
{
int itemCount = adapter != null ? adapter.GetItemCount() : 0;

View File

@ -1203,12 +1203,11 @@ namespace AlicizaX.UI
return scroller.Position;
}
float itemSize = GetItemSize(index);
float itemSize = layoutManager.GetItemLength(index);
float viewportLength = direction == Direction.Vertical ? layoutManager.ViewportSize.y : layoutManager.ViewportSize.x;
float contentLength = direction == Direction.Vertical ? layoutManager.ContentSize.y : layoutManager.ContentSize.x;
// 计算目标项的原始位置不在此阶段做范围限制<E99990>?
float itemPosition = CalculateRawItemPosition(index);
float itemPosition = layoutManager.GetItemStartPosition(index);
float targetPosition = alignment switch
{
@ -1276,13 +1275,7 @@ namespace AlicizaX.UI
/// </summary>
private void OnMoveStoped()
{
if (snap && SnapToNearestItem())
{
return;
}
TryProcessPendingFocusRequest();
OnScrollStopped?.Invoke();
HandleScrollSettled();
}
/// <summary>
@ -1313,10 +1306,7 @@ namespace AlicizaX.UI
{
if (scroller == null) return;
if (scroller.Position < scroller.MaxPosition && snap)
{
SnapToNearestItem();
}
HandleScrollSettled();
}
#endregion
@ -1603,17 +1593,36 @@ namespace AlicizaX.UI
/// <returns>触发了新的吸附滚动时返回 <see langword="true"/>否则返<E58899>?<see langword="false"/><3E>?/returns>
private bool SnapToNearestItem()
{
int index = layoutManager.PositionToIndex(GetScrollPosition());
float targetPosition = layoutManager.IndexToPosition(index);
if (Mathf.Abs(targetPosition - GetScrollPosition()) <= 0.1f)
if (layoutManager == null || RecyclerViewAdapter == null || RecyclerViewAdapter.GetItemCount() <= 0)
{
return false;
}
ScrollTo(index, true);
float position = GetScrollPosition();
int index = layoutManager.GetSnapIndex(position);
index = Mathf.Clamp(index, 0, RecyclerViewAdapter.GetItemCount() - 1);
float targetPosition = layoutManager.IndexToPosition(index);
UpdateCurrentIndex(index);
if (Mathf.Abs(targetPosition - position) <= 0.1f)
{
return false;
}
scroller.ScrollToDuration(targetPosition, 0.12f);
return true;
}
private void HandleScrollSettled()
{
if (snap && SnapToNearestItem())
{
return;
}
TryProcessPendingFocusRequest();
OnScrollStopped?.Invoke();
}
/// <summary>
/// 更新当前逻辑索引并在变化时触发事件通知<E9809A>?
/// </summary>

View File

@ -55,8 +55,9 @@ namespace AlicizaX.UI
return delta * 0.1f;
}
protected override void Elastic()
protected override bool StartElasticMotion()
{
return false;
}
}
}

View File

@ -5,6 +5,12 @@ namespace AlicizaX.UI
{
public class Scroller : MonoBehaviour, IScroller, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler
{
private const float MaxInertiaVelocity = 60f;
private const float MinInertiaDuration = 0.06f;
private const float MaxInertiaDuration = 0.24f;
private const float MinInertiaDistanceFactor = 1.5f;
private const float MaxInertiaDistanceFactor = 6f;
protected enum MotionState
{
Idle,
@ -83,6 +89,8 @@ namespace AlicizaX.UI
private float motionDuration;
private float motionSpeed;
private float inertiaVelocity;
private float inertiaDistance;
private bool notifyMoveStoppedOnComplete;
public float MaxPosition => direction == Direction.Vertical ? Mathf.Max(contentSize.y - viewSize.y, 0) : Mathf.Max(contentSize.x - viewSize.x, 0);
@ -154,7 +162,7 @@ namespace AlicizaX.UI
return;
}
StartPositionMotion(position, scrollSpeed);
StartPositionMotion(position, scrollSpeed, true);
}
public virtual void ScrollToDuration(float position, float duration)
@ -177,6 +185,7 @@ namespace AlicizaX.UI
motionTargetPosition = position;
motionDuration = Mathf.Max(duration, 0.0001f);
motionElapsed = 0f;
notifyMoveStoppedOnComplete = true;
}
public virtual void ScrollToRatio(float ratio)
@ -202,8 +211,7 @@ namespace AlicizaX.UI
return;
}
Inertia();
Elastic();
StartReleaseMotion();
OnDragging?.Invoke(false);
}
@ -234,8 +242,7 @@ namespace AlicizaX.UI
position += velocity;
OnValueChanged?.Invoke(position);
Inertia();
Elastic();
StartReleaseMotion();
}
internal virtual float GetDelta(PointerEventData eventData)
@ -277,36 +284,57 @@ namespace AlicizaX.UI
motionState = MotionState.Inertia;
motionStartPosition = position;
motionElapsed = 0f;
motionDuration = snap ? 0.1f : 1f;
inertiaVelocity = velocity > 0 ? Mathf.Min(velocity, 100) : Mathf.Max(velocity, -100);
inertiaVelocity = Mathf.Clamp(velocity, -MaxInertiaVelocity, MaxInertiaVelocity);
float normalizedVelocity = Mathf.Clamp01(Mathf.Abs(inertiaVelocity) / MaxInertiaVelocity);
motionDuration = Mathf.Lerp(MinInertiaDuration, MaxInertiaDuration, normalizedVelocity);
float distanceFactor = Mathf.Lerp(MinInertiaDistanceFactor, MaxInertiaDistanceFactor, normalizedVelocity);
inertiaDistance = inertiaVelocity * distanceFactor;
notifyMoveStoppedOnComplete = true;
}
protected virtual void Elastic()
protected virtual bool StartElasticMotion()
{
if (position < 0)
{
StopMovement();
StartPositionMotion(0, 7f);
StartPositionMotion(0, 7f, true);
return true;
}
else if (position > MaxPosition)
if (position > MaxPosition)
{
StopMovement();
StartPositionMotion(MaxPosition, 7f);
StartPositionMotion(MaxPosition, 7f, true);
return true;
}
return false;
}
protected void StopMovement()
{
motionState = MotionState.Idle;
notifyMoveStoppedOnComplete = false;
}
private void StartPositionMotion(float targetPosition, float speed)
private void StartReleaseMotion()
{
if (StartElasticMotion())
{
return;
}
Inertia();
}
private void StartPositionMotion(float targetPosition, float speed, bool notifyStopped)
{
motionState = MotionState.Smooth;
motionStartPosition = position;
motionTargetPosition = targetPosition;
motionElapsed = Time.deltaTime;
motionSpeed = speed;
notifyMoveStoppedOnComplete = notifyStopped;
}
private void TickSmooth(float deltaTime)
@ -343,8 +371,8 @@ namespace AlicizaX.UI
{
motionElapsed += deltaTime;
float t = Mathf.Clamp01(motionElapsed / motionDuration);
float y = (float)EaseUtil.EaseOutCirc(t) * 40f;
float nextPosition = motionStartPosition + y * inertiaVelocity;
float offset = (float)EaseUtil.EaseOutCirc(t) * inertiaDistance;
float nextPosition = motionStartPosition + offset;
float maxPosition = MaxPosition;
if (nextPosition < 0f)
@ -352,7 +380,7 @@ namespace AlicizaX.UI
position = 0f;
OnValueChanged?.Invoke(position);
StopMovement();
StartPositionMotion(0f, 7f);
StartPositionMotion(0f, 7f, true);
return;
}
@ -361,7 +389,7 @@ namespace AlicizaX.UI
position = maxPosition;
OnValueChanged?.Invoke(position);
StopMovement();
StartPositionMotion(maxPosition, 7f);
StartPositionMotion(maxPosition, 7f, true);
return;
}
@ -378,8 +406,10 @@ namespace AlicizaX.UI
{
motionState = MotionState.Idle;
velocity = 0f;
bool shouldNotify = notifyStopped || notifyMoveStoppedOnComplete;
notifyMoveStoppedOnComplete = false;
if (notifyStopped)
if (shouldNotify)
{
OnMoveStoped?.Invoke();
}