HorizontalScrollSnap.cs 12 KB

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