From 37fa5da1c8d968e4be52cd7daa88878f46adb3dc Mon Sep 17 00:00:00 2001 From: "Simon (darkside) Jackson" Date: Wed, 14 Oct 2020 18:03:49 +0100 Subject: [PATCH] Adding new magnetic scroll control Includes some fix updates to the Infinite scroll for content of various sizes --- Examples~ | 2 +- .../Utilities/UIExtensionsInputManager.cs | 12 ++ .../Scripts/Utilities/UI_InfiniteScroll.cs | 63 ++++-- .../Utilities/UI_MagneticInfiniteScroll.cs | 204 ++++++++++++++++++ .../UI_MagneticInfiniteScroll.cs.meta | 11 + 5 files changed, 278 insertions(+), 14 deletions(-) create mode 100644 Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs create mode 100644 Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs.meta diff --git a/Examples~ b/Examples~ index a8ce2ee..9d756f7 160000 --- a/Examples~ +++ b/Examples~ @@ -1 +1 @@ -Subproject commit a8ce2ee705be43c98db82e8307b5b4bf4801387e +Subproject commit 9d756f7bd9c2195d34e482c9402b715cf760fc96 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..aa84881 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) /// /// @@ -29,12 +30,12 @@ namespace UnityEngine.UI.Extensions 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 items = new List(); + protected List items = new List(); private Vector2 _newAnchoredPosition = Vector2.zero; //TO DISABLE FLICKERING OBJECT WHEN SCROLL VIEW IS IDLE IN BETWEEN OBJECTS private float _treshold = 100f; @@ -42,12 +43,52 @@ namespace UnityEngine.UI.Extensions private float _recordOffsetX = 0; private float _recordOffsetY = 0; - void Awake() + protected virtual void Awake() { if (!InitByUser) Init(); } + public virtual void SetNewItems(ref List 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()); + } + + _itemCount = _scrollRect.content.childCount; + } + public void Init() { if (GetComponent() != 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()); - } if (_scrollRect.content.GetComponent() != null) { _verticalLayoutGroup = _scrollRect.content.GetComponent(); @@ -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().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().rect.width/2 + items[0].sizeDelta.x; + _disableMarginX = _recordOffsetX * _itemCount / 2; } if (_verticalLayoutGroup) diff --git a/Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs b/Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs new file mode 100644 index 0000000..b28fcd6 --- /dev/null +++ b/Runtime/Scripts/Utilities/UI_MagneticInfiniteScroll.cs @@ -0,0 +1,204 @@ +/// Credit Febo Zodiaco +/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/349/magnticinfinitescroll +/// + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace UnityEngine.UI.Extensions +{ + [AddComponentMenu("UI/Extensions/UI Magnetic Infinite Scroll")] + public class UI_MagneticInfiniteScroll : UI_InfiniteScroll + { + public event Action OnNewSelect; + + [Tooltip("The pointer to the pivot, the visual element for centering objects.")] + [SerializeField] + private RectTransform pivot = null; + [Tooltip("The pointer to the object container")] + [SerializeField] + private RectTransform content = 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 initial index of the object which must be initially centered")] + private int indexStart = 0; + [SerializeField] + [Tooltip("The time to decelerate and aim for the pivot")] + private float timeForDeceleration = 0.05f; + + [SerializeField] + private ScrollRect scrollRect = null; + + 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 Items { get; } + + protected override void Awake() + { + base.Awake(); + StartCoroutine(SetInitContent()); + } + + private void Update() + { + if (!content || !pivot || !_useMagnetic || !_isMovement || items == null || scrollRect == null) + { + return; + } + + float currentPosition = GetRightAxis(content.anchoredPosition); + _currentSpeed = Mathf.Abs(currentPosition - _pastPosition); + _pastPosition = currentPosition; + if (Mathf.Abs(_currentSpeed) > maxSpeedForMagnetic) + { + return; + } + + if (_isStopping) + { + Vector2 anchoredPosition = content.anchoredPosition; + _currentTime += Time.deltaTime; + float valueLerp = _currentTime / timeForDeceleration; + + float newPosition = Mathf.Lerp(GetRightAxis(anchoredPosition), _stopValue, valueLerp); + + 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 void Drag() + { + float currentPosition = GetRightAxis(UIExtensionsInputManager.MousePosition); + + initMovementDirection = Mathf.Sign(currentPosition - _pastPositionMouseSpeed); + _pastPositionMouseSpeed = currentPosition; + _useMagnetic = false; + _isStopping = false; + } + + public void EndDrag() + { + FinishPrepareMovement(); + } + + public override void SetNewItems(ref List newItems) + { + foreach (var element in newItems) + { + RectTransform rectTransform = element.GetComponent(); + if (rectTransform && pivot) + { + rectTransform.sizeDelta = pivot.sizeDelta; + } + } + base.SetNewItems(ref newItems); + } + + public void Scroll() + { + initMovementDirection = -UIExtensionsInputManager.MouseScrollDelta.y; + FinishPrepareMovement(); + } + + public void SetContentInPivot(int index) + { + float newPos = GetAnchoredPositionForPivot(index); + Vector2 anchoredPosition = content.anchoredPosition; + + if (content) + { + content.anchoredPosition = _isVertical ? new Vector2(anchoredPosition.x, newPos) : + new Vector2(newPos, anchoredPosition.y); + _pastPosition = GetRightAxis(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; + } + } +} 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: