UI_MagneticInfiniteScroll.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /// Credit Febo Zodiaco
  2. /// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/349/magnticinfinitescroll
  3. ///
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using UnityEngine.EventSystems;
  8. namespace UnityEngine.UI.Extensions
  9. {
  10. [AddComponentMenu("UI/Extensions/UI Magnetic Infinite Scroll")]
  11. [RequireComponent(typeof(ScrollRect))]
  12. public class UI_MagneticInfiniteScroll : UI_InfiniteScroll, IDragHandler, IEndDragHandler, IScrollHandler
  13. {
  14. public event Action<GameObject> OnNewSelect;
  15. [Tooltip("The pointer to the pivot, the visual element for centering objects.")]
  16. [SerializeField]
  17. private RectTransform pivot = null;
  18. [Tooltip("The maximum speed that allows you to activate the magnet to center on the pivot")]
  19. [SerializeField]
  20. private float maxSpeedForMagnetic = 10f;
  21. [SerializeField]
  22. [Tooltip("The index of the object which must be initially centered")]
  23. private int indexStart = 0;
  24. [SerializeField]
  25. [Tooltip("The time to decelerate and aim to the pivot")]
  26. private float timeForDeceleration = 0.05f;
  27. private float _pastPositionMouseSpeed;
  28. private float _initMovementDirection = 0;
  29. private float _pastPosition = 0;
  30. private float _currentSpeed = 0.0f;
  31. private float _stopValue = 0.0f;
  32. private readonly float _waitForContentSet = 0.1f;
  33. private float _currentTime = 0;
  34. private int _nearestIndex = 0;
  35. private bool _useMagnetic = true;
  36. private bool _isStopping = false;
  37. private bool _isMovement = false;
  38. public List<RectTransform> Items { get; }
  39. protected override void Awake()
  40. {
  41. base.Awake();
  42. StartCoroutine(SetInitContent());
  43. }
  44. private void Update()
  45. {
  46. if (_scrollRect == null || !_scrollRect.content || !pivot || !_useMagnetic || !_isMovement || items == null)
  47. {
  48. return;
  49. }
  50. float currentPosition = GetRightAxis(_scrollRect.content.anchoredPosition);
  51. _currentSpeed = Mathf.Abs(currentPosition - _pastPosition);
  52. _pastPosition = currentPosition;
  53. if (Mathf.Abs(_currentSpeed) > maxSpeedForMagnetic)
  54. {
  55. return;
  56. }
  57. if (_isStopping)
  58. {
  59. Vector2 anchoredPosition = _scrollRect.content.anchoredPosition;
  60. _currentTime += Time.deltaTime;
  61. float valueLerp = _currentTime / timeForDeceleration;
  62. float newPosition = Mathf.Lerp(GetRightAxis(anchoredPosition), _stopValue, valueLerp);
  63. _scrollRect.content.anchoredPosition = _isVertical ? new Vector2(anchoredPosition.x, newPosition) :
  64. new Vector2(newPosition, anchoredPosition.y);
  65. if (newPosition == GetRightAxis(anchoredPosition) && _nearestIndex > 0 && _nearestIndex < items.Count)
  66. {
  67. _isStopping = false;
  68. _isMovement = false;
  69. var item = items[_nearestIndex];
  70. if (item != null && OnNewSelect != null)
  71. {
  72. OnNewSelect.Invoke(item.gameObject);
  73. }
  74. }
  75. }
  76. else
  77. {
  78. float distance = Mathf.Infinity * (-_initMovementDirection);
  79. for (int i = 0; i < items.Count; i++)
  80. {
  81. var item = items[i];
  82. if (item == null)
  83. {
  84. continue;
  85. }
  86. var aux = GetRightAxis(item.position) - GetRightAxis(pivot.position);
  87. if ((_initMovementDirection <= 0 && aux < distance && aux > 0) ||
  88. (_initMovementDirection > 0 && aux > distance && aux < 0))
  89. {
  90. distance = aux;
  91. _nearestIndex = i;
  92. }
  93. }
  94. _isStopping = true;
  95. _stopValue = GetAnchoredPositionForPivot(_nearestIndex);
  96. _scrollRect.StopMovement();
  97. }
  98. }
  99. public override void SetNewItems(ref List<Transform> newItems)
  100. {
  101. foreach (var element in newItems)
  102. {
  103. RectTransform rectTransform = element.GetComponent<RectTransform>();
  104. if (rectTransform && pivot)
  105. {
  106. rectTransform.sizeDelta = pivot.sizeDelta;
  107. }
  108. }
  109. base.SetNewItems(ref newItems);
  110. }
  111. public void SetContentInPivot(int index)
  112. {
  113. float newPos = GetAnchoredPositionForPivot(index);
  114. Vector2 anchoredPosition = _scrollRect.content.anchoredPosition;
  115. if (_scrollRect.content)
  116. {
  117. _scrollRect.content.anchoredPosition = _isVertical ? new Vector2(anchoredPosition.x, newPos) :
  118. new Vector2(newPos, anchoredPosition.y);
  119. _pastPosition = GetRightAxis(_scrollRect.content.anchoredPosition);
  120. }
  121. }
  122. private IEnumerator SetInitContent()
  123. {
  124. yield return new WaitForSeconds(_waitForContentSet);
  125. SetContentInPivot(indexStart);
  126. }
  127. private float GetAnchoredPositionForPivot(int index)
  128. {
  129. if (!pivot || items == null || items.Count < 0)
  130. {
  131. return 0f;
  132. }
  133. index = Mathf.Clamp(index, 0, items.Count - 1);
  134. float posItem = GetRightAxis(items[index].anchoredPosition);
  135. float posPivot = GetRightAxis(pivot.anchoredPosition);
  136. return posPivot - posItem;
  137. }
  138. private void FinishPrepareMovement()
  139. {
  140. _isMovement = true;
  141. _useMagnetic = true;
  142. _isStopping = false;
  143. _currentTime = 0;
  144. }
  145. private float GetRightAxis(Vector2 vector)
  146. {
  147. return _isVertical ? vector.y : vector.x;
  148. }
  149. public void OnDrag(PointerEventData eventData)
  150. {
  151. float currentPosition = GetRightAxis(UIExtensionsInputManager.MousePosition);
  152. _initMovementDirection = Mathf.Sign(currentPosition - _pastPositionMouseSpeed);
  153. _pastPositionMouseSpeed = currentPosition;
  154. _useMagnetic = false;
  155. _isStopping = false;
  156. }
  157. public void OnEndDrag(PointerEventData eventData)
  158. {
  159. FinishPrepareMovement();
  160. }
  161. public void OnScroll(PointerEventData eventData)
  162. {
  163. _initMovementDirection = -UIExtensionsInputManager.MouseScrollDelta.y;
  164. FinishPrepareMovement();
  165. }
  166. }
  167. }