循环滚动
using System; using System.Collections.Generic; using UnityEngine.EventSystems; namespace UnityEngine.UI { public class LoopScroll : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { enum DragMode { None, Horizontal, Vertical, } public Action<int> callBack; [SerializeField] private DragMode mode; [SerializeField] public Transform content; [SerializeField, Tooltip("拖拽速度"), Range(1, 100f)] private float speed = 1; [SerializeField, Tooltip("居中速度"), Range(1, 20f)] private float ratio = 1; [SerializeField, Tooltip("滑动力度"), Range(10, 1000)] private float spring = 30; [SerializeField, Tooltip("显示间隔(s)"), Range(1, 60)] private float interval = 3; [SerializeField, Tooltip("自动显示")] private bool auto; [SerializeField] private Vector2 center; private Vector2 space; private Vector2 cell; private Vector2 vector; private float offset; private Vector2 position; private Vector2 point_begin; private Vector2 point_end; private int index_pre; private int index_target; private float spring_value; private int spring_offset; private bool center_state; private float center_ratio; private Vector2 center_vector; private Vector2 center_position; private float auto_timer; private bool m_drag; private readonly List<RectTransform> m_childs = new List<RectTransform>(); private void Awake() { FormatPosition(); } private void Update() { if (!m_drag && center_state) { center_ratio += Time.deltaTime * ratio; center_position = Vector2.Lerp(m_childs[index_target].localPosition, center, center_ratio); center_vector = center_position - (Vector2)m_childs[index_target].localPosition; Drag(center_vector); if (center_ratio > 1) { center_state = false; } } if (auto) { if (m_drag) auto_timer = 0; else auto_timer += Time.deltaTime; if (auto_timer >= interval) { auto_timer = 0; Next(); } } } public void OnBeginDrag(PointerEventData eventData) { if (Lock) return; point_begin = Input.mousePosition; m_drag = true; } public void OnDrag(PointerEventData eventData) { if (m_drag) { Drag(eventData.delta * speed * Time.deltaTime); } } public void OnEndDrag(PointerEventData eventData) { if (Lock) return; point_end = Input.mousePosition; m_drag = false; index_target = -1; for (int i = 0; i < m_childs.Count; i++) { if (Center(m_childs[i].localPosition)) { index_target = i; break; } } if (index_target != -1) { if (index_target == index_pre) { switch (mode) { case DragMode.Horizontal: spring_value = point_end.x - point_begin.x; break; case DragMode.Vertical: spring_value = point_end.y - point_begin.y; break; } if (Math.Abs(spring_value) > spring) { spring_offset = spring_value > 0 ? -1 : 1; index_target += spring_offset; if (index_target < 0) { index_target += m_childs.Count; } else if (index_target >= m_childs.Count) { index_target %= m_childs.Count; } } } index_pre = index_target; callBack?.Invoke(index_target); } center_ratio = 0; center_state = index_target != -1; } public void FormatPosition() { m_childs.Clear(); Content _content = content.GetComponent<Content>(); cell.x = _content.Horizontal; cell.y = _content.Vertical; for (int i = 0; i < content.childCount; i++) { if (content.GetChild(i).gameObject.activeSelf) { m_childs.Add(content.GetChild(i) as RectTransform); } } Vector2 _position = center; for (int i = 0; i < m_childs.Count; i++) { m_childs[i].anchorMin = Vector2.one * 0.5f; m_childs[i].anchorMax = Vector2.one * 0.5f; m_childs[i].pivot = Vector2.one * 0.5f; m_childs[i].anchoredPosition = _position; _content.Adapt(m_childs[i]); switch (mode) { case DragMode.Horizontal: _position.x += cell.x; break; case DragMode.Vertical: _position.y -= cell.y; break; default: _position += cell; break; } } int preview_index = 2; if (m_childs.Count > 0) { switch (mode) { case DragMode.Horizontal: space.x = m_childs[0].localPosition.x - preview_index * cell.x; space.y = m_childs[m_childs.Count - preview_index].localPosition.x; break; case DragMode.Vertical: space.x = m_childs[m_childs.Count - preview_index].localPosition.y; space.y = m_childs[0].localPosition.y + preview_index * cell.y; break; } } else { space = Vector2.zero; } } public void Jump(int index) { if (index < 0 || index >= m_childs.Count) return; index_pre = index_target = index; Drag(Vector2.one * -1); if (index_target != -1) { callBack?.Invoke(index_target); } center_ratio = 0; center_state = index_target != -1; } public bool Lock { get; set; } private void Next() { if (m_childs.Count == 0) return; index_target++; if (index_target >= m_childs.Count) { index_target %= m_childs.Count; } index_pre = index_target; Drag(Vector2.one * -1); if (index_target != -1) { callBack?.Invoke(index_target); } center_ratio = 0; center_state = index_target != -1; } private void Drag(Vector2 delta) { switch (mode) { case DragMode.Horizontal: vector.x = delta.x; vector.y = 0; break; case DragMode.Vertical: vector.x = 0; vector.y = delta.y; break; default: vector = delta; break; } for (int i = 0; i < m_childs.Count; i++) { position = m_childs[i].localPosition; position += vector; switch (mode) { case DragMode.Horizontal: if (position.x > space.y) { offset = position.x - space.y; position.x = space.x + offset; } else if (position.x < space.x) { offset = position.x - space.x; position.x = space.y + offset; } break; case DragMode.Vertical: if (position.y > space.y) { offset = position.y - space.y; position.y = space.x + offset; } else if (position.y < space.x) { offset = position.y - space.x; position.y = space.y + offset; } break; default: break; } m_childs[i].localPosition = position; } } private bool Center(Vector2 position) { bool result = false; switch (mode) { case DragMode.Horizontal: result = Math.Abs(position.x - center.x) < cell.x / 2; break; case DragMode.Vertical: result = Math.Abs(position.y - center.y) < cell.y / 2; break; } return result; } } }
using System.Collections.Generic; namespace UnityEngine.UI { [ExecuteInEditMode] public class Content : MonoBehaviour { private enum Axis { Horiaontal, Vertical, } private enum Stretch { None, Horiaontal, Vertical, Full, } [SerializeField] private Axis axis = Axis.Horiaontal; [SerializeField] private Stretch stretch = Stretch.None; [SerializeField] private RectTransform anchor = null; [SerializeField] private Vector2 size = new Vector2(100, 100); [SerializeField] private Vector2 spacing = Vector2.zero; [SerializeField] private bool adapt = false; private RectTransform m_target; private Vector2 m_position; private readonly List<RectTransform> m_childs = new List<RectTransform>(); private void Awake() { m_target = GetComponent<RectTransform>(); UpdateStretch(); } #if UNITY_EDITOR private void OnValidate() { if (m_target == null) return; UpdateStretch(); m_childs.Clear(); for (int i = 0; i < m_target.childCount; i++) { if (m_target.GetChild(i).gameObject.activeSelf) { m_childs.Add(m_target.GetChild(i) as RectTransform); } } RefreshUI(); } #endif private void RefreshUI() { m_position = Vector2.zero; for (int i = 0; i < m_childs.Count; i++) { m_childs[i].anchoredPosition = m_position; Adapt(m_childs[i]); switch (axis) { case Axis.Horiaontal: m_position.x += Horizontal; break; case Axis.Vertical: m_position.y -= Vertical; break; } } } public void UpdateStretch() { switch (stretch) { case Stretch.Horiaontal: size.x = anchor != null ? anchor.rect.width : m_target.rect.width; break; case Stretch.Vertical: size.y = anchor != null ? anchor.rect.height : m_target.rect.height; break; case Stretch.Full: size.x = anchor != null ? anchor.rect.width : m_target.rect.width; size.y = anchor != null ? anchor.rect.height : m_target.rect.height; break; } } public void Adapt(RectTransform rect) { if (adapt) { rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, Horizontal); rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, Vertical); } } public float Horizontal { get { return size.x + spacing.x; } } public float Vertical { get { return size.y + spacing.y; } } } }
注册:scroll.callBack = (index)=>{...};
使用:scroll.Jump(index);
需要展示的子节点物体需>=3,结构类似Scroll Rect,不需要加Fitter