//-------------------------------------------------- // 公有方法 //-------------------------------------------------- // 直接设置指定索引的页面到视口 public void SetTo(int index) { // Disabled 状态下留待 enable 后执行 if (!isActiveAndEnabled) { newIndex = index; return; } SetTargetIndex(index); if (isDragging) { return; } // 当前位置 layout 和 viewport 左上角之间的距离(有方向) Vector2 cornerDist = UILayoutUtility.GetLayoutLT2ViewportLT(layoutRectTransform, viewport); if (direction == Direction.Horizontal && !Mathf.Approximately(cornerDist.x, targetCornerDistance)) { SetLayoutAnchoredPosition(layoutRectTransform.anchoredPosition + new Vector2(cornerDist.x - targetCornerDistance, 0)); } else if (direction == Direction.Vertical && !Mathf.Approximately(cornerDist.y, targetCornerDistance)) { SetLayoutAnchoredPosition(layoutRectTransform.anchoredPosition + new Vector2(0, cornerDist.y - targetCornerDistance)); } }
/// <summary> /// 获取页面中心到 viewport 中心在滚动方向上的距离(带符号)。 /// </summary> private float GetPageCenterToViewportCenter(int pageIndex) { Vector2 delta = UILayoutUtility.GetItemCenterToViewportCenter(layoutRectTransform, viewport, scrolltoableLayout, pageIndex); return(direction == Direction.Horizontal ? delta.x : delta.y); }
/// <summary> /// 将页面惯性滚动到目标位置。 /// </summary> private void ScrollToTargetPosition(float deltaTime) { // 当前位置 layout 和 viewport 左上角之间的距离(有方向) Vector2 cornerDist = UILayoutUtility.GetLayoutLT2ViewportLT(layoutRectTransform, viewport); // 进行横向滚动 if (direction == Direction.Horizontal && !Mathf.Approximately(cornerDist.x, targetCornerDistance)) { float speed = Mathf.Max(minScrollSpeed, Mathf.Abs(smoothVelocity.x)); // 滚动速度不低于下限 float delta = cornerDist.x - targetCornerDistance; float step = Mathf.Abs(speed) * deltaTime * Mathf.Sign(delta); if ((delta < 0 && step < delta) || (delta > 0 && step > delta)) { step = delta; } SetLayoutAnchoredPosition(layoutRectTransform.anchoredPosition + new Vector2(step, 0)); } // 进行纵向滚动 else if (direction == Direction.Vertical && !Mathf.Approximately(cornerDist.y, targetCornerDistance)) { float speed = Mathf.Max(minScrollSpeed, Mathf.Abs(smoothVelocity.y)); float delta = cornerDist.y - targetCornerDistance; float step = Mathf.Abs(speed) * deltaTime * Mathf.Sign(delta); if ((delta < 0 && step < delta) || (delta > 0 && step > delta)) { step = delta; } SetLayoutAnchoredPosition(layoutRectTransform.anchoredPosition + new Vector2(0, step)); } }
private void UpdatePagesVisibilities() { // 视口宽度 float viewportWidth = viewport.rect.width; // 视口左边缘相对 Layout 左上角的 x 坐标(通常正值) float layoutViewLeft = UILayoutUtility.GetLayoutLT2ViewportLT(rectTransform, viewport).x; // 视口左侧最近的不可见 page // pageWidth + (pageWidth + pageSpace) * minIndex <= layoutViewLeft; // 0.0001f 的作用: // - 消除浮点误差的影响,如 2.0f 的实际结果可能是 2.0000001f // - 解决 CeilToInt(2.0) == 2.0 的问题,保证 2.0 不被显示 // 视口内最左列 int minShownIndex = Mathf.CeilToInt((layoutViewLeft - pageSize.x) / (pageSize.x + pageSpace) + 0.0001f); minShownIndex = Mathf.Max(minShownIndex, 0); // 视口右侧最近的不可见 page // (pageWidth + pageSpace) * minIndex => layoutViewLeft + viewportWidth; // 视口内最右列 int maxShownIndex = Mathf.FloorToInt((layoutViewLeft + viewportWidth) / (pageSize.x + pageSpace) - 0.0001f); maxShownIndex = Mathf.Min(maxShownIndex, pages.Count - 1); if (currentMinShownIndex == -1) { for (int i = minShownIndex; i <= maxShownIndex; i++) { pages[i].ShowItem(); } } else if (currentMinShownIndex < minShownIndex || currentMaxShownIndex < maxShownIndex) { for (int i = currentMinShownIndex; i <= Mathf.Min(minShownIndex - 1, currentMaxShownIndex); i++) { pages[i].HideItem(); } for (int i = Mathf.Max(minShownIndex, currentMaxShownIndex + 1); i <= maxShownIndex; i++) { pages[i].ShowItem(); } } else if (currentMinShownIndex > minShownIndex || currentMaxShownIndex > maxShownIndex) { for (int i = Mathf.Max(currentMinShownIndex, maxShownIndex + 1); i <= currentMaxShownIndex; i++) { pages[i].HideItem(); } for (int i = minShownIndex; i <= Mathf.Min(maxShownIndex, currentMinShownIndex - 1); i++) { pages[i].ShowItem(); } } currentMinShownIndex = minShownIndex; currentMaxShownIndex = maxShownIndex; }
/// <summary> /// 更新所有 row 的可见性,显示可见 rows 的元素,隐藏(回收)不可见 row 的元素。 /// </summary> private void UpdateRowsVisibilities() { // 视口上边缘相对 Layout 左上角的 y 坐标(通常负值) float layoutViewTop = UILayoutUtility.GetLayoutLT2ViewportLT(rectTransform, viewport).y; float layoutViewBottom = layoutViewTop - viewport.rect.height; // 视口上部最近的不可见 row // -topPadding - rowSize.y - (rowSpace + rowSize.y) * minIndex < layoutViewTop; // 视口内最上排 int minShownIndex = Mathf.CeilToInt(-(layoutViewTop + topPadding + rowSize.y) / (rowSpace + rowSize.y)); minShownIndex = Mathf.Max(minShownIndex, 0); // 视口下部最近的不可见 row // -topPadding - (rowSpace + rowSize.y) * maxIndex > layoutViewBottom; // 视口内最下排 int maxShownIndex = Mathf.FloorToInt(-(layoutViewBottom + topPadding) / (rowSpace + rowSize.y)); maxShownIndex = Mathf.Min(maxShownIndex, rows.Count - 1); if (lastMinShownIndex == -1) { for (int i = minShownIndex; i <= maxShownIndex; i++) { rows[i].ShowItems(); } } else if (lastMinShownIndex < minShownIndex || lastMaxShownIndex < maxShownIndex) { for (int i = lastMinShownIndex; i <= Mathf.Min(minShownIndex - 1, lastMaxShownIndex); i++) { rows[i].HideItems(); } for (int i = Mathf.Max(minShownIndex, lastMaxShownIndex + 1); i <= maxShownIndex; i++) { rows[i].ShowItems(); } } else if (lastMinShownIndex > minShownIndex || lastMaxShownIndex > maxShownIndex) { for (int i = Mathf.Max(lastMinShownIndex, maxShownIndex + 1); i <= lastMaxShownIndex; i++) { rows[i].HideItems(); } for (int i = minShownIndex; i <= Mathf.Min(maxShownIndex, lastMinShownIndex - 1); i++) { rows[i].ShowItems(); } } lastMinShownIndex = minShownIndex; lastMaxShownIndex = maxShownIndex; }
/// <summary> /// 计算如果 layout 被移动到指定 anchoredPosition 时超出 viewport 的向量。 /// 用于约束 layout 在限定的范围内(不要跟随手指超出 viewport 的约束范围)。 /// </summary> /// <param name="anchoredPosition">指定的 layout 锚定位置。</param> /// <returns>偏移量。如果未超出则返回零值。</returns> private Vector2 GetOutOffset(Vector2 anchoredPosition) { // 计入 viewport 和 page 尺寸差异 Vector2 deltaSize = (viewport.rect.size - scrolltoableLayout.GetItemSize(0)) * 0.5f; // 目标位置时 viewport 和 layout 左上角之间的距离 Vector2 layoutLT2ViewLT = UILayoutUtility.GetLayoutLT2ViewportLT(layoutRectTransform, viewport); layoutLT2ViewLT += layoutRectTransform.anchoredPosition - anchoredPosition; if (direction == Direction.Horizontal) { // 左端超出(右滑) if (layoutLT2ViewLT.x + deltaSize.x < 0) { return(new Vector2(layoutLT2ViewLT.x + deltaSize.x, 0)); } // 右端超出(左滑) else if (layoutLT2ViewLT.x + viewport.rect.width - deltaSize.x > layoutRectTransform.rect.width) { return(new Vector2( layoutLT2ViewLT.x + viewport.rect.width - layoutRectTransform.rect.width - deltaSize.x, 0)); } } else { // 上端超出(下滑) if (layoutLT2ViewLT.y - deltaSize.y > 0) { return(new Vector2(0, layoutLT2ViewLT.y - deltaSize.y)); } // 下端超出(上滑) else if (layoutLT2ViewLT.y - viewport.rect.height + deltaSize.y < -layoutRectTransform.rect.height) { return(new Vector2( 0, layoutLT2ViewLT.y - viewport.rect.height + layoutRectTransform.rect.height + deltaSize.y)); } } return(Vector2.zero); }