2
0

VerticalScrollSnap.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /// Credit BinaryX, SimonDarksideJ
  2. /// Sourced from - http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-2#post-1945602
  3. /// Updated by SimonDarksideJ - removed dependency on a custom ScrollRect script. Now implements drag interfaces and standard Scroll Rect.
  4. /// Updated by SimonDarksideJ - major refactoring on updating current position and scroll management
  5. using System;
  6. using UnityEngine.EventSystems;
  7. namespace UnityEngine.UI.Extensions
  8. {
  9. [RequireComponent(typeof(ScrollRect))]
  10. [AddComponentMenu("Layout/Extensions/Vertical Scroll Snap")]
  11. public class VerticalScrollSnap : ScrollSnapBase
  12. {
  13. private bool updated = true;
  14. void Start()
  15. {
  16. _isVertical = true;
  17. _childAnchorPoint = new Vector2(0.5f,0);
  18. _currentPage = StartingScreen;
  19. panelDimensions = gameObject.GetComponent<RectTransform>().rect;
  20. UpdateLayout();
  21. }
  22. void Update()
  23. {
  24. updated = false;
  25. if (!_lerp && _scroll_rect.velocity == Vector2.zero)
  26. {
  27. if (!_settled && !_pointerDown)
  28. {
  29. if (!IsRectSettledOnaPage(_screensContainer.anchoredPosition))
  30. {
  31. ScrollToClosestElement();
  32. }
  33. }
  34. return;
  35. }
  36. else if (_lerp)
  37. {
  38. _screensContainer.anchoredPosition = Vector3.Lerp(_screensContainer.anchoredPosition, _lerp_target, transitionSpeed * (UseTimeScale ? Time.deltaTime : Time.unscaledDeltaTime));
  39. if (Vector3.Distance(_screensContainer.anchoredPosition, _lerp_target) < 0.1f)
  40. {
  41. _screensContainer.anchoredPosition = _lerp_target;
  42. _lerp = false;
  43. EndScreenChange();
  44. }
  45. }
  46. if (UseHardSwipe) return;
  47. CurrentPage = GetPageforPosition(_screensContainer.anchoredPosition);
  48. //If the container is moving check if it needs to settle on a page
  49. if (!_pointerDown)
  50. {
  51. if (_scroll_rect.velocity.y > 0.01 || _scroll_rect.velocity.y < -0.01)
  52. {
  53. // if the pointer is released and is moving slower than the threshold, then just land on a page
  54. if (IsRectMovingSlowerThanThreshold(0))
  55. {
  56. ScrollToClosestElement();
  57. }
  58. }
  59. }
  60. }
  61. private bool IsRectMovingSlowerThanThreshold(float startingSpeed)
  62. {
  63. return (_scroll_rect.velocity.y > startingSpeed && _scroll_rect.velocity.y < SwipeVelocityThreshold) ||
  64. (_scroll_rect.velocity.y < startingSpeed && _scroll_rect.velocity.y > -SwipeVelocityThreshold);
  65. }
  66. public void DistributePages()
  67. {
  68. _screens = _screensContainer.childCount;
  69. _scroll_rect.verticalNormalizedPosition = 0;
  70. float _offset = 0;
  71. float _dimension = 0;
  72. Rect panelDimensions = gameObject.GetComponent<RectTransform>().rect;
  73. float currentYPosition = 0;
  74. var pageStepValue = _childSize = (int)panelDimensions.height * ((PageStep == 0) ? 3 : PageStep);
  75. for (int i = 0; i < _screensContainer.transform.childCount; i++)
  76. {
  77. RectTransform child = _screensContainer.transform.GetChild(i).gameObject.GetComponent<RectTransform>();
  78. currentYPosition = _offset + i * pageStepValue;
  79. child.sizeDelta = new Vector2(panelDimensions.width, panelDimensions.height);
  80. child.anchoredPosition = new Vector2(0f, currentYPosition);
  81. child.anchorMin = child.anchorMax = child.pivot = _childAnchorPoint;
  82. }
  83. _dimension = currentYPosition + _offset * -1;
  84. _screensContainer.GetComponent<RectTransform>().offsetMax = new Vector2(0f, _dimension);
  85. }
  86. /// <summary>
  87. /// Add a new child to this Scroll Snap and recalculate it's children
  88. /// </summary>
  89. /// <param name="GO">GameObject to add to the ScrollSnap</param>
  90. public void AddChild(GameObject GO)
  91. {
  92. AddChild(GO, false);
  93. }
  94. /// <summary>
  95. /// Add a new child to this Scroll Snap and recalculate it's children
  96. /// </summary>
  97. /// <param name="GO">GameObject to add to the ScrollSnap</param>
  98. /// <param name="WorldPositionStays">Should the world position be updated to it's parent transform?</param>
  99. public void AddChild(GameObject GO, bool WorldPositionStays)
  100. {
  101. _scroll_rect.verticalNormalizedPosition = 0;
  102. GO.transform.SetParent(_screensContainer, WorldPositionStays);
  103. InitialiseChildObjectsFromScene();
  104. DistributePages();
  105. if (MaskArea) UpdateVisible();
  106. SetScrollContainerPosition();
  107. }
  108. /// <summary>
  109. /// Remove a new child to this Scroll Snap and recalculate it's children
  110. /// *Note, this is an index address (0-x)
  111. /// </summary>
  112. /// <param name="index">Index element of child to remove</param>
  113. /// <param name="ChildRemoved">Resulting removed GO</param>
  114. public void RemoveChild(int index, out GameObject ChildRemoved)
  115. {
  116. RemoveChild(index, false, out ChildRemoved);
  117. }
  118. /// <summary>
  119. /// Remove a new child to this Scroll Snap and recalculate it's children
  120. /// *Note, this is an index address (0-x)
  121. /// </summary>
  122. /// <param name="index">Index element of child to remove</param>
  123. /// <param name="WorldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before</param>
  124. /// <param name="ChildRemoved">Resulting removed GO</param>
  125. public void RemoveChild(int index, bool WorldPositionStays, out GameObject ChildRemoved)
  126. {
  127. ChildRemoved = null;
  128. if (index < 0 || index > _screensContainer.childCount)
  129. {
  130. return;
  131. }
  132. _scroll_rect.verticalNormalizedPosition = 0;
  133. Transform child = _screensContainer.transform.GetChild(index);
  134. child.SetParent(null, WorldPositionStays);
  135. ChildRemoved = child.gameObject;
  136. InitialiseChildObjectsFromScene();
  137. DistributePages();
  138. if (MaskArea) UpdateVisible();
  139. if (_currentPage > _screens - 1)
  140. {
  141. CurrentPage = _screens - 1;
  142. }
  143. SetScrollContainerPosition();
  144. }
  145. /// <summary>
  146. /// Remove all children from this ScrollSnap
  147. /// </summary>
  148. /// <param name="ChildrenRemoved">Array of child GO's removed</param>
  149. public void RemoveAllChildren(out GameObject[] ChildrenRemoved)
  150. {
  151. RemoveAllChildren(false, out ChildrenRemoved);
  152. }
  153. /// <summary>
  154. /// Remove all children from this ScrollSnap
  155. /// </summary>
  156. /// <param name="WorldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before</param>
  157. /// <param name="ChildrenRemoved">Array of child GO's removed</param>
  158. public void RemoveAllChildren(bool WorldPositionStays, out GameObject[] ChildrenRemoved)
  159. {
  160. var _screenCount = _screensContainer.childCount;
  161. ChildrenRemoved = new GameObject[_screenCount];
  162. for (int i = _screenCount - 1; i >= 0; i--)
  163. {
  164. ChildrenRemoved[i] = _screensContainer.GetChild(i).gameObject;
  165. ChildrenRemoved[i].transform.SetParent(null, WorldPositionStays);
  166. }
  167. _scroll_rect.verticalNormalizedPosition = 0;
  168. CurrentPage = 0;
  169. InitialiseChildObjectsFromScene();
  170. DistributePages();
  171. if (MaskArea) UpdateVisible();
  172. }
  173. private void SetScrollContainerPosition()
  174. {
  175. _scrollStartPosition = _screensContainer.anchoredPosition.y;
  176. _scroll_rect.verticalNormalizedPosition = (float)(_currentPage) / (_screens - 1);
  177. OnCurrentScreenChange(_currentPage);
  178. }
  179. /// <summary>
  180. /// used for changing / updating between screen resolutions
  181. /// </summary>
  182. public void UpdateLayout()
  183. {
  184. _lerp = false;
  185. DistributePages();
  186. if (MaskArea) UpdateVisible();
  187. SetScrollContainerPosition();
  188. OnCurrentScreenChange(_currentPage);
  189. }
  190. private void OnRectTransformDimensionsChange()
  191. {
  192. if (_childAnchorPoint != Vector2.zero)
  193. {
  194. UpdateLayout();
  195. }
  196. }
  197. private void OnEnable()
  198. {
  199. InitialiseChildObjectsFromScene();
  200. DistributePages();
  201. if (MaskArea)
  202. UpdateVisible();
  203. if (JumpOnEnable || !RestartOnEnable)
  204. SetScrollContainerPosition();
  205. if (RestartOnEnable)
  206. GoToScreen(StartingScreen);
  207. }
  208. /// <summary>
  209. /// Release screen to swipe
  210. /// </summary>
  211. /// <param name="eventData"></param>
  212. public override void OnEndDrag(PointerEventData eventData)
  213. {
  214. if (updated)
  215. {
  216. return;
  217. }
  218. // to prevent double dragging, only act on EndDrag once per frame
  219. updated = true;
  220. _pointerDown = false;
  221. if (_scroll_rect.vertical)
  222. {
  223. if (UseSwipeDeltaThreshold && Math.Abs(eventData.delta.y) < SwipeDeltaThreshold)
  224. {
  225. ScrollToClosestElement();
  226. }
  227. else
  228. {
  229. var distance = Vector3.Distance(_startPosition, _screensContainer.anchoredPosition);
  230. if (UseHardSwipe)
  231. {
  232. _scroll_rect.velocity = Vector3.zero;
  233. if (distance > FastSwipeThreshold)
  234. {
  235. if (_startPosition.y - _screensContainer.anchoredPosition.y > 0)
  236. {
  237. NextScreen();
  238. }
  239. else
  240. {
  241. PreviousScreen();
  242. }
  243. }
  244. else
  245. {
  246. ScrollToClosestElement();
  247. }
  248. }
  249. else
  250. {
  251. if (UseFastSwipe && distance < panelDimensions.height + FastSwipeThreshold && distance >= 1f)
  252. {
  253. _scroll_rect.velocity = Vector3.zero;
  254. if (_startPosition.y - _screensContainer.anchoredPosition.y > 0)
  255. {
  256. if (_startPosition.y - _screensContainer.anchoredPosition.y > _childSize / 3)
  257. {
  258. ScrollToClosestElement();
  259. }
  260. else
  261. {
  262. NextScreen();
  263. }
  264. }
  265. else
  266. {
  267. if (_startPosition.y - _screensContainer.anchoredPosition.y > -_childSize / 3)
  268. {
  269. ScrollToClosestElement();
  270. }
  271. else
  272. {
  273. PreviousScreen();
  274. }
  275. }
  276. }
  277. }
  278. }
  279. }
  280. }
  281. }
  282. }