diff --git a/Examples~ b/Examples~ index a8ce2ee..906bad3 160000 --- a/Examples~ +++ b/Examples~ @@ -1 +1 @@ -Subproject commit a8ce2ee705be43c98db82e8307b5b4bf4801387e +Subproject commit 906bad37c84bc2abc156aeef50bcb93b85ea08b3 diff --git a/Runtime/Scripts/Utilities/UIExtensionsInputManager.cs b/Runtime/Scripts/Utilities/UIExtensionsInputManager.cs index 2445aff..450fcce 100644 --- a/Runtime/Scripts/Utilities/UIExtensionsInputManager.cs +++ b/Runtime/Scripts/Utilities/UIExtensionsInputManager.cs @@ -273,6 +273,18 @@ namespace UnityEngine.UI.Extensions return Input.mousePosition; #else return Mouse.current.position.ReadValue(); +#endif + } + } + + public static Vector3 MouseScrollDelta + { + get + { +#if ENABLE_LEGACY_INPUT_MANAGER + return Input.mouseScrollDelta; +#else + return Mouse.current.position.ReadValue(); #endif } } diff --git a/Runtime/Scripts/Utilities/UI_InfiniteScroll.cs b/Runtime/Scripts/Utilities/UI_InfiniteScroll.cs index dde85f0..742788b 100644 --- a/Runtime/Scripts/Utilities/UI_InfiniteScroll.cs +++ b/Runtime/Scripts/Utilities/UI_InfiniteScroll.cs @@ -1,6 +1,7 @@ /// Credit Tomasz Schelenz /// Sourced from - https://bitbucket.org/SimonDarksideJ/unity-ui-extensions/issues/81/infinite-scrollrect /// 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) +/// Updated by Febo Zodiaco - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/349/magnticinfinitescroll using System.Collections.Generic; @@ -11,10 +12,10 @@ namespace UnityEngine.UI.Extensions /// /// Fields /// - InitByUSer - in case your scrollrect is populated from code, you can explicitly Initialize the infinite scroll after your scroll is ready - /// by callin Init() method + /// by calling Init() method /// /// Notes - /// - doesn't work in both vertical and horizontal orientation at the same time. + /// - does not work in both vertical and horizontal orientation at the same time. /// - in order to work it disables layout components and size fitter if present(automatically) /// /// </summary> @@ -24,30 +25,70 @@ namespace UnityEngine.UI.Extensions //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) [Tooltip("If false, will Init automatically, otherwise you need to call Init() method")] public bool InitByUser = false; - private ScrollRect _scrollRect; + protected ScrollRect _scrollRect; private ContentSizeFitter _contentSizeFitter; private VerticalLayoutGroup _verticalLayoutGroup; private HorizontalLayoutGroup _horizontalLayoutGroup; private GridLayoutGroup _gridLayoutGroup; - private bool _isVertical = false; - private bool _isHorizontal = false; + protected bool _isVertical = false; + protected bool _isHorizontal = false; private float _disableMarginX = 0; private float _disableMarginY = 0; private bool _hasDisabledGridComponents = false; - private List<RectTransform> items = new List<RectTransform>(); + protected List<RectTransform> items = new List<RectTransform>(); private Vector2 _newAnchoredPosition = Vector2.zero; //TO DISABLE FLICKERING OBJECT WHEN SCROLL VIEW IS IDLE IN BETWEEN OBJECTS - private float _treshold = 100f; + private float _threshold = 100f; private int _itemCount = 0; private float _recordOffsetX = 0; private float _recordOffsetY = 0; - void Awake() + protected virtual void Awake() { if (!InitByUser) Init(); } + public virtual void SetNewItems(ref List<Transform> newItems) + { + if (_scrollRect != null) + { + if (_scrollRect.content == null && newItems == null) + { + return; + } + + if (items != null) + { + items.Clear(); + } + + for (int i = _scrollRect.content.childCount - 1; i >= 0; i--) + { + Transform child = _scrollRect.content.GetChild(i); + child.SetParent(null); + GameObject.DestroyImmediate(child.gameObject); + } + + foreach (Transform newItem in newItems) + { + newItem.SetParent(_scrollRect.content); + } + + SetItems(); + } + } + + private void SetItems() + { + for (int i = 0; i < _scrollRect.content.childCount; i++) + { + items.Add(_scrollRect.content.GetChild(i).GetComponent<RectTransform>()); + } + + _itemCount = _scrollRect.content.childCount; + } + public void Init() { if (GetComponent<ScrollRect>() != null) @@ -56,10 +97,6 @@ namespace UnityEngine.UI.Extensions _scrollRect.onValueChanged.AddListener(OnScroll); _scrollRect.movementType = ScrollRect.MovementType.Unrestricted; - for (int i = 0; i < _scrollRect.content.childCount; i++) - { - items.Add(_scrollRect.content.GetChild(i).GetComponent<RectTransform>()); - } if (_scrollRect.content.GetComponent<VerticalLayoutGroup>() != null) { _verticalLayoutGroup = _scrollRect.content.GetComponent<VerticalLayoutGroup>(); @@ -85,7 +122,7 @@ namespace UnityEngine.UI.Extensions Debug.LogError("UI_InfiniteScroll doesn't support scrolling in both directions, please choose one direction (horizontal or vertical)"); } - _itemCount = _scrollRect.content.childCount; + SetItems(); } else { @@ -102,7 +139,7 @@ namespace UnityEngine.UI.Extensions { _recordOffsetY *= -1; } - _disableMarginY = _recordOffsetY * _itemCount / 2;// _scrollRect.GetComponent<RectTransform>().rect.height/2 + items[0].sizeDelta.y; + _disableMarginY = _recordOffsetY * _itemCount / 2; } if (_isHorizontal) { @@ -111,7 +148,7 @@ namespace UnityEngine.UI.Extensions { _recordOffsetX *= -1; } - _disableMarginX = _recordOffsetX * _itemCount / 2;//_scrollRect.GetComponent<RectTransform>().rect.width/2 + items[0].sizeDelta.x; + _disableMarginX = _recordOffsetX * _itemCount / 2; } if (_verticalLayoutGroup) @@ -142,7 +179,7 @@ namespace UnityEngine.UI.Extensions { if (_isHorizontal) { - if (_scrollRect.transform.InverseTransformPoint(items[i].gameObject.transform.position).x > _disableMarginX + _treshold) + if (_scrollRect.transform.InverseTransformPoint(items[i].gameObject.transform.position).x > _disableMarginX + _threshold) { _newAnchoredPosition = items[i].anchoredPosition; _newAnchoredPosition.x -= _itemCount * _recordOffsetX; @@ -160,7 +197,7 @@ namespace UnityEngine.UI.Extensions if (_isVertical) { - if (_scrollRect.transform.InverseTransformPoint(items[i].gameObject.transform.position).y > _disableMarginY + _treshold) + if (_scrollRect.transform.InverseTransformPoint(items[i].gameObject.transform.position).y > _disableMarginY + _threshold) { _newAnchoredPosition = items[i].anchoredPosition; _newAnchoredPosition.y -= _itemCount * _recordOffsetY; diff --git a/Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs b/Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs new file mode 100644 index 0000000..0d9b6dc --- /dev/null +++ b/Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs @@ -0,0 +1,199 @@ +/// Credit Febo Zodiaco +/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/349/magnticinfinitescroll +/// + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine.EventSystems; + +namespace UnityEngine.UI.Extensions +{ + [AddComponentMenu("UI/Extensions/UI Magnetic Infinite Scroll")] + public class UI_MagneticInfiniteScroll : UI_InfiniteScroll, IDragHandler, IEndDragHandler, IScrollHandler + { + public event Action<GameObject> OnNewSelect; + + [Tooltip("The pointer to the pivot, the visual element for centering objects.")] + [SerializeField] + private RectTransform pivot = null; + [Tooltip("The maximum speed that allows you to activate the magnet to center on the pivot")] + [SerializeField] + private float maxSpeedForMagnetic = 10f; + [SerializeField] + [Tooltip("The index of the object which must be initially centered")] + private int indexStart = 0; + [SerializeField] + [Tooltip("The time to decelerate and aim to the pivot")] + private float timeForDeceleration = 0.05f; + + private float _pastPositionMouseSpeed; + private float _initMovementDirection = 0; + private float _pastPosition = 0; + + private float _currentSpeed = 0.0f; + private float _stopValue = 0.0f; + private readonly float _waitForContentSet = 0.1f; + private float _currentTime = 0; + private int _nearestIndex = 0; + + private bool _useMagnetic = true; + private bool _isStopping = false; + private bool _isMovement = false; + + public List<RectTransform> Items { get; } + + protected override void Awake() + { + base.Awake(); + StartCoroutine(SetInitContent()); + } + + private void Update() + { + if (_scrollRect == null || !_scrollRect.content || !pivot || !_useMagnetic || !_isMovement || items == null) + { + return; + } + + float currentPosition = GetRightAxis(_scrollRect.content.anchoredPosition); + _currentSpeed = Mathf.Abs(currentPosition - _pastPosition); + _pastPosition = currentPosition; + if (Mathf.Abs(_currentSpeed) > maxSpeedForMagnetic) + { + return; + } + + if (_isStopping) + { + Vector2 anchoredPosition = _scrollRect.content.anchoredPosition; + _currentTime += Time.deltaTime; + float valueLerp = _currentTime / timeForDeceleration; + + float newPosition = Mathf.Lerp(GetRightAxis(anchoredPosition), _stopValue, valueLerp); + + _scrollRect.content.anchoredPosition = _isVertical ? new Vector2(anchoredPosition.x, newPosition) : + new Vector2(newPosition, anchoredPosition.y); + + + if (newPosition == GetRightAxis(anchoredPosition) && _nearestIndex > 0 && _nearestIndex < items.Count) + { + _isStopping = false; + _isMovement = false; + var item = items[_nearestIndex]; + if (item != null && OnNewSelect != null) + { + + OnNewSelect.Invoke(item.gameObject); + } + } + } + else + { + float distance = Mathf.Infinity * (-_initMovementDirection); + + for (int i = 0; i < items.Count; i++) + { + var item = items[i]; + if (item == null) + { + continue; + } + + var aux = GetRightAxis(item.position) - GetRightAxis(pivot.position); + + if ((_initMovementDirection <= 0 && aux < distance && aux > 0) || + (_initMovementDirection > 0 && aux > distance && aux < 0)) + { + distance = aux; + _nearestIndex = i; + } + } + + _isStopping = true; + _stopValue = GetAnchoredPositionForPivot(_nearestIndex); + _scrollRect.StopMovement(); + } + } + + public override void SetNewItems(ref List<Transform> newItems) + { + foreach (var element in newItems) + { + RectTransform rectTransform = element.GetComponent<RectTransform>(); + if (rectTransform && pivot) + { + rectTransform.sizeDelta = pivot.sizeDelta; + } + } + base.SetNewItems(ref newItems); + } + + public void SetContentInPivot(int index) + { + float newPos = GetAnchoredPositionForPivot(index); + Vector2 anchoredPosition = _scrollRect.content.anchoredPosition; + + if (_scrollRect.content) + { + _scrollRect.content.anchoredPosition = _isVertical ? new Vector2(anchoredPosition.x, newPos) : + new Vector2(newPos, anchoredPosition.y); + _pastPosition = GetRightAxis(_scrollRect.content.anchoredPosition); + } + } + + private IEnumerator SetInitContent() + { + yield return new WaitForSeconds(_waitForContentSet); + SetContentInPivot(indexStart); + } + + private float GetAnchoredPositionForPivot(int index) + { + if (!pivot || items == null || items.Count < 0) + { + return 0f; + } + + index = Mathf.Clamp(index, 0, items.Count - 1); + + float posItem = GetRightAxis(items[index].anchoredPosition); + float posPivot = GetRightAxis(pivot.anchoredPosition); + return posPivot - posItem; + } + + private void FinishPrepareMovement() + { + _isMovement = true; + _useMagnetic = true; + _isStopping = false; + _currentTime = 0; + } + + private float GetRightAxis(Vector2 vector) + { + return _isVertical ? vector.y : vector.x; + } + + public void OnDrag(PointerEventData eventData) + { + float currentPosition = GetRightAxis(UIExtensionsInputManager.MousePosition); + + _initMovementDirection = Mathf.Sign(currentPosition - _pastPositionMouseSpeed); + _pastPositionMouseSpeed = currentPosition; + _useMagnetic = false; + _isStopping = false; + } + + public void OnEndDrag(PointerEventData eventData) + { + FinishPrepareMovement(); + } + + public void OnScroll(PointerEventData eventData) + { + _initMovementDirection = -UIExtensionsInputManager.MouseScrollDelta.y; + FinishPrepareMovement(); + } + } +} diff --git a/Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs.meta b/Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs.meta new file mode 100644 index 0000000..1fe4d6f --- /dev/null +++ b/Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8a2ddc0989b894a499a02fb975aa0322 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: