using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.Events; using UnityEngine.EventSystems; namespace JC { public class ScrollPanel : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler, IPointerUpHandler { //public enum scrollAlignment //{ // Upper, // Middle, // Lower //} public enum InitType { Statics, Dynamic } public InitType initType = InitType.Statics; ScrollRect _scrollRect; RectTransform _rectTransform; ScrollRect scrollRect { get { if (!_scrollRect) { _scrollRect = this.GetComponent(); } return _scrollRect; } } RectTransform rectTransform { get { if (!_rectTransform) { _rectTransform = this.GetComponent(); } return _rectTransform; } } //偏移量 public int offset = 0; //解决【Content节点的AnchorPresets在Inspector中设置成了Center,但运行时的效果却是LeftTop】的问题。 Vector3 contentPosition { get { Vector3 v3 = scrollRect.content.localPosition; v3.x -= rectTransform.sizeDelta.x / 2 + offset; v3.y += rectTransform.sizeDelta.y / 2 ; return v3; } set { value.x += rectTransform.sizeDelta.x / 2 + offset; value.y -= rectTransform.sizeDelta.y / 2; scrollRect.content.localPosition = value; } } public List itemList = new List(); RectTransform minItem { get { RectTransform bestItem = null; foreach (RectTransform item in itemList) { if (!bestItem || item.localPosition.x < bestItem.localPosition.x) { bestItem = item; } } return bestItem; } } RectTransform maxItem { get { RectTransform bestItem = null; foreach (RectTransform item in itemList) { if (!bestItem || item.localPosition.x > bestItem.localPosition.x) { bestItem = item; } } return bestItem; } } public int startIndex = 0; public int spacing = 0; public bool openReceiveItemViewInfo = false; public UnityEvent onReceiveItemViewInfo; [Tooltip("以下节点将作为预制体,不加入ItemList中作为滚动内容,组件初始化时会隐藏预制体。")] int currentItemIndex = -1; void SetCurrentItemIndex(int index) { if (index != currentItemIndex) { currentItemIndex = index; onItemChange.Invoke(itemList[index], index); } } public UnityEvent onItemChange; public List prefabs; void Start() { for (int i = 0; i < scrollRect.content.childCount; i++) { RectTransform item = scrollRect.content.GetChild(i).GetComponent(); if (prefabs.Contains(item.gameObject)) { item.gameObject.SetActive(false); } else { if (initType == InitType.Statics) { AddItem(item); } } } UpdateItems(); SelectItemByStartIndex(); } public void SelectItemByStartIndex() { if (startIndex >= 0 && startIndex < itemList.Count) { SetCurrentItemIndex(startIndex); MoveItemToCenter(itemList[startIndex]); } } bool snapping = false; int snapIndex = 0; void Update() { if (snapping) { if (snapIndex >= 0 && snapIndex < itemList.Count) { RectTransform item = itemList[snapIndex]; // Debug.Log("snapIndex:"+ snapIndex); MoveItemToCenter(item); } else if (snapIndex >= itemList.Count) { snapIndex = itemList.Count - 1; } } } public void UpdateItems() { if (itemList.Count == 0) return; if (!scrollRect.horizontal) return; RectTransform minItemRecord = minItem; RectTransform maxItemRecord = maxItem; float centerX = -contentPosition.x; float deltaForMin = Mathf.Abs(minItemRecord.localPosition.x - centerX); float deltaForMax = Mathf.Abs(maxItemRecord.localPosition.x - centerX); if (deltaForMin < deltaForMax) { RectTransform item = minItemRecord; while (item && IsInMinBound(item)) { //Debug.Log("next==== -1"); item = RenderNextItem(item, -1); } } else if (deltaForMax < deltaForMin) { RectTransform item = maxItemRecord; while (item && IsInMaxBound(item)) { //Debug.Log("next====1"); item = RenderNextItem(item, 1); } } if (openReceiveItemViewInfo) { Vector3 contentPoint = contentPosition; foreach (var item in itemList) { Vector3 positinInView = contentPoint + item.localPosition; Vector2 viewSize = rectTransform.sizeDelta; onReceiveItemViewInfo.Invoke(item, positinInView, viewSize); } } } bool IsInMinBound(RectTransform item) { return item.localPosition.x + contentPosition.x - item.sizeDelta.x / 2 > -rectTransform.sizeDelta.x / 2; } bool IsInMaxBound(RectTransform item) { return item.localPosition.x + contentPosition.x + item.sizeDelta.x / 2 < rectTransform.sizeDelta.x / 2; } bool IsOutBound(RectTransform item) { if (item.localPosition.x + contentPosition.x + item.sizeDelta.x / 2 < -rectTransform.sizeDelta.x / 2) return true; if (item.localPosition.x + contentPosition.x - item.sizeDelta.x / 2 > rectTransform.sizeDelta.x / 2) return true; return false; } RectTransform RenderNextItem(RectTransform item, int relativeIndex) { int index = itemList.IndexOf(item); index += relativeIndex; if (index < 0) { index = itemList.Count - 1; } else if (index >= itemList.Count) { index = 0; } //Debug.Log("index====:"+ index); RectTransform targetItem = itemList[index]; if (IsOutBound(targetItem)) { Vector3 position = targetItem.localPosition; position.x = item.localPosition.x + relativeIndex * (targetItem.sizeDelta.x + spacing); targetItem.localPosition = position; return targetItem; } return null; } public void AddItem(RectTransform item) { if (scrollRect.horizontal) { item.SetParent(scrollRect.content); Vector3 position = item.localPosition; position.x = 0; if (itemList.Count > 0) { RectTransform lastItem = itemList[itemList.Count - 1]; foreach (RectTransform child in itemList) { if (child.localPosition.x > lastItem.localPosition.x) { Vector3 childPosition = child.localPosition; childPosition.x += child.sizeDelta.x + spacing; child.localPosition = childPosition; } } position.x = lastItem.localPosition.x + item.sizeDelta.x + spacing; } position.y = 0; position.z = 0; item.localPosition = position; } itemList.Add(item); } public void RemoveItem(RectTransform item) { if (itemList.IndexOf(item) == currentItemIndex) { currentItemIndex = -1; } itemList.Remove(item); if (scrollRect.horizontal) { foreach (RectTransform child in itemList) { if (child.localPosition.x > item.localPosition.x) { Vector3 childPosition = child.localPosition; childPosition.x -= child.sizeDelta.x + spacing; child.localPosition = childPosition; } } } Destroy(item.gameObject); } public void ClearItems() { foreach (var item in itemList) { Destroy(item.gameObject); } itemList.Clear(); currentItemIndex = -1; } RectTransform FindNearestItemToCenter() { RectTransform bestItem = null; Vector3 position = contentPosition; foreach(RectTransform item in itemList) { if ( !bestItem || Mathf.Abs(item.localPosition.x + position.x - 0) < Mathf.Abs(bestItem.localPosition.x + position.x - 0) ) { bestItem = item; } } return bestItem; } public void MoveNearestItemToCenter() { if (itemList.Count == 0) return; RectTransform item = FindNearestItemToCenter(); snapping = true; snapIndex = itemList.IndexOf(item); SetCurrentItemIndex(snapIndex); } void MoveItemToCenter(RectTransform item) { Vector3 position = contentPosition; position.x -= (item.localPosition.x + contentPosition.x); if (Mathf.Abs(contentPosition.x - position.x) < 1) { snapping = false; } contentPosition = snapping ? Vector3.Lerp(contentPosition, position, Time.deltaTime * 5) : position; UpdateItems(); } public void MoveNextToCenter(int direction) { if (itemList.Count == 0) return; if (direction == 0) { MoveNearestItemToCenter(); return; } RectTransform item = FindNearestItemToCenter(); int index = itemList.IndexOf(item); if (direction < 0) { index--; //int nextIndex = index - Mathf.Abs(direction); //index = nextIndex >= 0 ? nextIndex : itemList.Count + nextIndex; } if (direction > 0) { //int nextIndex = index + Mathf.Abs(direction); // index = nextIndex < itemList.Count ? nextIndex : nextIndex - itemList.Count; index++; } if (index < 0) { index = itemList.Count - 1; } if (index >= itemList.Count) { index = 0; } //item = itemList[index]; snapping = true; snapIndex = index; SetCurrentItemIndex(snapIndex); } public void OnBeginDrag(PointerEventData eventData) { snapping = false; } public void OnDrag(PointerEventData eventData) { UpdateItems(); } public void OnEndDrag(PointerEventData eventData) { MoveNearestItemToCenter(); } public void OnPointerDown(PointerEventData eventData) { snapping = false; } public void OnPointerUp(PointerEventData eventData) { snapping = true; } } }