diff --git a/Editor/FancyScrollView/ScrollerEditor.cs b/Editor/FancyScrollView/ScrollerEditor.cs index 3771ce4..66ce944 100644 --- a/Editor/FancyScrollView/ScrollerEditor.cs +++ b/Editor/FancyScrollView/ScrollerEditor.cs @@ -13,7 +13,7 @@ namespace UnityEngine.UI.Extensions public class ScrollerEditor : Editor { SerializedProperty viewport; - SerializedProperty directionOfRecognize; + SerializedProperty scrollDirection; SerializedProperty movementType; SerializedProperty elasticity; SerializedProperty scrollSensitivity; @@ -24,6 +24,8 @@ namespace UnityEngine.UI.Extensions SerializedProperty snapVelocityThreshold; SerializedProperty snapDuration; SerializedProperty snapEasing; + SerializedProperty draggable; + SerializedProperty scrollbar; AnimBool showElasticity; AnimBool showInertiaRelatedValues; @@ -32,7 +34,7 @@ namespace UnityEngine.UI.Extensions void OnEnable() { viewport = serializedObject.FindProperty("viewport"); - directionOfRecognize = serializedObject.FindProperty("directionOfRecognize"); + scrollDirection = serializedObject.FindProperty("scrollDirection"); movementType = serializedObject.FindProperty("movementType"); elasticity = serializedObject.FindProperty("elasticity"); scrollSensitivity = serializedObject.FindProperty("scrollSensitivity"); @@ -43,6 +45,8 @@ namespace UnityEngine.UI.Extensions snapVelocityThreshold = serializedObject.FindProperty("snap.VelocityThreshold"); snapDuration = serializedObject.FindProperty("snap.Duration"); snapEasing = serializedObject.FindProperty("snap.Easing"); + draggable = serializedObject.FindProperty("draggable"); + scrollbar = serializedObject.FindProperty("scrollbar"); showElasticity = new AnimBool(Repaint); showInertiaRelatedValues = new AnimBool(Repaint); @@ -59,7 +63,7 @@ namespace UnityEngine.UI.Extensions void SetAnimBools(bool instant) { - SetAnimBool(showElasticity, !movementType.hasMultipleDifferentValues && movementType.enumValueIndex == (int)Scroller.MovementType.Elastic, instant); + SetAnimBool(showElasticity, !movementType.hasMultipleDifferentValues && movementType.enumValueIndex == (int)MovementType.Elastic, instant); SetAnimBool(showInertiaRelatedValues, !inertia.hasMultipleDifferentValues && inertia.boolValue, instant); SetAnimBool(showSnapEnableRelatedValues, !snapEnable.hasMultipleDifferentValues && snapEnable.boolValue, instant); } @@ -82,12 +86,14 @@ namespace UnityEngine.UI.Extensions serializedObject.Update(); EditorGUILayout.PropertyField(viewport); - EditorGUILayout.PropertyField(directionOfRecognize); + EditorGUILayout.PropertyField(scrollDirection); EditorGUILayout.PropertyField(movementType); DrawMovementTypeRelatedValue(); EditorGUILayout.PropertyField(scrollSensitivity); EditorGUILayout.PropertyField(inertia); DrawInertiaRelatedValues(); + EditorGUILayout.PropertyField(draggable); + EditorGUILayout.PropertyField(scrollbar); serializedObject.ApplyModifiedProperties(); } @@ -111,7 +117,7 @@ namespace UnityEngine.UI.Extensions { using (var group = new EditorGUILayout.FadeGroupScope(showInertiaRelatedValues.faded)) { - if (!group.visible) + if (!group.visible) { return; } @@ -149,4 +155,4 @@ namespace UnityEngine.UI.Extensions } } } -} +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/Core.meta b/Scripts/Layout/FancyScrollView/Core.meta new file mode 100644 index 0000000..c5d2095 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6096cd9225d267f4da3b54947c75809c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/FancyScrollView.cs b/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs similarity index 83% rename from Scripts/Layout/FancyScrollView/FancyScrollView.cs rename to Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs index 49a3830..c524730 100644 --- a/Scripts/Layout/FancyScrollView/FancyScrollView.cs +++ b/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs @@ -1,15 +1,13 @@ /// Credit setchi (https://github.com/setchi) /// Sourced from - https://github.com/setchi/FancyScrollView -using System; using System.Collections.Generic; -using UnityEngine; namespace UnityEngine.UI.Extensions { public abstract class FancyScrollView : MonoBehaviour where TContext : class, new() { - [SerializeField, Range(float.Epsilon, 1f)] protected float cellInterval = 0.2f; + [SerializeField, Range(1e-2f, 1f)] protected float cellInterval = 0.2f; [SerializeField, Range(0f, 1f)] protected float scrollOffset = 0.5f; [SerializeField] protected bool loop = false; [SerializeField] protected Transform cellContainer = default; @@ -17,7 +15,7 @@ namespace UnityEngine.UI.Extensions readonly IList> pool = new List>(); - float currentPosition; + protected float currentPosition; protected abstract GameObject CellPrefab { get; } protected IList ItemsSource { get; set; } = new List(); @@ -27,7 +25,7 @@ namespace UnityEngine.UI.Extensions /// Updates the contents. /// /// Items source. - protected void UpdateContents(IList itemsSource) + protected virtual void UpdateContents(IList itemsSource) { ItemsSource = itemsSource; Refresh(); @@ -36,15 +34,15 @@ namespace UnityEngine.UI.Extensions /// /// Refreshes the cells. /// - protected void Refresh() => UpdatePosition(currentPosition, true); + protected virtual void Refresh() => UpdatePosition(currentPosition, true); /// /// Updates the scroll position. /// /// Position. - protected void UpdatePosition(float position) => UpdatePosition(position, false); + protected virtual void UpdatePosition(float position) => UpdatePosition(position, false); - void UpdatePosition(float position, bool forceRefresh) + protected void UpdatePosition(float position, bool forceRefresh) { currentPosition = position; @@ -62,15 +60,8 @@ namespace UnityEngine.UI.Extensions void ResizePool(float firstPosition) { - if (CellPrefab == null) - { - throw new NullReferenceException(nameof(CellPrefab)); - } - - if (cellContainer == null) - { - throw new MissingComponentException(nameof(cellContainer)); - } + Debug.Assert(CellPrefab != null); + Debug.Assert(cellContainer != null); var addCount = Mathf.CeilToInt((1f - firstPosition) / cellInterval) - pool.Count; for (var i = 0; i < addCount; i++) @@ -139,11 +130,7 @@ namespace UnityEngine.UI.Extensions #endif } - public sealed class FancyScrollViewNullContext - { - } + public sealed class FancyScrollViewNullContext { } - public abstract class FancyScrollView : FancyScrollView - { - } -} + public abstract class FancyScrollView : FancyScrollView { } +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/FancyScrollView.cs.meta b/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs.meta similarity index 100% rename from Scripts/Layout/FancyScrollView/FancyScrollView.cs.meta rename to Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs.meta diff --git a/Scripts/Layout/FancyScrollView/FancyScrollViewCell.cs b/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs similarity index 98% rename from Scripts/Layout/FancyScrollView/FancyScrollViewCell.cs rename to Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs index ed3ecd2..7628d28 100644 --- a/Scripts/Layout/FancyScrollView/FancyScrollViewCell.cs +++ b/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs @@ -1,8 +1,6 @@ /// Credit setchi (https://github.com/setchi) /// Sourced from - https://github.com/setchi/FancyScrollView -using UnityEngine; - namespace UnityEngine.UI.Extensions { public abstract class FancyScrollViewCell : MonoBehaviour where TContext : class, new() @@ -54,4 +52,4 @@ namespace UnityEngine.UI.Extensions { public sealed override void SetupContext(FancyScrollViewNullContext context) => base.SetupContext(context); } -} +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/FancyScrollViewCell.cs.meta b/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs.meta similarity index 100% rename from Scripts/Layout/FancyScrollView/FancyScrollViewCell.cs.meta rename to Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs.meta diff --git a/Scripts/Layout/FancyScrollView/EasingCore.cs b/Scripts/Layout/FancyScrollView/EasingCore.cs deleted file mode 100644 index 877f7ca..0000000 --- a/Scripts/Layout/FancyScrollView/EasingCore.cs +++ /dev/null @@ -1,214 +0,0 @@ -// -// EasingCore - https://github.com/setchi/EasingCore -// -// The MIT License (MIT) -// -// Copyright (c) 2019 setchi -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using UnityEngine; - -namespace UnityEngine.UI.Extensions.EasingCore -{ - public enum Ease - { - Linear, - InBack, - InBounce, - InCirc, - InCubic, - InElastic, - InExpo, - InQuad, - InQuart, - InQuint, - InSine, - OutBack, - OutBounce, - OutCirc, - OutCubic, - OutElastic, - OutExpo, - OutQuad, - OutQuart, - OutQuint, - OutSine, - InOutBack, - InOutBounce, - InOutCirc, - InOutCubic, - InOutElastic, - InOutExpo, - InOutQuad, - InOutQuart, - InOutQuint, - InOutSine, - } - - public static class EasingFunction - { - /// - /// Gets the easing function - /// - /// ease type - /// easing function - public static Func Get(Ease type) - { - switch (type) - { - case Ease.Linear: return Linear; - case Ease.InBack: return InBack; - case Ease.InBounce: return InBounce; - case Ease.InCirc: return InCirc; - case Ease.InCubic: return InCubic; - case Ease.InElastic: return InElastic; - case Ease.InExpo: return InExpo; - case Ease.InQuad: return InQuad; - case Ease.InQuart: return InQuart; - case Ease.InQuint: return InQuint; - case Ease.InSine: return InSine; - case Ease.OutBack: return OutBack; - case Ease.OutBounce: return OutBounce; - case Ease.OutCirc: return OutCirc; - case Ease.OutCubic: return OutCubic; - case Ease.OutElastic: return OutElastic; - case Ease.OutExpo: return OutExpo; - case Ease.OutQuad: return OutQuad; - case Ease.OutQuart: return OutQuart; - case Ease.OutQuint: return OutQuint; - case Ease.OutSine: return OutSine; - case Ease.InOutBack: return InOutBack; - case Ease.InOutBounce: return InOutBounce; - case Ease.InOutCirc: return InOutCirc; - case Ease.InOutCubic: return InOutCubic; - case Ease.InOutElastic: return InOutElastic; - case Ease.InOutExpo: return InOutExpo; - case Ease.InOutQuad: return InOutQuad; - case Ease.InOutQuart: return InOutQuart; - case Ease.InOutQuint: return InOutQuint; - case Ease.InOutSine: return InOutSine; - default: return Linear; - } - } - - static float Linear(float t) => t; - - static float InBack(float t) => t * t * t - t * Mathf.Sin(t * Mathf.PI); - - static float OutBack(float t) => 1f - InBack(1f - t); - - static float InOutBack(float t) => - t < 0.5f - ? 0.5f * InBack(2f * t) - : 0.5f * OutBack(2f * t - 1f) + 0.5f; - - static float InBounce(float t) => 1f - OutBounce(1f - t); - - static float OutBounce(float t) => - t < 4f / 11.0f ? - (121f * t * t) / 16.0f : - t < 8f / 11.0f ? - (363f / 40.0f * t * t) - (99f / 10.0f * t) + 17f / 5.0f : - t < 9f / 10.0f ? - (4356f / 361.0f * t * t) - (35442f / 1805.0f * t) + 16061f / 1805.0f : - (54f / 5.0f * t * t) - (513f / 25.0f * t) + 268f / 25.0f; - - static float InOutBounce(float t) => - t < 0.5f - ? 0.5f * InBounce(2f * t) - : 0.5f * OutBounce(2f * t - 1f) + 0.5f; - - static float InCirc(float t) => 1f - Mathf.Sqrt(1f - (t * t)); - - static float OutCirc(float t) => Mathf.Sqrt((2f - t) * t); - - static float InOutCirc(float t) => - t < 0.5f - ? 0.5f * (1 - Mathf.Sqrt(1f - 4f * (t * t))) - : 0.5f * (Mathf.Sqrt(-((2f * t) - 3f) * ((2f * t) - 1f)) + 1f); - - static float InCubic(float t) => t * t * t; - - static float OutCubic(float t) => InCubic(t - 1f) + 1f; - - static float InOutCubic(float t) => - t < 0.5f - ? 4f * t * t * t - : 0.5f * InCubic(2f * t - 2f) + 1f; - - static float InElastic(float t) => Mathf.Sin(13f * (Mathf.PI * 0.5f) * t) * Mathf.Pow(2f, 10f * (t - 1f)); - - static float OutElastic(float t) => Mathf.Sin(-13f * (Mathf.PI * 0.5f) * (t + 1)) * Mathf.Pow(2f, -10f * t) + 1f; - - static float InOutElastic(float t) => - t < 0.5f - ? 0.5f * Mathf.Sin(13f * (Mathf.PI * 0.5f) * (2f * t)) * Mathf.Pow(2f, 10f * ((2f * t) - 1f)) - : 0.5f * (Mathf.Sin(-13f * (Mathf.PI * 0.5f) * ((2f * t - 1f) + 1f)) * Mathf.Pow(2f, -10f * (2f * t - 1f)) + 2f); - - static float InExpo(float t) => Mathf.Approximately(0.0f, t) ? t : Mathf.Pow(2f, 10f * (t - 1f)); - - static float OutExpo(float t) => Mathf.Approximately(1.0f, t) ? t : 1f - Mathf.Pow(2f, -10f * t); - - static float InOutExpo(float v) => - Mathf.Approximately(0.0f, v) || Mathf.Approximately(1.0f, v) - ? v - : v < 0.5f - ? 0.5f * Mathf.Pow(2f, (20f * v) - 10f) - : -0.5f * Mathf.Pow(2f, (-20f * v) + 10f) + 1f; - - static float InQuad(float t) => t * t; - - static float OutQuad(float t) => -t * (t - 2f); - - static float InOutQuad(float t) => - t < 0.5f - ? 2f * t * t - : -2f * t * t + 4f * t - 1f; - - static float InQuart(float t) => t * t * t * t; - - static float OutQuart(float t) - { - var u = t - 1f; - return u * u * u * (1f - t) + 1f; - } - - static float InOutQuart(float t) => - t < 0.5f - ? 8f * InQuart(t) - : -8f * InQuart(t - 1f) + 1f; - - static float InQuint(float t) => t * t * t * t * t; - - static float OutQuint(float t) => InQuint(t - 1f) + 1f; - - static float InOutQuint(float t) => - t < 0.5f - ? 16f * InQuint(t) - : 0.5f * InQuint(2f * t - 2f) + 1f; - - static float InSine(float t) => Mathf.Sin((t - 1f) * (Mathf.PI * 0.5f)) + 1f; - - static float OutSine(float t) => Mathf.Sin(t * (Mathf.PI * 0.5f)); - - static float InOutSine(float t) => 0.5f * (1f - Mathf.Cos(t * Mathf.PI)); - } -} diff --git a/Scripts/Layout/FancyScrollView/ScrollRect.meta b/Scripts/Layout/FancyScrollView/ScrollRect.meta new file mode 100644 index 0000000..f409f53 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e2c5beedd885c4490a86bb4973f965bf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs b/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs new file mode 100644 index 0000000..d3c1566 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs @@ -0,0 +1,12 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +namespace UnityEngine.UI.Extensions +{ + public enum Alignment + { + Head, + Center, + Tail, + } +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/Scroller.cs.meta b/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs.meta similarity index 83% rename from Scripts/Layout/FancyScrollView/Scroller.cs.meta rename to Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs.meta index 47ea59a..9c0ac48 100644 --- a/Scripts/Layout/FancyScrollView/Scroller.cs.meta +++ b/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 006f67d6afad7c2479f7188b033aea31 +guid: 3e786463ba934403cacf7e8c0d5822d7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs new file mode 100644 index 0000000..b6b76d3 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs @@ -0,0 +1,191 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +using System; +using System.Collections.Generic; +using UnityEngine.UI.Extensions.EasingCore; + +namespace UnityEngine.UI.Extensions +{ + [RequireComponent(typeof(Scroller))] + public abstract class FancyScrollRect : FancyScrollView + where TContext : class, IFancyScrollRectContext, new() + { + [SerializeField] protected float reuseCellMarginCount = 0f; + [SerializeField] protected float paddingHead = 0f; + [SerializeField] protected float paddingTail = 0f; + [SerializeField] protected float spacing = 0f; + + protected virtual float ScrollLength => 1f / Mathf.Max(cellInterval, 1e-2f) - 1f; + + protected virtual float ViewportLength => ScrollLength - reuseCellMarginCount * 2f; + + protected virtual float MaxScrollPosition => ItemsSource.Count + - ScrollLength + + reuseCellMarginCount * 2f + + (paddingHead + (paddingTail - spacing)) / (CellSize + spacing); + + protected virtual bool ScrollEnabled => MaxScrollPosition > 0f; + + protected virtual float CellSize => Scroller.ScrollDirection == ScrollDirection.Horizontal + ? CellRectTransform.rect.width + : CellRectTransform.rect.height; + + Scroller cachedScroller; + protected Scroller Scroller => cachedScroller ?? (cachedScroller = GetComponent()); + + RectTransform cachedCellRect; + RectTransform CellRectTransform => cachedCellRect ?? (cachedCellRect = CellPrefab.transform as RectTransform); + + protected virtual void Awake() + { + Context.CalculateScrollSize = () => + { + var interval = CellSize + spacing; + var reuseMargin = interval * reuseCellMarginCount; + var scrollSize = Scroller.ViewportSize + interval + reuseMargin * 2f; + return (scrollSize, reuseMargin); + }; + } + + protected virtual void Start() + { + AdjustCellIntervalAndScrollOffset(); + Scroller.OnValueChanged(OnScrollerValueChanged); + } + + void OnScrollerValueChanged(float p) + { + base.UpdatePosition(ScrollEnabled ? ToFancyScrollViewPosition(p) : 0f); + + if (Scroller.Scrollbar) + { + if (p > ItemsSource.Count - 1) + { + ShrinkScrollbar(p - (ItemsSource.Count - 1)); + } + else if (p < 0f) + { + ShrinkScrollbar(-p); + } + } + } + + void ShrinkScrollbar(float offset) + { + var scale = 1f - ToFancyScrollViewPosition(offset) / ViewportLength; + UpdateScrollbarSize(ViewportLength * scale / Mathf.Max(ItemsSource.Count, 1)); + } + + protected override void UpdateContents(IList items) + { + Debug.Assert(Context.CalculateScrollSize != null); + + AdjustCellIntervalAndScrollOffset(); + base.UpdateContents(items); + + Scroller.SetTotalCount(items.Count); + Scroller.Draggable = ScrollEnabled; + Scroller.ScrollSensitivity = ToScrollerPosition(ViewportLength); + Scroller.Position = ToScrollerPosition(currentPosition); + + if (Scroller.Scrollbar) + { + Scroller.Scrollbar.gameObject.SetActive(ScrollEnabled); + UpdateScrollbarSize(ViewportLength / Mathf.Max(ItemsSource.Count, 1)); + } + } + + protected new void UpdatePosition(float position) + { + UpdatePosition(position, Alignment.Center); + } + + protected virtual void UpdatePosition(float position, Alignment alignment) + { + Scroller.Position = ToScrollerPosition(position, alignment); + } + + public virtual void ScrollTo(int index, float duration, Alignment alignment = Alignment.Center, Action onComplete = null) + { + Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, onComplete); + } + + public virtual void ScrollTo(int index, float duration, Ease easing, Alignment alignment = Alignment.Center, Action onComplete = null) + { + Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, easing, onComplete); + } + + protected void UpdateScrollbarSize(float size) + { + Scroller.Scrollbar.size = ScrollEnabled ? Mathf.Clamp01(size) : 1f; + } + + protected virtual float ToFancyScrollViewPosition(float position) + { + return position + / Mathf.Max(ItemsSource.Count - 1, 1) + * MaxScrollPosition + - (paddingHead - spacing * 0.5f) / (CellSize + spacing); + } + + protected virtual float ToScrollerPosition(float position) + { + return (position + (paddingHead - spacing * 0.5f) / (CellSize + spacing)) + / MaxScrollPosition + * Mathf.Max(ItemsSource.Count - 1, 1); + } + + protected virtual float ToScrollerPosition(float position, Alignment alignment = Alignment.Center) + { + var offset = (ScrollLength - (1f + reuseCellMarginCount * 2f)) * GetAnchore(alignment); + return ToScrollerPosition(Mathf.Clamp(position - offset, 0f, MaxScrollPosition)); + } + + protected virtual float GetAnchore(Alignment alignment) + { + switch (alignment) + { + case Alignment.Head: return 0.0f; + case Alignment.Center: return 0.5f; + case Alignment.Tail: return 1.0f; + default: return GetAnchore(Alignment.Center); + } + } + + void AdjustCellIntervalAndScrollOffset() + { + var totalSize = Scroller.ViewportSize + (CellSize + spacing) * (1f + reuseCellMarginCount * 2f); + cellInterval = (CellSize + spacing) / totalSize; + scrollOffset = cellInterval * (1f + reuseCellMarginCount); + } + + protected virtual void OnValidate() + { + if (CellPrefab) + { + AdjustCellIntervalAndScrollOffset(); + } + + if (loop) + { + loop = false; + Debug.LogError("Loop is currently not supported in FancyScrollRect."); + } + + if (Scroller.SnapEnabled) + { + Scroller.SnapEnabled = false; + Debug.LogError("Snap is currently not supported in FancyScrollRect."); + } + + if (Scroller.MovementType == MovementType.Unrestricted) + { + Scroller.MovementType = MovementType.Elastic; + Debug.LogError("MovementType.Unrestricted is currently not supported in FancyScrollRect."); + } + } + } + + public abstract class FancyScrollRect : FancyScrollRect { } +} diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs.meta b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs.meta new file mode 100644 index 0000000..d5413e2 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66c8eb84fdbde4a4a8273b98227a282d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs new file mode 100644 index 0000000..6136b81 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs @@ -0,0 +1,28 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +namespace UnityEngine.UI.Extensions +{ + public abstract class FancyScrollRectCell : FancyScrollViewCell + where TContext : class, IFancyScrollRectContext, new() + { + public override void UpdatePosition(float position) + { + var (scrollSize, reuseMargin) = Context.CalculateScrollSize(); + + var unclampedPosition = (Mathf.Lerp(0f, scrollSize, position) - reuseMargin) / (scrollSize - reuseMargin * 2f); + + var start = 0.5f * scrollSize; + var end = -start; + + UpdatePosition(unclampedPosition, Mathf.Lerp(start, end, position)); + } + + protected virtual void UpdatePosition(float position, float viewportPosition) { } + } + + public abstract class FancyScrollRectCell : FancyScrollRectCell + { + public sealed override void SetupContext(FancyScrollRectContext context) => base.SetupContext(context); + } +} diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs.meta b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs.meta new file mode 100644 index 0000000..9786f4b --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 09f137a55810740eab42e24ef242dcfa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs new file mode 100644 index 0000000..2e5ea39 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs @@ -0,0 +1,12 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +using System; + +namespace UnityEngine.UI.Extensions +{ + public class FancyScrollRectContext : IFancyScrollRectContext + { + Func<(float ScrollSize, float ReuseMargin)> IFancyScrollRectContext.CalculateScrollSize { get; set; } + } +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs.meta b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs.meta new file mode 100644 index 0000000..a91bebd --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91383bd46cee541a7a03e08cfaa47c16 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs b/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs new file mode 100644 index 0000000..8ba33dd --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs @@ -0,0 +1,13 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +using System; + +namespace UnityEngine.UI.Extensions +{ + + public interface IFancyScrollRectContext + { + Func<(float ScrollSize, float ReuseMargin)> CalculateScrollSize { get; set; } + } +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs.meta b/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs.meta new file mode 100644 index 0000000..2ae49ed --- /dev/null +++ b/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e425de6354b6946c7a3b9f2c807b60fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/Scroller.meta b/Scripts/Layout/FancyScrollView/Scroller.meta new file mode 100644 index 0000000..0165cfd --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Scroller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 691a9f5ee4aec4112a01b9a4332cac51 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs b/Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs new file mode 100644 index 0000000..e6b1626 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs @@ -0,0 +1,214 @@ +// +// EasingCore - https://github.com/setchi/EasingCore +// +// The MIT License (MIT) +// +// Copyright (c) 2019 setchi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using UnityEngine; + +namespace UnityEngine.UI.Extensions.EasingCore +{ + public enum Ease + { + Linear, + InBack, + InBounce, + InCirc, + InCubic, + InElastic, + InExpo, + InQuad, + InQuart, + InQuint, + InSine, + OutBack, + OutBounce, + OutCirc, + OutCubic, + OutElastic, + OutExpo, + OutQuad, + OutQuart, + OutQuint, + OutSine, + InOutBack, + InOutBounce, + InOutCirc, + InOutCubic, + InOutElastic, + InOutExpo, + InOutQuad, + InOutQuart, + InOutQuint, + InOutSine, + } + + public static class EasingFunction + { + /// + /// Gets the easing function + /// + /// Ease type + /// Easing function + public static Func Get(Ease type) + { + switch (type) + { + case Ease.Linear: return linear; + case Ease.InBack: return inBack; + case Ease.InBounce: return inBounce; + case Ease.InCirc: return inCirc; + case Ease.InCubic: return inCubic; + case Ease.InElastic: return inElastic; + case Ease.InExpo: return inExpo; + case Ease.InQuad: return inQuad; + case Ease.InQuart: return inQuart; + case Ease.InQuint: return inQuint; + case Ease.InSine: return inSine; + case Ease.OutBack: return outBack; + case Ease.OutBounce: return outBounce; + case Ease.OutCirc: return outCirc; + case Ease.OutCubic: return outCubic; + case Ease.OutElastic: return outElastic; + case Ease.OutExpo: return outExpo; + case Ease.OutQuad: return outQuad; + case Ease.OutQuart: return outQuart; + case Ease.OutQuint: return outQuint; + case Ease.OutSine: return outSine; + case Ease.InOutBack: return inOutBack; + case Ease.InOutBounce: return inOutBounce; + case Ease.InOutCirc: return inOutCirc; + case Ease.InOutCubic: return inOutCubic; + case Ease.InOutElastic: return inOutElastic; + case Ease.InOutExpo: return inOutExpo; + case Ease.InOutQuad: return inOutQuad; + case Ease.InOutQuart: return inOutQuart; + case Ease.InOutQuint: return inOutQuint; + case Ease.InOutSine: return inOutSine; + default: return linear; + } + + float linear(float t) => t; + + float inBack(float t) => t * t * t - t * Mathf.Sin(t * Mathf.PI); + + float outBack(float t) => 1f - inBack(1f - t); + + float inOutBack(float t) => + t < 0.5f + ? 0.5f * inBack(2f * t) + : 0.5f * outBack(2f * t - 1f) + 0.5f; + + float inBounce(float t) => 1f - outBounce(1f - t); + + float outBounce(float t) => + t < 4f / 11.0f ? + (121f * t * t) / 16.0f : + t < 8f / 11.0f ? + (363f / 40.0f * t * t) - (99f / 10.0f * t) + 17f / 5.0f : + t < 9f / 10.0f ? + (4356f / 361.0f * t * t) - (35442f / 1805.0f * t) + 16061f / 1805.0f : + (54f / 5.0f * t * t) - (513f / 25.0f * t) + 268f / 25.0f; + + float inOutBounce(float t) => + t < 0.5f + ? 0.5f * inBounce(2f * t) + : 0.5f * outBounce(2f * t - 1f) + 0.5f; + + float inCirc(float t) => 1f - Mathf.Sqrt(1f - (t * t)); + + float outCirc(float t) => Mathf.Sqrt((2f - t) * t); + + float inOutCirc(float t) => + t < 0.5f + ? 0.5f * (1 - Mathf.Sqrt(1f - 4f * (t * t))) + : 0.5f * (Mathf.Sqrt(-((2f * t) - 3f) * ((2f * t) - 1f)) + 1f); + + float inCubic(float t) => t * t * t; + + float outCubic(float t) => inCubic(t - 1f) + 1f; + + float inOutCubic(float t) => + t < 0.5f + ? 4f * t * t * t + : 0.5f * inCubic(2f * t - 2f) + 1f; + + float inElastic(float t) => Mathf.Sin(13f * (Mathf.PI * 0.5f) * t) * Mathf.Pow(2f, 10f * (t - 1f)); + + float outElastic(float t) => Mathf.Sin(-13f * (Mathf.PI * 0.5f) * (t + 1)) * Mathf.Pow(2f, -10f * t) + 1f; + + float inOutElastic(float t) => + t < 0.5f + ? 0.5f * Mathf.Sin(13f * (Mathf.PI * 0.5f) * (2f * t)) * Mathf.Pow(2f, 10f * ((2f * t) - 1f)) + : 0.5f * (Mathf.Sin(-13f * (Mathf.PI * 0.5f) * ((2f * t - 1f) + 1f)) * Mathf.Pow(2f, -10f * (2f * t - 1f)) + 2f); + + float inExpo(float t) => Mathf.Approximately(0.0f, t) ? t : Mathf.Pow(2f, 10f * (t - 1f)); + + float outExpo(float t) => Mathf.Approximately(1.0f, t) ? t : 1f - Mathf.Pow(2f, -10f * t); + + float inOutExpo(float v) => + Mathf.Approximately(0.0f, v) || Mathf.Approximately(1.0f, v) + ? v + : v < 0.5f + ? 0.5f * Mathf.Pow(2f, (20f * v) - 10f) + : -0.5f * Mathf.Pow(2f, (-20f * v) + 10f) + 1f; + + float inQuad(float t) => t * t; + + float outQuad(float t) => -t * (t - 2f); + + float inOutQuad(float t) => + t < 0.5f + ? 2f * t * t + : -2f * t * t + 4f * t - 1f; + + float inQuart(float t) => t * t * t * t; + + float outQuart(float t) + { + var u = t - 1f; + return u * u * u * (1f - t) + 1f; + } + + float inOutQuart(float t) => + t < 0.5f + ? 8f * inQuart(t) + : -8f * inQuart(t - 1f) + 1f; + + float inQuint(float t) => t * t * t * t * t; + + float outQuint(float t) => inQuint(t - 1f) + 1f; + + float inOutQuint(float t) => + t < 0.5f + ? 16f * inQuint(t) + : 0.5f * inQuint(2f * t - 2f) + 1f; + + float inSine(float t) => Mathf.Sin((t - 1f) * (Mathf.PI * 0.5f)) + 1f; + + float outSine(float t) => Mathf.Sin(t * (Mathf.PI * 0.5f)); + + float inOutSine(float t) => 0.5f * (1f - Mathf.Cos(t * Mathf.PI)); + } + } +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/EasingCore.cs.meta b/Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs.meta similarity index 83% rename from Scripts/Layout/FancyScrollView/EasingCore.cs.meta rename to Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs.meta index 7cdac42..0a1d19c 100644 --- a/Scripts/Layout/FancyScrollView/EasingCore.cs.meta +++ b/Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b5e2a916ad19c74468262e656243bc6f +guid: 3b2a35f9ff1c8487582b74620e380486 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Scripts/Layout/FancyScrollView/Scroller/MovementDirection.cs b/Scripts/Layout/FancyScrollView/Scroller/MovementDirection.cs new file mode 100644 index 0000000..5a41163 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Scroller/MovementDirection.cs @@ -0,0 +1,13 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +namespace UnityEngine.UI.Extensions +{ + public enum MovementDirection + { + Left, + Right, + Up, + Down, + } +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/Scroller/MovementDirection.cs.meta b/Scripts/Layout/FancyScrollView/Scroller/MovementDirection.cs.meta new file mode 100644 index 0000000..e13bcff --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Scroller/MovementDirection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 11769de5e972543c7a4132ceff94a5c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/Scroller/MovementType.cs b/Scripts/Layout/FancyScrollView/Scroller/MovementType.cs new file mode 100644 index 0000000..cb8c210 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Scroller/MovementType.cs @@ -0,0 +1,12 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +namespace UnityEngine.UI.Extensions +{ + public enum MovementType + { + Unrestricted = ScrollRect.MovementType.Unrestricted, + Elastic = ScrollRect.MovementType.Elastic, + Clamped = ScrollRect.MovementType.Clamped + } +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/Scroller/MovementType.cs.meta b/Scripts/Layout/FancyScrollView/Scroller/MovementType.cs.meta new file mode 100644 index 0000000..9bd990b --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Scroller/MovementType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 585de8f4f272547c89041f94f2ad92b8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/Scroller/ScrollDirection.cs b/Scripts/Layout/FancyScrollView/Scroller/ScrollDirection.cs new file mode 100644 index 0000000..81a14ff --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Scroller/ScrollDirection.cs @@ -0,0 +1,11 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +namespace UnityEngine.UI.Extensions +{ + public enum ScrollDirection + { + Vertical, + Horizontal, + } +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/Scroller/ScrollDirection.cs.meta b/Scripts/Layout/FancyScrollView/Scroller/ScrollDirection.cs.meta new file mode 100644 index 0000000..a4f7b43 --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Scroller/ScrollDirection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3392a30c085c642139c92e94ec0d6309 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/FancyScrollView/Scroller.cs b/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs similarity index 61% rename from Scripts/Layout/FancyScrollView/Scroller.cs rename to Scripts/Layout/FancyScrollView/Scroller/Scroller.cs index 1cdffca..31a2801 100644 --- a/Scripts/Layout/FancyScrollView/Scroller.cs +++ b/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs @@ -10,47 +10,83 @@ namespace UnityEngine.UI.Extensions public class Scroller : UIBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler { [SerializeField] RectTransform viewport = default; - [SerializeField] ScrollDirection directionOfRecognize = ScrollDirection.Vertical; + [SerializeField] ScrollDirection scrollDirection = ScrollDirection.Vertical; [SerializeField] MovementType movementType = MovementType.Elastic; [SerializeField] float elasticity = 0.1f; [SerializeField] float scrollSensitivity = 1f; [SerializeField] bool inertia = true; [SerializeField] float decelerationRate = 0.03f; - [SerializeField] Snap snap = new Snap { + [SerializeField] + Snap snap = new Snap + { Enable = true, VelocityThreshold = 0.5f, Duration = 0.3f, Easing = Ease.InOutCubic }; + [SerializeField] bool draggable = true; + [SerializeField] Scrollbar scrollbar = default; + + public ScrollDirection ScrollDirection => scrollDirection; + + public MovementType MovementType + { + get => movementType; + set => movementType = value; + } + + public float ViewportSize => scrollDirection == ScrollDirection.Horizontal + ? viewport.rect.size.x + : viewport.rect.size.y; + + public bool SnapEnabled + { + get => snap.Enable; + set => snap.Enable = value; + } + + public float ScrollSensitivity + { + get => scrollSensitivity; + set => scrollSensitivity = value; + } + + public bool Draggable + { + get => draggable; + set => draggable = value; + } + + public Scrollbar Scrollbar => scrollbar; + + public float Position + { + get => currentPosition; + set + { + autoScrollState.Reset(); + velocity = 0f; + dragging = false; + + UpdatePosition(value); + } + } readonly AutoScrollState autoScrollState = new AutoScrollState(); Action onValueChanged; Action onSelectionChanged; - Vector2 pointerStartLocalPosition; - float dragStartScrollPosition; - float prevScrollPosition; - float currentScrollPosition; + Vector2 beginDragPointerPosition; + float scrollStartPosition; + float prevPosition; + float currentPosition; int totalCount; bool dragging; float velocity; - enum ScrollDirection - { - Vertical, - Horizontal, - } - - public enum MovementType - { - Unrestricted = ScrollRect.MovementType.Unrestricted, - Elastic = ScrollRect.MovementType.Elastic, - Clamped = ScrollRect.MovementType.Clamped - } - [Serializable] class Snap { @@ -69,7 +105,7 @@ namespace UnityEngine.UI.Extensions public float Duration; public Func EasingFunction; public float StartTime; - public float EndScrollPosition; + public float EndPosition; public Action OnComplete; @@ -80,7 +116,7 @@ namespace UnityEngine.UI.Extensions Duration = 0f; StartTime = 0f; EasingFunction = DefaultEasingFunction; - EndScrollPosition = 0f; + EndPosition = 0f; OnComplete = null; } @@ -91,21 +127,32 @@ namespace UnityEngine.UI.Extensions } } + protected override void Start() + { + base.Start(); + + if (scrollbar) + { + scrollbar.onValueChanged.AddListener(x => UpdatePosition(x * (totalCount - 1f), false)); + } + } + public void OnValueChanged(Action callback) => onValueChanged = callback; public void OnSelectionChanged(Action callback) => onSelectionChanged = callback; public void SetTotalCount(int totalCount) => this.totalCount = totalCount; - public void ScrollTo(int index, float duration, Action onComplete = null) => ScrollTo(index, duration, Ease.OutCubic, onComplete); + public void ScrollTo(float position, float duration, Action onComplete = null) => ScrollTo(position, duration, Ease.OutCubic, onComplete); - public void ScrollTo(int index, float duration, Ease easing, Action onComplete = null) => ScrollTo(index, duration, EasingFunction.Get(easing), onComplete); + public void ScrollTo(float position, float duration, Ease easing, Action onComplete = null) => ScrollTo(position, duration, EasingFunction.Get(easing), onComplete); - public void ScrollTo(int index, float duration, Func easingFunction, Action onComplete = null) + public void ScrollTo(float position, float duration, Func easingFunction, Action onComplete = null) { if (duration <= 0f) { - JumpTo(index); + Position = CircularPosition(position, totalCount); + onComplete?.Invoke(); return; } @@ -114,55 +161,64 @@ namespace UnityEngine.UI.Extensions autoScrollState.Duration = duration; autoScrollState.EasingFunction = easingFunction ?? DefaultEasingFunction; autoScrollState.StartTime = Time.unscaledTime; - autoScrollState.EndScrollPosition = CalculateDestinationIndex(index); + autoScrollState.EndPosition = currentPosition + CalculateMovementAmount(currentPosition, position); autoScrollState.OnComplete = onComplete; velocity = 0f; - dragStartScrollPosition = currentScrollPosition; + scrollStartPosition = currentPosition; - UpdateSelection(Mathf.RoundToInt(CircularPosition(autoScrollState.EndScrollPosition, totalCount))); + UpdateSelection(Mathf.RoundToInt(CircularPosition(autoScrollState.EndPosition, totalCount))); } public void JumpTo(int index) { + if (index < 0 || index > totalCount - 1) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + autoScrollState.Reset(); velocity = 0f; dragging = false; - index = CalculateDestinationIndex(index); - UpdateSelection(index); UpdatePosition(index); } + public MovementDirection GetMovementDirection(int sourceIndex, int destIndex) + { + var movementAmount = CalculateMovementAmount(sourceIndex, destIndex); + return scrollDirection == ScrollDirection.Horizontal + ? movementAmount > 0 + ? MovementDirection.Left + : MovementDirection.Right + : movementAmount > 0 + ? MovementDirection.Up + : MovementDirection.Down; + } + void IBeginDragHandler.OnBeginDrag(PointerEventData eventData) { - if (eventData.button != PointerEventData.InputButton.Left) + if (!draggable || eventData.button != PointerEventData.InputButton.Left) { return; } - pointerStartLocalPosition = Vector2.zero; RectTransformUtility.ScreenPointToLocalPointInRectangle( viewport, eventData.position, eventData.pressEventCamera, - out pointerStartLocalPosition); + out beginDragPointerPosition); - dragStartScrollPosition = currentScrollPosition; + scrollStartPosition = currentPosition; dragging = true; autoScrollState.Reset(); } void IDragHandler.OnDrag(PointerEventData eventData) { - if (eventData.button != PointerEventData.InputButton.Left) - { - return; - } - - if (!dragging) + if (!draggable || eventData.button != PointerEventData.InputButton.Left || !dragging) { return; } @@ -171,16 +227,16 @@ namespace UnityEngine.UI.Extensions viewport, eventData.position, eventData.pressEventCamera, - out var localCursor)) + out var dragPointerPosition)) { return; } - var pointerDelta = localCursor - pointerStartLocalPosition; - var position = (directionOfRecognize == ScrollDirection.Horizontal ? -pointerDelta.x : pointerDelta.y) + var pointerDelta = dragPointerPosition - beginDragPointerPosition; + var position = (scrollDirection == ScrollDirection.Horizontal ? -pointerDelta.x : pointerDelta.y) / ViewportSize * scrollSensitivity - + dragStartScrollPosition; + + scrollStartPosition; var offset = CalculateOffset(position); position += offset; @@ -198,7 +254,7 @@ namespace UnityEngine.UI.Extensions void IEndDragHandler.OnEndDrag(PointerEventData eventData) { - if (eventData.button != PointerEventData.InputButton.Left) + if (!draggable || eventData.button != PointerEventData.InputButton.Left) { return; } @@ -206,10 +262,6 @@ namespace UnityEngine.UI.Extensions dragging = false; } - float ViewportSize => directionOfRecognize == ScrollDirection.Horizontal - ? viewport.rect.size.x - : viewport.rect.size.y; - float CalculateOffset(float position) { if (movementType == MovementType.Unrestricted) @@ -230,10 +282,14 @@ namespace UnityEngine.UI.Extensions return 0f; } - void UpdatePosition(float position) + void UpdatePosition(float position, bool updateScrollbar = true) { - currentScrollPosition = position; - onValueChanged?.Invoke(currentScrollPosition); + onValueChanged?.Invoke(currentPosition = position); + + if (scrollbar && updateScrollbar) + { + scrollbar.value = Mathf.Clamp01(position / Mathf.Max(totalCount - 1f, 1e-4f)); + } } void UpdateSelection(int index) => onSelectionChanged?.Invoke(index); @@ -244,7 +300,7 @@ namespace UnityEngine.UI.Extensions void Update() { var deltaTime = Time.unscaledDeltaTime; - var offset = CalculateOffset(currentScrollPosition); + var offset = CalculateOffset(currentPosition); if (autoScrollState.Enable) { @@ -252,7 +308,7 @@ namespace UnityEngine.UI.Extensions if (autoScrollState.Elastic) { - position = Mathf.SmoothDamp(currentScrollPosition, currentScrollPosition + offset, ref velocity, + position = Mathf.SmoothDamp(currentPosition, currentPosition + offset, ref velocity, elasticity, Mathf.Infinity, deltaTime); if (Mathf.Abs(velocity) < 0.01f) @@ -265,8 +321,8 @@ namespace UnityEngine.UI.Extensions else { var alpha = Mathf.Clamp01((Time.unscaledTime - autoScrollState.StartTime) / - Mathf.Max(autoScrollState.Duration, float.Epsilon)); - position = Mathf.LerpUnclamped(dragStartScrollPosition, autoScrollState.EndScrollPosition, + Mathf.Max(autoScrollState.Duration, float.Epsilon)); + position = Mathf.LerpUnclamped(scrollStartPosition, autoScrollState.EndPosition, autoScrollState.EasingFunction(alpha)); if (Mathf.Approximately(alpha, 1f)) @@ -279,7 +335,7 @@ namespace UnityEngine.UI.Extensions } else if (!dragging && (!Mathf.Approximately(offset, 0f) || !Mathf.Approximately(velocity, 0f))) { - var position = currentScrollPosition; + var position = currentPosition; if (movementType == MovementType.Elastic && !Mathf.Approximately(offset, 0f)) { @@ -302,7 +358,7 @@ namespace UnityEngine.UI.Extensions if (snap.Enable && Mathf.Abs(velocity) < snap.VelocityThreshold) { - ScrollTo(Mathf.RoundToInt(currentScrollPosition), snap.Duration, snap.Easing); + ScrollTo(Mathf.RoundToInt(currentPosition), snap.Duration, snap.Easing); } } else @@ -330,30 +386,30 @@ namespace UnityEngine.UI.Extensions if (!autoScrollState.Enable && dragging && inertia) { - var newVelocity = (currentScrollPosition - prevScrollPosition) / deltaTime; + var newVelocity = (currentPosition - prevPosition) / deltaTime; velocity = Mathf.Lerp(velocity, newVelocity, deltaTime * 10f); } - prevScrollPosition = currentScrollPosition; + prevPosition = currentPosition; } - int CalculateDestinationIndex(int index) => movementType == MovementType.Unrestricted - ? CalculateClosestIndex(index) - : Mathf.Clamp(index, 0, totalCount - 1); - - int CalculateClosestIndex(int index) + float CalculateMovementAmount(float sourcePosition, float destPosition) { - var diff = CircularPosition(index, totalCount) - - CircularPosition(currentScrollPosition, totalCount); - - if (Mathf.Abs(diff) > totalCount * 0.5f) + if (movementType != MovementType.Unrestricted) { - diff = Mathf.Sign(-diff) * (totalCount - Mathf.Abs(diff)); + return Mathf.Clamp(destPosition, 0, totalCount - 1) - sourcePosition; } - return Mathf.RoundToInt(diff + currentScrollPosition); + var movementAmount = CircularPosition(destPosition, totalCount) - CircularPosition(sourcePosition, totalCount); + + if (Mathf.Abs(movementAmount) > totalCount * 0.5f) + { + movementAmount = Mathf.Sign(-movementAmount) * (totalCount - Mathf.Abs(movementAmount)); + } + + return movementAmount; } float CircularPosition(float p, int size) => size < 1 ? 0 : p < 0 ? size - 1 + (p + 1) % size : p % size; } -} +} \ No newline at end of file diff --git a/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs.meta b/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs.meta new file mode 100644 index 0000000..731287d --- /dev/null +++ b/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9f0e995f494626a4f878eedbded37c8d +timeCreated: 1487408581 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: