98 lines
4.6 KiB
C#
98 lines
4.6 KiB
C#
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
|
|
/* Snap a scroll rect to its children items. All self contained.
|
|
* Note: Supports 1 direction only.
|
|
*/
|
|
|
|
namespace eToile_example
|
|
{
|
|
[RequireComponent(typeof(ScrollRect))]
|
|
|
|
public class DragSnapper : UIBehaviour, IEndDragHandler, IBeginDragHandler
|
|
{
|
|
public SnapDirection direction; // The scroll direction.
|
|
public AnimationCurve decelerationCurve = AnimationCurve.Linear(0f, 0f, 1f, 1f); // Transition curve (easing).
|
|
public float speed = 0.5f; // The speed in which we snap.
|
|
public float snapPercentage = 0.2f; // Percent to snal to next streen.
|
|
public int startItem = 0; // First item to show.
|
|
|
|
ScrollRect scrollRect;
|
|
int itemCount; // Item count in our scroll rect (2 minimum)
|
|
float value; // Last valid snapped position.
|
|
int target; // Last target snap position.
|
|
|
|
new void Start()
|
|
{
|
|
itemCount = transform.Find("Container").childCount;
|
|
// Set first item view:
|
|
scrollRect = gameObject.GetComponent<ScrollRect>();
|
|
target = startItem;
|
|
scrollRect.normalizedPosition = new Vector2(startItem / (float)itemCount, 0f);
|
|
value = scrollRect.normalizedPosition.x;
|
|
// Set ScrollRect scroll direction:
|
|
if (direction == SnapDirection.Horizontal)
|
|
{
|
|
scrollRect.horizontal = true;
|
|
scrollRect.vertical = false;
|
|
}
|
|
else if (direction == SnapDirection.Vertical)
|
|
{
|
|
scrollRect.horizontal = false;
|
|
scrollRect.vertical = true;
|
|
}
|
|
}
|
|
|
|
// The direction we are snapping in:
|
|
public enum SnapDirection
|
|
{
|
|
Horizontal,
|
|
Vertical,
|
|
}
|
|
|
|
public void OnBeginDrag(PointerEventData eventData)
|
|
{
|
|
StopCoroutine(SnapRect()); // If we are snapping, stop for the next input.
|
|
}
|
|
|
|
public void OnEndDrag(PointerEventData eventData)
|
|
{
|
|
StartCoroutine(SnapRect());
|
|
}
|
|
|
|
private IEnumerator SnapRect()
|
|
{
|
|
if (itemCount < 2) print("Item count must be 2 or more");
|
|
|
|
float delta = 1f / (itemCount - 1); // Item percentage (just in case they change dynamically).
|
|
float startNormal = direction == SnapDirection.Horizontal ? scrollRect.horizontalNormalizedPosition : scrollRect.verticalNormalizedPosition;
|
|
float tempDelta = (startNormal - value) / delta; // Displacement percentage.
|
|
|
|
// Closest item depending on the current position:
|
|
target = Mathf.RoundToInt(value / delta);
|
|
if (tempDelta > snapPercentage) target++;
|
|
else if (tempDelta < -snapPercentage) target--;
|
|
target = Mathf.Clamp(target, 0, itemCount - 1);
|
|
|
|
float endNormal = delta * target; // Normalized value for the target item.
|
|
float duration = Mathf.Abs((endNormal - startNormal) / speed); // Time needed to get the target depending on speed.
|
|
|
|
float timer = 0f;
|
|
while (timer < 1f) // Animate deceleration.
|
|
{
|
|
timer = Mathf.Min(1f, timer + Time.deltaTime / duration); // Calculate our timer based on our speed.
|
|
value = Mathf.Lerp(startNormal, endNormal, decelerationCurve.Evaluate(timer)); // Our value based on the animation curve.
|
|
|
|
if (direction == SnapDirection.Horizontal) // Depending on direction we set our horizontal or vertical position.
|
|
scrollRect.horizontalNormalizedPosition = value;
|
|
else
|
|
scrollRect.verticalNormalizedPosition = value;
|
|
|
|
yield return new WaitForEndOfFrame(); // Wait until next frame.
|
|
}
|
|
}
|
|
}
|
|
}
|