18 [RequireComponent(typeof(ScrollRect))]
19 [AddComponentMenu(
"UI/Extensions/Scroll Snap")]
20 public class ScrollSnap : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollSnap
30 private ScrollRect _scroll_rect;
32 private RectTransform _scrollRectTransform;
34 private Transform _listContainerTransform;
40 private int _startingPage = 0;
43 private Vector3[] _pageAnchorPositions;
45 private Vector3 _lerpTarget;
50 private float _listContainerMinPosition;
52 private float _listContainerMaxPosition;
54 private float _listContainerSize;
56 private RectTransform _listContainerRectTransform;
58 private Vector2 _listContainerCachedSize;
60 private float _itemSize;
62 private int _itemsCount = 0;
65 private bool _startDrag =
true;
67 private Vector3 _positionOnDragStart =
new Vector3();
69 private int _pageOnDragStart;
71 private bool _fastSwipeTimer =
false;
73 private int _fastSwipeCounter = 0;
75 private int _fastSwipeTarget = 10;
77 [Tooltip(
"Button to go to the next page. (optional)")]
80 [Tooltip(
"Button to go to the previous page. (optional)")]
83 [Tooltip(
"Number of items visible in one page of scroll frame.")]
84 [RangeAttribute(1, 100)]
87 [Tooltip(
"Sets minimum width of list items to 1/itemsVisibleAtOnce.")]
90 [Tooltip(
"If you wish to update scrollbar numberOfSteps to number of active children on list.")]
93 [Tooltip(
"If you wish to update scrollrect sensitivity to size of list element.")]
111 _scroll_rect = gameObject.GetComponent<ScrollRect>();
112 _scrollRectTransform = gameObject.GetComponent<RectTransform>();
113 _listContainerTransform = _scroll_rect.content;
114 _listContainerRectTransform = _listContainerTransform.GetComponent<RectTransform>();
137 if (_scroll_rect.horizontalScrollbar !=
null && _scroll_rect.horizontal)
143 if (_scroll_rect.verticalScrollbar !=
null && _scroll_rect.vertical)
153 float currentSize = 0;
157 currentSize = _listContainerRectTransform.rect.width / _itemsCount;
162 currentSize = _listContainerRectTransform.rect.height / _itemsCount;
169 _scroll_rect.scrollSensitivity = _itemSize;
176 foreach (var tr
in _listContainerTransform)
178 GameObject child = ((Transform)tr).gameObject;
179 if (child.activeInHierarchy)
181 var childLayout = child.GetComponent<LayoutElement>();
183 if (childLayout ==
null)
185 childLayout = child.AddComponent<LayoutElement>();
188 childLayout.minWidth = _itemSize;
194 foreach (var tr
in _listContainerTransform)
196 GameObject child = ((Transform)tr).gameObject;
197 if (child.activeInHierarchy)
199 var childLayout = child.GetComponent<LayoutElement>();
201 if (childLayout ==
null)
203 childLayout = child.AddComponent<LayoutElement>();
206 childLayout.minHeight = _itemSize;
215 if (!_listContainerRectTransform.rect.size.Equals(_listContainerCachedSize))
220 foreach (var tr
in _listContainerTransform)
222 if (((Transform)tr).gameObject.activeInHierarchy)
230 Array.Resize(ref _pageAnchorPositions, activeCount);
239 _scroll_rect.horizontalNormalizedPosition = 0;
240 _listContainerMaxPosition = _listContainerTransform.localPosition.x;
241 _scroll_rect.horizontalNormalizedPosition = 1;
242 _listContainerMinPosition = _listContainerTransform.localPosition.x;
244 _listContainerSize = _listContainerMaxPosition - _listContainerMinPosition;
246 for (var i = 0; i < _pages; i++)
248 _pageAnchorPositions[i] =
new Vector3(
249 _listContainerMaxPosition - _itemSize * i,
250 _listContainerTransform.localPosition.y,
251 _listContainerTransform.localPosition.z
259 _scroll_rect.verticalNormalizedPosition = 1;
260 _listContainerMinPosition = _listContainerTransform.localPosition.y;
261 _scroll_rect.verticalNormalizedPosition = 0;
262 _listContainerMaxPosition = _listContainerTransform.localPosition.y;
264 _listContainerSize = _listContainerMaxPosition - _listContainerMinPosition;
266 for (var i = 0; i < _pages; i++)
268 _pageAnchorPositions[i] =
new Vector3(
269 _listContainerTransform.localPosition.x,
270 _listContainerMinPosition + _itemSize * i,
271 _listContainerTransform.localPosition.z
277 _startingPage = Mathf.Min(_startingPage, _pages);
281 if (_itemsCount != activeCount)
286 _itemsCount = activeCount;
287 _listContainerCachedSize.Set(_listContainerRectTransform.rect.size.x, _listContainerRectTransform.rect.size.y);
296 _scroll_rect.horizontalNormalizedPosition = _pages > 1 ? (float)_startingPage / (
float)(_pages - 1) : 0;
300 _scroll_rect.verticalNormalizedPosition = _pages > 1 ? (float)(_pages - _startingPage - 1) / (float)(_pages - 1) : 0;
304 private void UpdateScrollbar(
bool linkSteps)
310 if (_scroll_rect.horizontalScrollbar !=
null)
312 _scroll_rect.horizontalScrollbar.numberOfSteps = _pages;
317 if (_scroll_rect.verticalScrollbar !=
null)
319 _scroll_rect.verticalScrollbar.numberOfSteps = _pages;
327 if (_scroll_rect.horizontalScrollbar !=
null)
329 _scroll_rect.horizontalScrollbar.numberOfSteps = 0;
334 if (_scroll_rect.verticalScrollbar !=
null)
336 _scroll_rect.verticalScrollbar.numberOfSteps = 0;
349 UpdateScrollbar(
false);
351 _listContainerTransform.localPosition =
Vector3.Lerp(_listContainerTransform.localPosition, _lerpTarget, 7.5f * Time.deltaTime);
353 if (
Vector3.Distance(_listContainerTransform.localPosition, _lerpTarget) < 0.001f)
355 _listContainerTransform.localPosition = _lerpTarget;
362 if (
Vector3.Distance(_listContainerTransform.localPosition, _lerpTarget) < 10f)
374 private bool fastSwipe =
false;
385 _lerpTarget = _pageAnchorPositions[
CurrentPage() + 1];
399 _lerpTarget = _pageAnchorPositions[
CurrentPage() - 1];
406 private void NextScreenCommand()
408 if (_pageOnDragStart < _pages - 1)
413 _lerpTarget = _pageAnchorPositions[targetPage];
415 PageChanged(targetPage);
420 private void PrevScreenCommand()
422 if (_pageOnDragStart > 0)
427 _lerpTarget = _pageAnchorPositions[targetPage];
429 PageChanged(targetPage);
441 pos = _listContainerMaxPosition - _listContainerTransform.localPosition.x;
442 pos = Mathf.Clamp(pos, 0, _listContainerSize);
446 pos = _listContainerTransform.localPosition.y - _listContainerMinPosition;
447 pos = Mathf.Clamp(pos, 0, _listContainerSize);
450 float page = pos / _itemSize;
452 return Mathf.Clamp(Mathf.RoundToInt(page), 0, _pages);
465 if (0 <= page && page < _pages)
469 _lerpTarget = _pageAnchorPositions[page];
476 private void PageChanged(
int currentPage)
478 _startingPage = currentPage;
482 NextButton.interactable = currentPage < _pages - 1;
499 UpdateScrollbar(
false);
501 _fastSwipeCounter = 0;
502 _fastSwipeTimer =
true;
504 _positionOnDragStart = eventData.position;
515 change = _positionOnDragStart.x - eventData.position.x;
519 change = -_positionOnDragStart.y + eventData.position.y;
525 _fastSwipeTimer =
false;
527 if (_fastSwipeCounter <= _fastSwipeTarget)
558 public void OnDrag(PointerEventData eventData)
UnityEngine.UI.Button Button
Credit Erdener Gonenc - @PixelEnvision.