From d07e14add74e017b52ab2371efeea1aa4ea10ced Mon Sep 17 00:00:00 2001 From: chai Date: Sat, 8 May 2021 23:15:13 +0800 Subject: +init --- .../UI_Extension/Scripts/LayoutGroup/PagingView.cs | 554 +++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 Assets/UI_Extension/Scripts/LayoutGroup/PagingView.cs (limited to 'Assets/UI_Extension/Scripts/LayoutGroup/PagingView.cs') diff --git a/Assets/UI_Extension/Scripts/LayoutGroup/PagingView.cs b/Assets/UI_Extension/Scripts/LayoutGroup/PagingView.cs new file mode 100644 index 0000000..b8e9b98 --- /dev/null +++ b/Assets/UI_Extension/Scripts/LayoutGroup/PagingView.cs @@ -0,0 +1,554 @@ +//https://github.com/kiepng/Unity-PagingView +using System; +using UnityEngine.Events; +using UnityEngine.EventSystems; + +namespace UnityEngine.UI.Extensions +{ + [SelectionBase] + [ExecuteInEditMode] + [DisallowMultipleComponent] + [RequireComponent(typeof(RectTransform))] + [AddComponentMenu("UI/Extensions/PagingView")] + public class PagingView + : UIBehaviour + , IInitializePotentialDragHandler + , IBeginDragHandler, IEndDragHandler, IDragHandler + , IScrollHandler + , ICanvasElement + , ILayoutElement + , ILayoutGroup + { + [Serializable] public class PagingEvent : UnityEvent {} + + [SerializeField] private RectTransform m_Content; + public RectTransform Content + { + get { return m_Content; } + set { m_Content = value; } + } + + [SerializeField] private RectTransform[] m_Contents; + public RectTransform[] Contents + { + get { return m_Contents; } + set { m_Contents = value; } + } + + [SerializeField] private bool m_Horizontal = true; + public bool Horizontal + { + get { return m_Horizontal; } + set { m_Horizontal = value; } + } + + [SerializeField] private bool m_Vertical = true; + public bool Vertical + { + get { return m_Vertical; } + set { m_Vertical = value; } + } + + [SerializeField] private float m_Elasticity = 0.1f; + public float Elasticity + { + get { return m_Elasticity; } + set { m_Elasticity = value; } + } + + [SerializeField] private bool m_Inertia = true; + public bool Inertia + { + get { return m_Inertia; } + set { m_Inertia = value; } + } + + [SerializeField] private float m_DecelerationRate = 0.135f; + public float DecelerationRate + { + get { return m_DecelerationRate; } + set { m_DecelerationRate = value; } + } + + [SerializeField] private float m_ScrollSensitivity = 1.0f; + public float ScrollSensitivity + { + get { return m_ScrollSensitivity; } + set { m_ScrollSensitivity = value; } + } + + [SerializeField] private RectTransform m_Viewport; + public RectTransform viewport + { + get {return m_Viewport; } + set { m_Viewport = value; SetDirtyCaching(); } + } + + [SerializeField] private PagingEvent m_OnValueChanged = new PagingEvent(); + public PagingEvent OnValueChanged + { + get { return m_OnValueChanged; } + set { m_OnValueChanged = value; } + } + + private Vector2 m_PointerStartLocalCursor = Vector2.zero; + private Vector2 m_ContentStartPosition = Vector2.zero; + + private RectTransform m_ViewRect; + public RectTransform ViewRect + { + get + { + if (m_ViewRect == null) + m_ViewRect = m_Viewport; + if (m_ViewRect == null) + m_ViewRect = (RectTransform) transform; + return m_ViewRect; + } + } + + [NonSerialized] private RectTransform m_Rect; + private RectTransform RectTransform + { + get + { + if (m_Rect == null) + m_Rect = GetComponent(); + return m_Rect; + } + } + + private Vector2 m_Velocity; + public Vector2 Velocity + { + get { return m_Velocity; } + set { m_Velocity = value; } + } + + private bool m_Dragging; + + private Bounds m_ViewBounds; + private Bounds m_ContentBounds; + private Bounds m_CurrentContentBounds; + private Bounds m_PrevViewBounds; + private Bounds m_PrevContentBounds; + private Bounds m_PrevCurrentContentBounds; + private Vector2 m_PrevPosition = Vector2.zero; + + private DrivenRectTransformTracker m_Tracker; + + [NonSerialized] private bool m_HasRebuiltLayout; + + private const float EPSILON = float.Epsilon; + + protected PagingView() + { + flexibleWidth = -1; + } + + public void Rebuild(CanvasUpdate executing) + { + if (executing == CanvasUpdate.PostLayout) + { + UpdateBounds(); + UpdatePrevData(); + + m_HasRebuiltLayout = true; + } + } + + protected override void OnRectTransformDimensionsChange() + { + SetDirty(); + } + + public virtual void LayoutComplete() {} + + public virtual void GraphicUpdateComplete() {} + + public virtual void CalculateLayoutInputHorizontal() {} + + public virtual void CalculateLayoutInputVertical() {} + + public virtual float minWidth { get { return -1; } } + + public virtual float preferredWidth { get { return -1; } } + + public virtual float flexibleWidth { get; private set; } + + public virtual float minHeight { get { return -1; } } + + public virtual float preferredHeight { get { return -1; } } + + public virtual float flexibleHeight { get { return -1; } } + + public virtual int layoutPriority { get { return -1; } } + + public virtual void SetLayoutHorizontal() { m_Tracker.Clear(); } + + public virtual void SetLayoutVertical() + { + m_ViewBounds = new Bounds(ViewRect.rect.center, ViewRect.rect.size); + m_ContentBounds = GetBounds(m_Content); + m_CurrentContentBounds = GetBounds(m_Contents[m_ContentIndex]); + } + + public virtual void OnScroll(PointerEventData eventData) + { + if (!IsActive()) + return; + + EnsureLayoutHasRebuilt(); + UpdateBounds(); + + Vector2 delta = eventData.scrollDelta; + delta.y *= -1; + if (Vertical && !Horizontal) + { + if (Mathf.Abs(delta.x) > Mathf.Abs(delta.y)) + delta.y = delta.x; + delta.x = 0; + } + + if (Horizontal && !Vertical) + { + if (Mathf.Abs(delta.y) > Mathf.Abs(delta.x)) + delta.x = delta.y; + delta.y = 0; + } + + Vector2 position = m_Content.anchoredPosition; + position += delta * m_ScrollSensitivity; + + SetContentAnchoredPosition(position); + UpdateBounds(); + } + + public void OnInitializePotentialDrag(PointerEventData eventData) + { + if (eventData.button != PointerEventData.InputButton.Left) + return; + + m_Velocity = Vector2.zero; + } + + public void OnBeginDrag(PointerEventData eventData) + { + if (eventData.button != PointerEventData.InputButton.Left) + return; + + if (!IsActive()) + return; + + UpdateBounds(); + + m_PointerStartLocalCursor = Vector2.zero; + RectTransformUtility.ScreenPointToLocalPointInRectangle(ViewRect, eventData.position, + eventData.pressEventCamera, out m_PointerStartLocalCursor); + m_ContentStartPosition = m_Content.anchoredPosition; + m_Dragging = true; + } + + public void OnEndDrag(PointerEventData eventData) + { + if (eventData.button != PointerEventData.InputButton.Left) + return; + + m_Dragging = false; + + JudgementIndex(); + } + + public void OnDrag(PointerEventData eventData) + { + if (eventData.button != PointerEventData.InputButton.Left) + return; + + if (!IsActive()) + return; + + Vector2 localCursor; + if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(ViewRect, eventData.position, + eventData.pressEventCamera, out localCursor)) + return; + + UpdateBounds(); + + var pointerDelta = localCursor - m_PointerStartLocalCursor; + Vector2 position = m_ContentStartPosition + pointerDelta; + Vector2 offset = CalculateOffset(m_ContentBounds, position - m_Content.anchoredPosition); + position += offset; + if (Math.Abs(offset.x) > EPSILON) + position.x = position.x - RubberDelta(offset.x, m_ViewBounds.size.x); + if (Math.Abs(offset.y) > EPSILON) + position.y = position.y - RubberDelta(offset.y, m_ViewBounds.size.y); + + SetContentAnchoredPosition(position); + } + + protected override void OnEnable() + { + base.OnEnable(); + + CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); + } + + protected override void OnDisable() + { + CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this); + + m_Tracker.Clear(); + m_Velocity = Vector2.zero; + LayoutRebuilder.MarkLayoutForRebuild(RectTransform); + + base.OnDisable(); + } + + public override bool IsActive() + { + return base.IsActive() && m_Content != null; + } + + private int m_PrevContentIndex; + private int m_ContentIndex; + private void LateUpdate() + { + if (!m_Content) + return; + + EnsureLayoutHasRebuilt(); + UpdateBounds(); + float deltaTime = Time.unscaledDeltaTime; + Vector2 offset = CalculateOffset(m_CurrentContentBounds, Vector2.zero); + if (!m_Dragging && (offset != Vector2.zero || m_Velocity != Vector2.zero)) + { + Vector2 position = m_Content.anchoredPosition; + for (int axis = 0; axis < 2; ++axis) + { + if (Math.Abs(offset[axis]) > EPSILON) + { + float speed = m_Velocity[axis]; + position[axis] = Mathf.SmoothDamp(m_Content.anchoredPosition[axis], m_Content.anchoredPosition[axis] + offset[axis], ref speed, m_Elasticity, Mathf.Infinity, deltaTime); + m_Velocity[axis] = speed; + } + else if (m_Inertia) + { + m_Velocity[axis] *= Mathf.Pow(m_DecelerationRate, deltaTime); + if (Mathf.Abs(m_Velocity[axis]) < 1) + m_Velocity[axis] = 0; + position[axis] += m_Velocity[axis] * deltaTime; + } + else + { + m_Velocity[axis] = 0; + } + } + + if (m_Velocity != Vector2.zero) + SetContentAnchoredPosition(position); + } + + if (m_Dragging && m_Inertia) + { + Vector3 newVelocity = (m_Content.anchoredPosition - m_PrevPosition) / deltaTime; + m_Velocity = Vector3.Lerp(m_Velocity, newVelocity, deltaTime * 10); + } + + if (m_Dragging && m_Velocity != Vector2.zero) + JudgementIndex(offset); + + if (!m_Dragging && m_PrevContentIndex != m_ContentIndex) + { + m_OnValueChanged.Invoke(m_ContentIndex); + m_PrevContentIndex = m_ContentIndex; + } + + if (m_ViewBounds != m_PrevViewBounds || m_ContentBounds != m_PrevContentBounds + || m_ContentBounds != m_PrevCurrentContentBounds || m_Content.anchoredPosition != m_PrevPosition) + UpdatePrevData(); + } + + public virtual void StopMovement() + { + m_Velocity = Vector2.zero; + } + + protected virtual void SetContentAnchoredPosition(Vector2 position) + { + if (!m_Horizontal) + position.x = m_Content.anchoredPosition.x; + if (!m_Vertical) + position.y = m_Content.anchoredPosition.y; + + if (position != m_Content.anchoredPosition) + { + m_Content.anchoredPosition = position; + UpdateBounds(); + } + } + + private void EnsureLayoutHasRebuilt() + { + if (m_HasRebuiltLayout && !CanvasUpdateRegistry.IsRebuildingLayout()) + Canvas.ForceUpdateCanvases(); + } + + private void UpdatePrevData() + { + if (m_Content == null) + m_PrevPosition = Vector2.zero; + else + m_PrevPosition = m_Content.anchoredPosition; + m_PrevViewBounds = m_ViewBounds; + m_PrevContentBounds = m_ContentBounds; + m_PrevCurrentContentBounds = m_ContentBounds; + } + + private static float RubberDelta(float overStretching, float viewSize) + { + return (1 - (1 / ((Mathf.Abs(overStretching) * 0.55f / viewSize) + 1))) + * viewSize * Mathf.Sign(overStretching); + } + + private void UpdateBounds() + { + m_ViewBounds = new Bounds(ViewRect.rect.center, ViewRect.rect.size); + m_ContentBounds = GetBounds(m_Content); + m_CurrentContentBounds = GetBounds(m_Contents[m_ContentIndex]); + + if (m_Content == null) + return; + + Vector3 contentSize = m_ContentBounds.size; + Vector3 contentPos = m_ContentBounds.center; + Vector3 excess = m_ViewBounds.size - contentSize; + if (excess.x > 0) + { + contentPos.x -= excess.x * (m_Content.pivot.x - 0.5f); + contentSize.x = m_ViewBounds.size.x; + } + if (excess.y > 0) + { + contentPos.y -= excess.y * (m_Content.pivot.y - 0.5f); + contentSize.y = m_ViewBounds.size.y; + } + + m_ContentBounds.size = contentSize; + m_ContentBounds.center = contentPos; + } + + private readonly Vector3[] m_Corners = new Vector3[4]; + private Bounds GetBounds(RectTransform content) + { + if (m_Content == null) + return new Bounds(); + + var vMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); + var vMax = new Vector3(float.MinValue, float.MinValue, float.MinValue); + + var toLocal = ViewRect.worldToLocalMatrix; + content.GetWorldCorners(m_Corners); + for (int j = 0; j < 4; j++) + { + Vector3 v = toLocal.MultiplyPoint3x4(m_Corners[j]); + vMin = Vector3.Min(v, vMin); + vMax = Vector3.Max(v, vMax); + } + + var bounds = new Bounds(vMin, Vector3.zero); + bounds.Encapsulate(vMax); + return bounds; + } + + private Vector2 CalculateOffset(Bounds bounds, Vector2 delta) + { + Vector2 offset = Vector2.zero; + + Vector2 min = bounds.min; + Vector2 max = bounds.max; + + if (m_Horizontal) + { + min.x += delta.x; + max.x += delta.x; + if (min.x > m_ViewBounds.min.x) + offset.x = m_ViewBounds.min.x - min.x; + else if (max.x < m_ViewBounds.max.x) + offset.x = m_ViewBounds.max.x - max.x; + } + + if (m_Vertical) + { + min.y += delta.y; + max.y += delta.y; + if (max.y < m_ViewBounds.max.y) + offset.y = m_ViewBounds.max.y - max.y; + else if (min.y > m_ViewBounds.min.y) + offset.y = m_ViewBounds.min.y - min.y; + } + + return offset; + } + + private void JudgementIndex() + { + if (Horizontal) + { + if (-m_Velocity.x > m_ViewBounds.size.x) + m_ContentIndex = Mathf.Clamp(m_PrevContentIndex + 1, 0, m_Contents.Length - 1); + else if (m_Velocity.x > m_ViewBounds.size.x) + m_ContentIndex = Mathf.Clamp(m_PrevContentIndex - 1, 0, m_Contents.Length - 1); + } + + if (Vertical) + { + if (m_Velocity.y > m_ViewBounds.size.y) + m_ContentIndex = Mathf.Clamp(m_PrevContentIndex + 1, 0, m_Contents.Length - 1); + else if (-m_Velocity.y > m_ViewBounds.size.y) + m_ContentIndex = Mathf.Clamp(m_PrevContentIndex - 1, 0, m_Contents.Length - 1); + } + } + + private void JudgementIndex(Vector2 offset) + { + if (Horizontal) + { + if (offset.x > m_ViewBounds.extents.x) + m_ContentIndex = Mathf.Clamp(m_ContentIndex + 1, 0, m_Contents.Length - 1); + else if (-offset.x > m_ViewBounds.extents.x) + m_ContentIndex = Mathf.Clamp(m_ContentIndex - 1, 0, m_Contents.Length - 1); + } + + if (Vertical) + { + if (-offset.y > m_ViewBounds.extents.y) + m_ContentIndex = Mathf.Clamp(m_ContentIndex + 1, 0, m_Contents.Length - 1); + else if (offset.y > m_ViewBounds.extents.y) + m_ContentIndex = Mathf.Clamp(m_ContentIndex - 1, 0, m_Contents.Length - 1); + } + } + + protected void SetDirty() + { + if (!IsActive()) + return; + LayoutRebuilder.MarkLayoutForRebuild(RectTransform); + } + + protected void SetDirtyCaching() + { + if (!IsActive()) + return; + + CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); + LayoutRebuilder.MarkLayoutForRebuild(RectTransform); + } + +#if UNITY_EDITOR + protected override void OnValidate() + { + SetDirtyCaching(); + } +#endif + } +} -- cgit v1.1-26-g67d0