From fca370b906de36e284674cf225232926dede5ea2 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 24 Nov 2017 09:00:49 -0800 Subject: [PATCH] Created a Content Scroll Snap that snaps onto the children of the content no matter their sizing or spacing --- Editor/UIExtensionsMenuOptions.cs | 115 +++- .../ContentSnapScrollExample.unity | Bin 0 -> 87632 bytes .../ContentSnapScrollExample.unity.meta | 8 + Scripts/Layout/ContentScrollSnapHorizontal.cs | 612 ++++++++++++++++++ .../ContentScrollSnapHorizontal.cs.meta | 12 + 5 files changed, 743 insertions(+), 4 deletions(-) create mode 100644 Examples/HSS-VSS-ScrollSnap/ContentSnapScrollExample.unity create mode 100644 Examples/HSS-VSS-ScrollSnap/ContentSnapScrollExample.unity.meta create mode 100644 Scripts/Layout/ContentScrollSnapHorizontal.cs create mode 100644 Scripts/Layout/ContentScrollSnapHorizontal.cs.meta diff --git a/Editor/UIExtensionsMenuOptions.cs b/Editor/UIExtensionsMenuOptions.cs index 57af6ec..6bba15a 100644 --- a/Editor/UIExtensionsMenuOptions.cs +++ b/Editor/UIExtensionsMenuOptions.cs @@ -466,12 +466,119 @@ namespace UnityEditor.UI { FixedScrollSnapBase(menuCommand, "Scroll Snap Vertical Multiple", ScrollSnap.ScrollDirection.Vertical, 3, 15, new Vector2(100, 100)); } - #endregion + #endregion - #endregion + #region ContentScrollSnapHorizontal + [MenuItem("GameObject/UI/Extensions/Content Scroll Snap Horizontal", false)] + static public void AddContentScrollSnapHorizontal(MenuCommand menuCommand) + { + GameObject contentScrollSnapRoot = CreateUIElementRoot("Content Scroll Snap Horizontal", menuCommand, s_ThickGUIElementSize); - #region UIVertical Scroller - [MenuItem("GameObject/UI/Extensions/UI Vertical Scroller", false)] + GameObject childContent = CreateUIObject("Content", contentScrollSnapRoot); + + GameObject childPage01 = CreateUIObject("Position 1", childContent); + + GameObject childPage02 = CreateUIObject("Position 2", childContent); + + GameObject childPage03 = CreateUIObject("Position 3", childContent); + + GameObject childPage04 = CreateUIObject("Position 4", childContent); + + GameObject childPage05 = CreateUIObject("Position 5", childContent); + + //setup root + RectTransform contentScrollSnapRectTransform = ((RectTransform)contentScrollSnapRoot.transform); + contentScrollSnapRectTransform.anchorMin = new Vector2(0.5f, 0.5f); + contentScrollSnapRectTransform.anchorMax = new Vector2(0.5f, 0.5f); + contentScrollSnapRectTransform.anchoredPosition = Vector2.zero; + contentScrollSnapRectTransform.sizeDelta = new Vector2(100, 200); + + Image image = contentScrollSnapRoot.AddComponent(); + image.sprite = null; + image.color = new Color(1, 0, 0, .5f); + + ScrollRect sr = contentScrollSnapRoot.AddComponent(); + sr.vertical = false; + sr.horizontal = true; + + //setup content container + RectTransform contentTransform = ((RectTransform)childContent.transform); + contentTransform.anchorMin = new Vector2(.5f, .5f); + contentTransform.anchorMax = new Vector2(.5f, .5f); + contentTransform.pivot = new Vector2(.5f, .5f); + contentTransform.sizeDelta = new Vector2(200, 300); + + Image contentImage = childContent.AddComponent(); + contentImage.sprite = null; + contentImage.color = new Color(0, 0, 1, .5f); + + sr.content = contentTransform; + + //setup child 1 + RectTransform childPage01Transform = (RectTransform)childPage01.transform; + childPage01Transform.anchorMin = new Vector2(0, 1); + childPage01Transform.anchorMax = new Vector2(0, 1); + childPage01Transform.pivot = new Vector2(0, 1); + childPage01Transform.anchoredPosition = new Vector2(0, -125); + + Image childPage01Image = childPage01.AddComponent(); + childPage01Image.sprite = null; + childPage01Image.color = Color.white; + + //setup child 2 + RectTransform childPage02Transform = (RectTransform)childPage02.transform; + childPage02Transform.anchorMin = new Vector2(0, 1); + childPage02Transform.anchorMax = new Vector2(0, 1); + childPage02Transform.pivot = new Vector2(0, 1); + childPage02Transform.anchoredPosition = new Vector2(175, -150); + + Image childPage02Image = childPage02.AddComponent(); + childPage02Image.sprite = null; + childPage02Image.color = Color.white; + + //setup child 3 + RectTransform childPage03Transform = (RectTransform)childPage03.transform; + childPage03Transform.anchorMin = new Vector2(0, 1); + childPage03Transform.anchorMax = new Vector2(0, 1); + childPage03Transform.pivot = new Vector2(0, 1); + childPage03Transform.anchoredPosition = new Vector2(315, -125); + childPage03Transform.sizeDelta = new Vector2(50, 100); + + Image childPage03Image = childPage03.AddComponent(); + childPage03Image.sprite = null; + childPage03Image.color = Color.white; + + //setup child 4 + RectTransform childPage04Transform = (RectTransform)childPage04.transform; + childPage04Transform.anchorMin = new Vector2(0, 1); + childPage04Transform.anchorMax = new Vector2(0, 1); + childPage04Transform.pivot = new Vector2(0, 1); + childPage04Transform.anchoredPosition = new Vector2(490, -110); + + Image childPage04Image = childPage04.AddComponent(); + childPage04Image.sprite = null; + childPage04Image.color = Color.white; + + //setup child 5 + RectTransform childPage05Transform = (RectTransform)childPage05.transform; + childPage05Transform.anchorMin = new Vector2(0, 1); + childPage05Transform.anchorMax = new Vector2(0, 1); + childPage05Transform.pivot = new Vector2(0, 1); + childPage05Transform.anchoredPosition = new Vector2(630, -180); + + Image childPage05Image = childPage05.AddComponent(); + childPage05Image.sprite = null; + childPage05Image.color = Color.white; + + //add scroll snap after we've added the content & items + contentScrollSnapRoot.AddComponent(); + } + #endregion + + #endregion + + #region UIVertical Scroller + [MenuItem("GameObject/UI/Extensions/UI Vertical Scroller", false)] static public void AddUIVerticallScroller(MenuCommand menuCommand) { GameObject uiVerticalScrollerRoot = CreateUIElementRoot("UI Vertical Scroller", menuCommand, s_ThickGUIElementSize); diff --git a/Examples/HSS-VSS-ScrollSnap/ContentSnapScrollExample.unity b/Examples/HSS-VSS-ScrollSnap/ContentSnapScrollExample.unity new file mode 100644 index 0000000000000000000000000000000000000000..3500e65cd5fae142e5c42e90aa310977630fd808 GIT binary patch literal 87632 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 literal 0 HcmV?d00001 diff --git a/Examples/HSS-VSS-ScrollSnap/ContentSnapScrollExample.unity.meta b/Examples/HSS-VSS-ScrollSnap/ContentSnapScrollExample.unity.meta new file mode 100644 index 0000000..b377fbc --- /dev/null +++ b/Examples/HSS-VSS-ScrollSnap/ContentSnapScrollExample.unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0749bfbb194c58948abd4f5d4c46b6f6 +timeCreated: 1511210771 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Layout/ContentScrollSnapHorizontal.cs b/Scripts/Layout/ContentScrollSnapHorizontal.cs new file mode 100644 index 0000000..c02f57d --- /dev/null +++ b/Scripts/Layout/ContentScrollSnapHorizontal.cs @@ -0,0 +1,612 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine.EventSystems; +using UnityEngine.Events; +using System; + +namespace UnityEngine.UI.Extensions +{ + [ExecuteInEditMode] + [RequireComponent(typeof(ScrollRect))] + [AddComponentMenu("UI/Extensions/ContentSnapScrollHorizontal")] + public class ContentScrollSnapHorizontal : MonoBehaviour, IBeginDragHandler, IEndDragHandler + { + + [Serializable] + public class StartMovementEvent : UnityEvent { } + [Serializable] + public class CurrentItemChangeEvent : UnityEvent { } + [Serializable] + public class FoundItemToSnapToEvent : UnityEvent { } + [Serializable] + public class SnappedToItemEvent : UnityEvent { } + + public bool ignoreInactiveItems = true; + public MoveInfo startInfo = new MoveInfo(MoveInfo.IndexType.positionIndex, 0); + public GameObject prevButton; + public GameObject nextButton; + public GameObject pagination; + [Tooltip("The velocity below which the scroll rect will start to snap")] + public int snappingVelocityThreshold = 50; + + [Header("Paging Info")] + [Tooltip("Should the pagination & buttons jump or lerp to the items")] + public bool jumpToItem = false; + [Tooltip("The time it will take for the pagination or buttons to move between items")] + public float lerpTime = .3f; + + [Header("Events")] + [SerializeField] + [Tooltip("Event is triggered whenever the scroll rect starts to move, even when triggered programatically")] + private StartMovementEvent m_StartMovementEvent = new StartMovementEvent(); + public StartMovementEvent startMovementEvent + { + get + { + return m_StartMovementEvent; + } + set + { + m_StartMovementEvent = value; + } + } + + [SerializeField] + [Tooltip("Event is triggered whenever the closest item to the center of the scrollrect changes")] + private CurrentItemChangeEvent m_CurrentItemChangeEvent = new CurrentItemChangeEvent(); + public CurrentItemChangeEvent currentItemChangeEvent + { + get + { + return m_CurrentItemChangeEvent; + } + set + { + m_CurrentItemChangeEvent = value; + } + } + + [SerializeField] + [Tooltip("Event is triggered when the ContentSnapScroll decides which item it is going to snap to. Returns the index of the closest position.")] + private FoundItemToSnapToEvent m_FoundItemToSnapToEvent = new FoundItemToSnapToEvent(); + public FoundItemToSnapToEvent foundItemToSnapToEvent + { + get + { + return m_FoundItemToSnapToEvent; + } + set + { + m_FoundItemToSnapToEvent = value; + } + } + + [SerializeField] + [Tooltip("Event is triggered when we finally settle on an element. Returns the index of the item's position.")] + private SnappedToItemEvent m_SnappedToItemEvent = new SnappedToItemEvent(); + public SnappedToItemEvent snappedToItemEvent + { + get + { + return m_SnappedToItemEvent; + } + set + { + m_SnappedToItemEvent = value; + } + } + + private ScrollRect scrollRect; + private RectTransform scrollRectTransform; + private RectTransform contentTransform; + private List contentPositions; + private Vector3 lerpTarget; + private float totalScrollableWidth; + private DrivenRectTransformTracker tracker; + private bool lerp; + private bool pointerDown; + private float mLerpTime; + private int _closestItem; + private bool lerpToContentRunning; + private bool mSliding; + private bool mLerping; + private bool contentIsHorizonalLayoutGroup + { + get + { + return contentTransform.GetComponent() != null; + } + } + + #region Public Info + /// + /// Returns if the SnapScroll is moving + /// + public bool moving + { + get + { + return sliding || lerping; + } + } + + /// + /// Returns if the SnapScroll is moving because of a touch + /// + public bool sliding + { + get + { + return mSliding; + } + } + /// + /// Returns if the SnapScroll is moving programmatically + /// + public bool lerping + { + get + { + return mLerping; + } + } + + /// + /// Returns the closest item's index + /// *Note this is zero based, and based on position not child order + /// + public int closestItemIndex + { + get + { + return contentPositions.IndexOf(FindClosestFrom(contentTransform.localPosition)); + } + } + /// + /// Returns the lerpTarget's index + /// *Note this is zero-based, and based on position not child order + /// + public int lerpTargetIndex + { + get + { + return contentPositions.IndexOf(lerpTarget); + } + } + #endregion + + #region Setup + private void Awake() + { + scrollRect = GetComponent(); + scrollRectTransform = (RectTransform) scrollRect.transform; + contentTransform = scrollRect.content; + + if (nextButton) + nextButton.GetComponent