Skip to content
This repository was archived by the owner on Mar 20, 2023. It is now read-only.

Commit 0d29dce

Browse files
Merged in Add/MagneticScroll (pull request #100)
Adding new magnetic scroll control, docs updated
2 parents dbd3bcb + 61d0c8e commit 0d29dce

File tree

5 files changed

+277
-18
lines changed

5 files changed

+277
-18
lines changed

Examples~

Submodule Examples~ updated from a8ce2ee to 906bad3

Runtime/Scripts/Utilities/UIExtensionsInputManager.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,18 @@ public static Vector3 MousePosition
273273
return Input.mousePosition;
274274
#else
275275
return Mouse.current.position.ReadValue();
276+
#endif
277+
}
278+
}
279+
280+
public static Vector3 MouseScrollDelta
281+
{
282+
get
283+
{
284+
#if ENABLE_LEGACY_INPUT_MANAGER
285+
return Input.mouseScrollDelta;
286+
#else
287+
return Mouse.current.position.ReadValue();
276288
#endif
277289
}
278290
}

Runtime/Scripts/Utilities/UI_InfiniteScroll.cs

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/// Credit Tomasz Schelenz
22
/// Sourced from - https://bitbucket.org/SimonDarksideJ/unity-ui-extensions/issues/81/infinite-scrollrect
33
/// Demo - https://www.youtube.com/watch?v=uVTV7Udx78k - configures automatically. - works in both vertical and horizontal (but not both at the same time) - drag and drop - can be initialized by code (in case you populate your scrollview content from code)
4+
/// Updated by Febo Zodiaco - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/349/magnticinfinitescroll
45

56
using System.Collections.Generic;
67

@@ -11,10 +12,10 @@ namespace UnityEngine.UI.Extensions
1112
///
1213
/// Fields
1314
/// - InitByUSer - in case your scrollrect is populated from code, you can explicitly Initialize the infinite scroll after your scroll is ready
14-
/// by callin Init() method
15+
/// by calling Init() method
1516
///
1617
/// Notes
17-
/// - doesn't work in both vertical and horizontal orientation at the same time.
18+
/// - does not work in both vertical and horizontal orientation at the same time.
1819
/// - in order to work it disables layout components and size fitter if present(automatically)
1920
///
2021
/// </summary>
@@ -24,30 +25,70 @@ public class UI_InfiniteScroll : MonoBehaviour
2425
//if true user will need to call Init() method manually (in case the contend of the scrollview is generated from code or requires special initialization)
2526
[Tooltip("If false, will Init automatically, otherwise you need to call Init() method")]
2627
public bool InitByUser = false;
27-
private ScrollRect _scrollRect;
28+
protected ScrollRect _scrollRect;
2829
private ContentSizeFitter _contentSizeFitter;
2930
private VerticalLayoutGroup _verticalLayoutGroup;
3031
private HorizontalLayoutGroup _horizontalLayoutGroup;
3132
private GridLayoutGroup _gridLayoutGroup;
32-
private bool _isVertical = false;
33-
private bool _isHorizontal = false;
33+
protected bool _isVertical = false;
34+
protected bool _isHorizontal = false;
3435
private float _disableMarginX = 0;
3536
private float _disableMarginY = 0;
3637
private bool _hasDisabledGridComponents = false;
37-
private List<RectTransform> items = new List<RectTransform>();
38+
protected List<RectTransform> items = new List<RectTransform>();
3839
private Vector2 _newAnchoredPosition = Vector2.zero;
3940
//TO DISABLE FLICKERING OBJECT WHEN SCROLL VIEW IS IDLE IN BETWEEN OBJECTS
40-
private float _treshold = 100f;
41+
private float _threshold = 100f;
4142
private int _itemCount = 0;
4243
private float _recordOffsetX = 0;
4344
private float _recordOffsetY = 0;
4445

45-
void Awake()
46+
protected virtual void Awake()
4647
{
4748
if (!InitByUser)
4849
Init();
4950
}
5051

52+
public virtual void SetNewItems(ref List<Transform> newItems)
53+
{
54+
if (_scrollRect != null)
55+
{
56+
if (_scrollRect.content == null && newItems == null)
57+
{
58+
return;
59+
}
60+
61+
if (items != null)
62+
{
63+
items.Clear();
64+
}
65+
66+
for (int i = _scrollRect.content.childCount - 1; i >= 0; i--)
67+
{
68+
Transform child = _scrollRect.content.GetChild(i);
69+
child.SetParent(null);
70+
GameObject.DestroyImmediate(child.gameObject);
71+
}
72+
73+
foreach (Transform newItem in newItems)
74+
{
75+
newItem.SetParent(_scrollRect.content);
76+
}
77+
78+
SetItems();
79+
}
80+
}
81+
82+
private void SetItems()
83+
{
84+
for (int i = 0; i < _scrollRect.content.childCount; i++)
85+
{
86+
items.Add(_scrollRect.content.GetChild(i).GetComponent<RectTransform>());
87+
}
88+
89+
_itemCount = _scrollRect.content.childCount;
90+
}
91+
5192
public void Init()
5293
{
5394
if (GetComponent<ScrollRect>() != null)
@@ -56,10 +97,6 @@ public void Init()
5697
_scrollRect.onValueChanged.AddListener(OnScroll);
5798
_scrollRect.movementType = ScrollRect.MovementType.Unrestricted;
5899

59-
for (int i = 0; i < _scrollRect.content.childCount; i++)
60-
{
61-
items.Add(_scrollRect.content.GetChild(i).GetComponent<RectTransform>());
62-
}
63100
if (_scrollRect.content.GetComponent<VerticalLayoutGroup>() != null)
64101
{
65102
_verticalLayoutGroup = _scrollRect.content.GetComponent<VerticalLayoutGroup>();
@@ -85,7 +122,7 @@ public void Init()
85122
Debug.LogError("UI_InfiniteScroll doesn't support scrolling in both directions, please choose one direction (horizontal or vertical)");
86123
}
87124

88-
_itemCount = _scrollRect.content.childCount;
125+
SetItems();
89126
}
90127
else
91128
{
@@ -102,7 +139,7 @@ void DisableGridComponents()
102139
{
103140
_recordOffsetY *= -1;
104141
}
105-
_disableMarginY = _recordOffsetY * _itemCount / 2;// _scrollRect.GetComponent<RectTransform>().rect.height/2 + items[0].sizeDelta.y;
142+
_disableMarginY = _recordOffsetY * _itemCount / 2;
106143
}
107144
if (_isHorizontal)
108145
{
@@ -111,7 +148,7 @@ void DisableGridComponents()
111148
{
112149
_recordOffsetX *= -1;
113150
}
114-
_disableMarginX = _recordOffsetX * _itemCount / 2;//_scrollRect.GetComponent<RectTransform>().rect.width/2 + items[0].sizeDelta.x;
151+
_disableMarginX = _recordOffsetX * _itemCount / 2;
115152
}
116153

117154
if (_verticalLayoutGroup)
@@ -142,7 +179,7 @@ public void OnScroll(Vector2 pos)
142179
{
143180
if (_isHorizontal)
144181
{
145-
if (_scrollRect.transform.InverseTransformPoint(items[i].gameObject.transform.position).x > _disableMarginX + _treshold)
182+
if (_scrollRect.transform.InverseTransformPoint(items[i].gameObject.transform.position).x > _disableMarginX + _threshold)
146183
{
147184
_newAnchoredPosition = items[i].anchoredPosition;
148185
_newAnchoredPosition.x -= _itemCount * _recordOffsetX;
@@ -160,7 +197,7 @@ public void OnScroll(Vector2 pos)
160197

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

Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)