Updated ScrollSnap with fixes from author

--HG--
branch : develop_4.6
release
Simon (darkside) Jackson 2015-10-17 16:16:23 +01:00
parent fee79a9549
commit b6f4e5a144
1 changed files with 199 additions and 136 deletions

View File

@ -9,23 +9,23 @@
/// if you dont wish to use this auto resize turn of autoLayoutItems /// if you dont wish to use this auto resize turn of autoLayoutItems
/// - fixed current page made it independant from pivot /// - fixed current page made it independant from pivot
/// - replaced pagination with delegate function /// - replaced pagination with delegate function
using System; using System;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[ExecuteInEditMode] [ExecuteInEditMode]
[RequireComponent(typeof(ScrollRect))] [RequireComponent(typeof(ScrollRect))]
[AddComponentMenu("UI/Extensions/Scroll Snap")] [AddComponentMenu("UI/Extensions/Scroll Snap")]
public class ScrollSnap : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler public class ScrollSnap : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
{ {
// needed becouse of reversed behavior of axis Y compared to X // needed becouse of reversed behavior of axis Y compared to X
// (positions of children lower in children list in horizontal directions grows when in vertical it gets smaller) // (positions of children lower in children list in horizontal directions grows when in vertical it gets smaller)
public enum ScrollDirection { public enum ScrollDirection
{
Horizontal, Horizontal,
Vertical Vertical
}; }
public delegate void PageSnapChange(int page); public delegate void PageSnapChange(int page);
@ -34,11 +34,12 @@ namespace UnityEngine.UI.Extensions
public ScrollDirection direction = ScrollDirection.Horizontal; public ScrollDirection direction = ScrollDirection.Horizontal;
protected ScrollRect scrollRect; protected ScrollRect scrollRect;
protected RectTransform scrollRectTransform;
protected Transform listContainerTransform;
protected RectTransform rectTransform;
protected int items = 0; protected RectTransform scrollRectTransform;
protected Transform listContainerTransform;
protected RectTransform rectTransform;
int pages; int pages;
@ -46,22 +47,31 @@ namespace UnityEngine.UI.Extensions
// anchor points to lerp to to see child on certain indexes // anchor points to lerp to to see child on certain indexes
protected Vector3[] pageAnchorPositions; protected Vector3[] pageAnchorPositions;
protected Vector3 lerpTarget; protected Vector3 lerpTarget;
protected bool lerp; protected bool lerp;
// item list related // item list related
protected float listContainerMinPosition; protected float listContainerMinPosition;
protected float listContainerMaxPosition; protected float listContainerMaxPosition;
protected float listContainerSize; protected float listContainerSize;
protected RectTransform listContainerRectTransform; protected RectTransform listContainerRectTransform;
protected Vector2 listContainerCachedSize; protected Vector2 listContainerCachedSize;
protected float itemSize; protected float itemSize;
protected int itemsCount = 0;
[Tooltip("Button to go to the next page. (optional)")] [Tooltip("Button to go to the next page. (optional)")]
public GameObject nextButton; public Button nextButton;
[Tooltip("Button to go to the previous page. (optional)")]
public GameObject prevButton; [Tooltip("Button to go to the previous page. (optional)")]
public Button prevButton;
[Tooltip("Number of items visible in one page of scroll frame.")] [Tooltip("Number of items visible in one page of scroll frame.")]
[RangeAttribute(1,100)] [RangeAttribute(1,100)]
@ -72,74 +82,97 @@ namespace UnityEngine.UI.Extensions
[Tooltip("If you wish to update scrollbar numberOfSteps to number of active children on list.")] [Tooltip("If you wish to update scrollbar numberOfSteps to number of active children on list.")]
public bool linkScrolbarSteps = false; public bool linkScrolbarSteps = false;
[Tooltip("If you wish to update scrollrect sensitivity to size of list element.")]
public bool linkScrolrectScrollSensitivity = false;
public Boolean useFastSwipe = true; public Boolean useFastSwipe = true;
public int fastSwipeThreshold = 100;
public int fastSwipeThreshold = 100;
// drag related // drag related
protected bool startDrag = true; protected bool startDrag = true;
protected Vector3 positionOnDragStart = new Vector3(); protected Vector3 positionOnDragStart = new Vector3();
protected int pageOnDragStart; protected int pageOnDragStart;
protected bool fastSwipeTimer = false; protected bool fastSwipeTimer = false;
protected int fastSwipeCounter = 0; protected int fastSwipeCounter = 0;
protected int fastSwipeTarget = 10; protected int fastSwipeTarget = 10;
// Use this for initialization // Use this for initialization
void Start() void Awake()
{ {
lerp = false; lerp = false;
scrollRect = gameObject.GetComponent<ScrollRect> (); scrollRect = gameObject.GetComponent<ScrollRect>();
scrollRectTransform = gameObject.GetComponent<RectTransform> (); scrollRectTransform = gameObject.GetComponent<RectTransform>();
listContainerTransform = scrollRect.content; listContainerTransform = scrollRect.content;
listContainerRectTransform = listContainerTransform.GetComponent<RectTransform> (); listContainerRectTransform = listContainerTransform.GetComponent<RectTransform>();
rectTransform = listContainerTransform.gameObject.GetComponent<RectTransform> (); rectTransform = listContainerTransform.gameObject.GetComponent<RectTransform>();
UpdateListItemsSize(); UpdateListItemsSize();
UpdateListItemPositions(); UpdateListItemPositions();
ChangePage (CurrentPage ()); ChangePage(CurrentPage());
if (nextButton) if (nextButton)
{ {
nextButton.GetComponent<Button> ().onClick.AddListener (() => { NextScreen (); }); nextButton.GetComponent<Button>().onClick.AddListener(() => {
NextScreen(); });
} }
if (prevButton) if (prevButton)
{ {
prevButton.GetComponent<Button> ().onClick.AddListener (() => { PreviousScreen (); }); prevButton.GetComponent<Button>().onClick.AddListener(() => {
PreviousScreen(); });
} }
} }
void Start()
{
Awake();
}
public void UpdateListItemsSize() public void UpdateListItemsSize()
{ {
float size = 0; float size = 0;
float currentSize = 0;
if (direction == ScrollSnap.ScrollDirection.Horizontal) if (direction == ScrollSnap.ScrollDirection.Horizontal)
{ {
size = scrollRectTransform.rect.width / itemsVisibleAtOnce; size = scrollRectTransform.rect.width / itemsVisibleAtOnce;
currentSize = listContainerRectTransform.rect.width / itemsCount;
} }
else else
{ {
size = scrollRectTransform.rect.height / itemsVisibleAtOnce; size = scrollRectTransform.rect.height / itemsVisibleAtOnce;
currentSize = listContainerRectTransform.rect.height / itemsCount;
} }
itemSize = size; itemSize = size;
if (autoLayoutItems && size != itemSize) if (linkScrolrectScrollSensitivity)
{
scrollRect.scrollSensitivity = itemSize;
}
if (autoLayoutItems && currentSize != size && itemsCount > 0)
{ {
if (direction == ScrollSnap.ScrollDirection.Horizontal) if (direction == ScrollSnap.ScrollDirection.Horizontal)
{ {
foreach (var tr in listContainerTransform) foreach (var tr in listContainerTransform)
{ {
GameObject child = ((Transform)tr).gameObject; GameObject child = ((Transform) tr).gameObject;
if (child.activeInHierarchy) if (child.activeInHierarchy)
{ {
var childLayout = child.GetComponent<LayoutElement> (); var childLayout = child.GetComponent<LayoutElement>();
if (childLayout == null) if (childLayout == null)
{ {
childLayout = child.AddComponent<LayoutElement> (); childLayout = child.AddComponent<LayoutElement>();
} }
childLayout.minWidth = itemSize; childLayout.minWidth = itemSize;
@ -150,14 +183,14 @@ namespace UnityEngine.UI.Extensions
{ {
foreach (var tr in listContainerTransform) foreach (var tr in listContainerTransform)
{ {
GameObject child = ((Transform)tr).gameObject; GameObject child = ((Transform) tr).gameObject;
if (child.activeInHierarchy) if (child.activeInHierarchy)
{ {
var childLayout = child.GetComponent<LayoutElement> (); var childLayout = child.GetComponent<LayoutElement>();
if (childLayout == null) if (childLayout == null)
{ {
childLayout = child.AddComponent<LayoutElement> (); childLayout = child.AddComponent<LayoutElement>();
} }
childLayout.minHeight = itemSize; childLayout.minHeight = itemSize;
@ -174,22 +207,25 @@ namespace UnityEngine.UI.Extensions
// checking how many children of list are active // checking how many children of list are active
int activeCount = 0; int activeCount = 0;
foreach (var tr in listContainerTransform) { foreach (var tr in listContainerTransform)
if (((Transform)tr).gameObject.activeInHierarchy) { {
if (((Transform) tr).gameObject.activeInHierarchy)
{
activeCount++; activeCount++;
} }
} }
// if anything changed since last check reinitialize anchors list // if anything changed since last check reinitialize anchors list
items = 0; itemsCount = 0;
Array.Resize(ref pageAnchorPositions, activeCount); Array.Resize(ref pageAnchorPositions, activeCount);
if (activeCount > 0) if (activeCount > 0)
{ {
pages = Mathf.Max (activeCount - itemsVisibleAtOnce + 1, 1); pages = Mathf.Max(activeCount - itemsVisibleAtOnce + 1, 1);
if (direction == ScrollDirection.Horizontal) if (direction == ScrollDirection.Horizontal)
{ {
//Debug.Log ("-------------looking for list spanning range----------------");
// looking for list spanning range min/max // looking for list spanning range min/max
scrollRect.horizontalNormalizedPosition = 0; scrollRect.horizontalNormalizedPosition = 0;
listContainerMaxPosition = listContainerTransform.localPosition.x; listContainerMaxPosition = listContainerTransform.localPosition.x;
@ -197,6 +233,8 @@ namespace UnityEngine.UI.Extensions
listContainerMinPosition = listContainerTransform.localPosition.x; listContainerMinPosition = listContainerTransform.localPosition.x;
listContainerSize = listContainerMaxPosition - listContainerMinPosition; listContainerSize = listContainerMaxPosition - listContainerMinPosition;
//itemSize = pages > 1 ? listContainerSize / (float)(pages - 1) : listContainerSize / activeCount;
//itemSize *= (pages + itemsVisibleAtOnce) / (pages + 1);
for (var i = 0; i < pages; i ++) for (var i = 0; i < pages; i ++)
{ {
@ -206,9 +244,12 @@ namespace UnityEngine.UI.Extensions
listContainerTransform.localPosition.z listContainerTransform.localPosition.z
); );
} }
//Debug.Log (String.Format("min:{0:0.0}, max:{1:0.0}, pages: {2:0}, width: {3:0}", listContainerMinPosition, listContainerMaxPosition, pages, listContainerSize/pages));
} }
else else
{ {
//Debug.Log ("-------------looking for list spanning range----------------");
// looking for list spanning range // looking for list spanning range
scrollRect.verticalNormalizedPosition = 1; scrollRect.verticalNormalizedPosition = 1;
listContainerMinPosition = listContainerTransform.localPosition.y; listContainerMinPosition = listContainerTransform.localPosition.y;
@ -216,6 +257,8 @@ namespace UnityEngine.UI.Extensions
listContainerMaxPosition = listContainerTransform.localPosition.y; listContainerMaxPosition = listContainerTransform.localPosition.y;
listContainerSize = listContainerMaxPosition - listContainerMinPosition; listContainerSize = listContainerMaxPosition - listContainerMinPosition;
//itemSize = pages > 1 ? listContainerSize / (float)(pages - 1) : listContainerSize / activeCount;
//itemSize *= (pages + itemsVisibleAtOnce) / (pages + 1);
for (var i = 0; i < pages; i ++) for (var i = 0; i < pages; i ++)
{ {
@ -224,22 +267,24 @@ namespace UnityEngine.UI.Extensions
listContainerMinPosition + itemSize * i, listContainerMinPosition + itemSize * i,
listContainerTransform.localPosition.z listContainerTransform.localPosition.z
); );
} //Debug.Log (String.Format("pos[{0:0}]={1} {2:0%}", i, pageAnchorPositions[i], pages > 1 ? i/(float)(pages-1) : (float)i));
foreach (var tr in listContainerTransform) {
if (((Transform)tr).gameObject.activeInHierarchy) {
activeCount++;
}
} }
//Debug.Log (String.Format("min:{0:0.0}, max:{1:0.0}, pages: {2:0}, width: {3:0}", listContainerMinPosition, listContainerMaxPosition, pages, listContainerSize/pages));
} }
UpdateScrollbar(linkScrolbarSteps); UpdateScrollbar(linkScrolbarSteps);
startingPage = Mathf.Min(startingPage, pages); startingPage = Mathf.Min(startingPage, pages);
ResetPage(); ResetPage();
} }
items = activeCount;
listContainerCachedSize.Set (listContainerRectTransform.rect.size.x, listContainerRectTransform.rect.size.y); if (itemsCount != activeCount)
{
ChangePage(CurrentPage());
}
itemsCount = activeCount;
listContainerCachedSize.Set(listContainerRectTransform.rect.size.x, listContainerRectTransform.rect.size.y);
} }
} }
@ -248,11 +293,11 @@ namespace UnityEngine.UI.Extensions
{ {
if (direction == ScrollDirection.Horizontal) if (direction == ScrollDirection.Horizontal)
{ {
scrollRect.horizontalNormalizedPosition = pages > 1 ? (float)startingPage / (float)(pages - 1) : 0; scrollRect.horizontalNormalizedPosition = pages > 1 ? (float) startingPage / (float) (pages - 1) : 0;
} }
else else
{ {
scrollRect.verticalNormalizedPosition = pages > 1 ? (float)(pages - startingPage - 1) / (float)(pages - 1) : 0; scrollRect.verticalNormalizedPosition = pages > 1 ? (float) (pages - startingPage - 1) / (float) (pages - 1) : 0;
} }
} }
@ -294,7 +339,7 @@ namespace UnityEngine.UI.Extensions
} }
} }
void Update() void LateUpdate()
{ {
UpdateListItemsSize(); UpdateListItemsSize();
UpdateListItemPositions(); UpdateListItemPositions();
@ -303,7 +348,7 @@ namespace UnityEngine.UI.Extensions
{ {
UpdateScrollbar(false); UpdateScrollbar(false);
listContainerTransform.localPosition = Vector3.Lerp(listContainerTransform.localPosition, lerpTarget, 7.5f * Time.deltaTime); listContainerTransform.localPosition = Vector3.Lerp(listContainerTransform.localPosition, lerpTarget, 7.5f * Time.deltaTime);
if (Vector3.Distance(listContainerTransform.localPosition, lerpTarget) < 0.001f) if (Vector3.Distance(listContainerTransform.localPosition, lerpTarget) < 0.001f)
{ {
@ -311,83 +356,87 @@ namespace UnityEngine.UI.Extensions
lerp = false; lerp = false;
UpdateScrollbar(linkScrolbarSteps); UpdateScrollbar(linkScrolbarSteps);
}
//Debug.Log(String.Format("Lerp: {0}/{1} page: {2:0}", lerpTarget, listContainerTransform.localPosition, CurrentPage()));
}
//change the info bullets at the bottom of the screen. Just for visual effect //change the info bullets at the bottom of the screen. Just for visual effect
if (Vector3.Distance(listContainerTransform.localPosition, lerpTarget) < 10f) if (Vector3.Distance(listContainerTransform.localPosition, lerpTarget) < 10f)
{ {
ChangePage(CurrentPage()); ChangePage(CurrentPage());
} }
}
if (fastSwipeTimer)
{
fastSwipeCounter++;
} }
}
private bool fastSwipe = false; //to determine if a fast swipe was performed if (fastSwipeTimer)
{
fastSwipeCounter++;
}
//Debug.Log(String.Format("Update: {0}", listContainerTransform.localPosition));
}
private bool fastSwipe = false; //to determine if a fast swipe was performed
//Function for switching screens with buttons //Function for switching screens with buttons
public void NextScreen() public void NextScreen()
{ {
UpdateListItemPositions (); UpdateListItemPositions();
if (CurrentPage() < pages - 1) if (CurrentPage() < pages - 1)
{ {
lerp = true; lerp = true;
lerpTarget = pageAnchorPositions[CurrentPage() + 1]; lerpTarget = pageAnchorPositions[CurrentPage() + 1];
ChangePage(CurrentPage() + 1); ChangePage(CurrentPage() + 1);
} }
} }
//Function for switching screens with buttons //Function for switching screens with buttons
public void PreviousScreen() public void PreviousScreen()
{ {
UpdateListItemPositions (); UpdateListItemPositions();
if (CurrentPage() > 0) if (CurrentPage() > 0)
{ {
lerp = true; lerp = true;
lerpTarget = pageAnchorPositions[CurrentPage() - 1]; lerpTarget = pageAnchorPositions[CurrentPage() - 1];
ChangePage(CurrentPage() - 1); ChangePage(CurrentPage() - 1);
} }
} }
//Because the CurrentScreen function is not so reliable, these are the functions used for swipes //Because the CurrentScreen function is not so reliable, these are the functions used for swipes
private void NextScreenCommand() private void NextScreenCommand()
{ {
if (pageOnDragStart < pages - 1) if (pageOnDragStart < pages - 1)
{ {
int targetPage = Mathf.Min(pages - 1, pageOnDragStart + itemsVisibleAtOnce); int targetPage = Mathf.Min(pages - 1, pageOnDragStart + itemsVisibleAtOnce);
lerp = true; lerp = true;
lerpTarget = pageAnchorPositions[targetPage]; lerpTarget = pageAnchorPositions[targetPage];
ChangePage(targetPage); ChangePage(targetPage);
} }
} }
//Because the CurrentScreen function is not so reliable, these are the functions used for swipes //Because the CurrentScreen function is not so reliable, these are the functions used for swipes
private void PrevScreenCommand() private void PrevScreenCommand()
{ {
if (pageOnDragStart > 0) if (pageOnDragStart > 0)
{ {
int targetPage = Mathf.Max(0, pageOnDragStart - itemsVisibleAtOnce); int targetPage = Mathf.Max(0, pageOnDragStart - itemsVisibleAtOnce);
lerp = true; lerp = true;
lerpTarget = pageAnchorPositions[targetPage]; lerpTarget = pageAnchorPositions[targetPage];
ChangePage(targetPage); ChangePage(targetPage);
} }
} }
//returns the current screen that the is seeing //returns the current screen that the is seeing
public int CurrentPage() public int CurrentPage()
{ {
float pos; float pos;
@ -404,35 +453,49 @@ namespace UnityEngine.UI.Extensions
float page = pos / itemSize; float page = pos / itemSize;
return Mathf.Clamp(Mathf.RoundToInt(page), 0, pages); //Debug.Log (String.Format("min:{0:0.0}, max:{1:0.0}, current:{2:0.0}/{4:0.0}, page:{3:0.0}, target:{5}", listContainerMinPosition, listContainerMaxPosition, pos, page, listContainerTransform.localPosition.x, pageAnchorPositions[Mathf.RoundToInt(page)]));
}
//changes the bullets on the bottom of the page - pagination return Mathf.Clamp(Mathf.RoundToInt(page), 0, pages);
private void ChangePage(int currentPage) }
{
//changes the bullets on the bottom of the page - pagination
private void ChangePage(int currentPage)
{
startingPage = currentPage; startingPage = currentPage;
if (nextButton)
{
nextButton.interactable = currentPage < pages - 1;
}
if (prevButton)
{
prevButton.interactable = currentPage > 0;
}
if (onPageChange != null) if (onPageChange != null)
{ {
onPageChange(currentPage); onPageChange(currentPage);
} }
} }
#region Interfaces #region Interfaces
public void OnBeginDrag(PointerEventData eventData) public void OnBeginDrag(PointerEventData eventData)
{ {
UpdateScrollbar(false); UpdateScrollbar(false);
fastSwipeCounter = 0; fastSwipeCounter = 0;
fastSwipeTimer = true; fastSwipeTimer = true;
positionOnDragStart = eventData.position; positionOnDragStart = eventData.position;
pageOnDragStart = CurrentPage(); pageOnDragStart = CurrentPage();
}
public void OnEndDrag(PointerEventData eventData) //Debug.Log (String.Format("pageOnDragStart:{0:0}", pageOnDragStart));
{ }
startDrag = true;
public void OnEndDrag(PointerEventData eventData)
{
startDrag = true;
float change = 0; float change = 0;
if (direction == ScrollDirection.Horizontal) if (direction == ScrollDirection.Horizontal)
@ -444,20 +507,20 @@ namespace UnityEngine.UI.Extensions
change = -positionOnDragStart.y + eventData.position.y; change = -positionOnDragStart.y + eventData.position.y;
} }
if (useFastSwipe) if (useFastSwipe)
{ {
fastSwipe = false; fastSwipe = false;
fastSwipeTimer = false; fastSwipeTimer = false;
if (fastSwipeCounter <= fastSwipeTarget) if (fastSwipeCounter <= fastSwipeTarget)
{ {
if (Math.Abs(change) > fastSwipeThreshold) if (Math.Abs(change) > fastSwipeThreshold)
{ {
fastSwipe = true; fastSwipe = true;
} }
} }
if (fastSwipe) if (fastSwipe)
{ {
if (change > 0) if (change > 0)
{ {
NextScreenCommand(); NextScreenCommand();
@ -466,30 +529,30 @@ namespace UnityEngine.UI.Extensions
{ {
PrevScreenCommand(); PrevScreenCommand();
} }
} }
else else
{ {
lerp = true; lerp = true;
lerpTarget = pageAnchorPositions[CurrentPage()]; lerpTarget = pageAnchorPositions[CurrentPage()];
} }
} }
else else
{ {
lerp = true; lerp = true;
lerpTarget = pageAnchorPositions[CurrentPage()]; lerpTarget = pageAnchorPositions[CurrentPage()];
} }
} }
public void OnDrag(PointerEventData eventData) public void OnDrag(PointerEventData eventData)
{ {
lerp = false; lerp = false;
if (startDrag) if (startDrag)
{ {
OnBeginDrag(eventData); OnBeginDrag(eventData);
startDrag = false; startDrag = false;
} }
} }
#endregion #endregion
} }
} }