| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- using UnityEngine;
- using UnityEngine.UI;
- using DG.Tweening;
- using System.Collections.Generic;
- using System.Collections;
- public class RankUpLooper : MonoBehaviour
- {
- public ScrollRect scrollRect;
- public RectTransform content;
- public RectTransform viewport;
- public GameObject itemPrefab;
- public int itemCount = 20;
- public int targetRankIndex = 2;
- public float scrollSpeed = 2000f;
- public float scrollDuration = 1.5f;
- public float stopDelay = 0.5f;
- private RectTransform highlightItem;
- private float itemHeight;
- private bool isAnimating = false;
- private float contentResetThreshold;
- private List<GameObject> allItems = new List<GameObject>();
- public void OnClickRankUp()
- {
- PlayRankUp(currentRankIndex: 10, targetRankIndex: 2);// 从第10名冲到第2名(模拟)
- }
- void Start()
- {
- InitRankList();
- }
- void InitRankList()
- {
- allItems.Clear();
- foreach (Transform child in content) Destroy(child.gameObject);
- // 创建原始项
- for (int i = 0; i < itemCount; i++)
- {
- var item = Instantiate(itemPrefab, content);
- item.GetComponentInChildren<Text>().text = $"#{i + 1}";
- allItems.Add(item);
- }
- // 复制一轮,实现循环
- for (int i = 0; i < itemCount; i++)
- {
- var clone = Instantiate(itemPrefab, content);
- clone.GetComponentInChildren<Text>().text = $"#{i + 1} (copy)";
- allItems.Add(clone);
- }
- LayoutRebuilder.ForceRebuildLayoutImmediate(content);
- // 获取 item 高度
- itemHeight = allItems[0].GetComponent<RectTransform>().rect.height;
- contentResetThreshold = itemHeight * itemCount;
- }
- public void PlayRankUp(int currentRankIndex, int targetRankIndex)
- {
- if (isAnimating) return;
- this.targetRankIndex = targetRankIndex;
- var original = allItems[currentRankIndex - 1];
- highlightItem = Instantiate(original, viewport).GetComponent<RectTransform>();
- highlightItem.name = "HighlightItem";
- highlightItem.SetAsLastSibling();
- if (!highlightItem.TryGetComponent(out CanvasGroup group))
- group = highlightItem.gameObject.AddComponent<CanvasGroup>();
- group.alpha = 1f;
- highlightItem.localScale = Vector3.one * 1.2f;
- isAnimating = true;
- scrollRect.enabled = false;
- // 🔄 强制刷新布局
- LayoutRebuilder.ForceRebuildLayoutImmediate(content);
- itemHeight = allItems[0].GetComponent<RectTransform>().rect.height;
- contentResetThreshold = itemHeight * itemCount;
- // 🎯 提前计算目标位置
- Vector3 targetPosition = CalculateTargetLocalPosition(targetRankIndex);
- // 启动滚动协程(传入目标位置)
- StartCoroutine(ScrollAndStop(targetPosition));
- }
- // IEnumerator DelayedScroll()
- // {
- // // 🔄 强制更新布局
- // LayoutRebuilder.ForceRebuildLayoutImmediate(content);
- // // ⏳ 等待一帧,确保布局完成
- // yield return new WaitForEndOfFrame();
- // // 再次确认布局高度等
- // itemHeight = allItems[0].GetComponent<RectTransform>().rect.height;
- // contentResetThreshold = itemHeight * itemCount;
- // // 🔥 启动滚动
- // StartCoroutine(ScrollAndStop());
- // }
- IEnumerator ScrollAndStop(Vector3 targetPos)
- {
- float elapsed = 0f;
- while (elapsed < scrollDuration)
- {
- content.anchoredPosition += Vector2.up * scrollSpeed * Time.deltaTime;
- // 无限滚动逻辑
- if (content.anchoredPosition.y >= contentResetThreshold)
- {
- content.anchoredPosition -= Vector2.up * contentResetThreshold;
- }
- elapsed += Time.deltaTime;
- yield return null;
- }
- yield return new WaitForSeconds(stopDelay);
- SnapToTarget(targetPos);
- }
- void SnapToTarget(Vector3 targetPos)
- {
- content.DOAnchorPos(targetPos, 0.6f).SetEase(Ease.OutCubic);
- highlightItem.DOScale(Vector3.one, 0.3f).SetEase(Ease.OutBack);
- highlightItem.DOAnchorPosY(0, 0.4f).SetEase(Ease.OutSine).OnComplete(() =>
- {
- isAnimating = false;
- });
- }
- private Vector3 CalculateTargetLocalPosition(int targetRank)
- {
- float spacing = 0f;
- float totalItemHeight = itemHeight + spacing;
- float contentY = (targetRank - 1) * totalItemHeight;
- float centeredOffset = contentY - (viewport.rect.height - totalItemHeight) / 2f;
- // 限制 offset 范围
- centeredOffset = Mathf.Clamp(centeredOffset, 0, content.rect.height - viewport.rect.height);
- return new Vector3(0f, centeredOffset, 0f);
- }
- void UpdateHighlightPosition()
- {
- RectTransform targetItem = allItems[targetRankIndex - 1].GetComponent<RectTransform>();
- Vector3 worldPos = targetItem.position;
- Vector2 localPoint;
- RectTransformUtility.ScreenPointToLocalPointInRectangle(viewport, worldPos, null, out localPoint);
- highlightItem.anchoredPosition = localPoint;
- }
- // void SnapToTarget()
- // {
- // // 停止后直接对 content 归位
- // float targetY = itemHeight * (targetRankIndex - 1);
- // float totalContentHeight = content.rect.height;
- // float viewHeight = viewport.rect.height;
- // float offset = Mathf.Clamp(targetY - (viewHeight - itemHeight) / 2f, 0, totalContentHeight - viewHeight);
- // content.anchoredPosition = new Vector2(0, offset);
- // // 获取目标 item 的本地位置(在 content 中的位置)
- // RectTransform targetItem = allItems[targetRankIndex - 1].GetComponent<RectTransform>();
- // // 计算目标位置相对 viewport 的 localPosition
- // Vector3 worldPos = targetItem.position;
- // Vector2 localPoint;
- // RectTransformUtility.ScreenPointToLocalPointInRectangle(viewport, worldPos, null, out localPoint);
- // // Animate highlightItem 到目标位置
- // highlightItem.DOAnchorPos(localPoint, 0.4f).SetEase(Ease.OutSine);
- // highlightItem.DOScale(Vector3.one, 0.3f).SetEase(Ease.OutBack).OnComplete(() =>
- // {
- // isAnimating = false;
- // });
- // }
- }
|