// // Author: rgy // Date: 2016-06-13 19:56:00 // using System; using UnityEngine; using System.Collections.Generic; using Neatly.UI; using UnityEngine.EventSystems; using UnityEngine.UI; [RequireComponent(typeof(RectTransform))] public class NScrollRect : NUIBehaviour, IPointerDownHandler, IPointerUpHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, ICanvasElement { public enum ReleasePosition { None, Down, Top, } public enum ReleaseEndShow { Hide, ShowEndHint, } public bool m_Horizontal; //水平移动 public bool m_Vertical; //垂直移动 public RectTransform m_Viewport; //显示 public RectTransform m_Content; //内容 private float m_clampedLength = 0; //夹的距离 public RectTransform m_HeadPanel; //头部跟随Panel public RectTransform m_ChoosePanel; //Choose跟随Panel private bool m_OpenChoosePanel; private int m_ChooseIndex = -1; public RectTransform m_TopItem; //顶部Item public RectTransform m_BottomItem; //底部Item private Vector2 m_PointerStartLocalCursor = Vector2.zero; private Vector2 m_ContentStartPosition = Vector2.zero; private Bounds m_ContentBounds; private Bounds m_ViewBounds; private bool m_Dragging; private bool m_IsEnable; #region Content与ViewPort便捷属性 /// Content的坐标 private Vector2 ContentPosition { get { return m_Content.anchoredPosition; } set { m_Content.anchoredPosition = value; } } /// Content宽度 private float ContentWidth { get { return m_Content.rect.width; } } /// Content高度 private float ContentHeight { get { return m_Content.rect.height; } } /// ViewPort宽度 private float ViewWidth { get { return m_Viewport.rect.width; } } /// ViewPort高度 private float ViewHeight { get { return m_Viewport.rect.height; } } private RectTransform viewRect { get { return m_Viewport; } } #endregion #region 无限循环滚动属性 private bool _useLoop; //使用Loop功能 private int _loopIndex; //当前标记位 private int _loopMaxCount; //循环最大数 private int _loopCacheCount; //循环缓存数 private bool _inBack; //是否在back中 private List _loopItems; //网格布局 private GridLayoutGroup m_gridLayoutGroup; private int gridConstraintCount = 1; private Vector2 gridPadding = Vector2.zero; /// 最后一项高度 private float LastLoopItemHeight { get { return _loopItems[_loopItems.Count - 1].rect.height; } } /// 第一项高度 private float FirstLoopItemHeight { get { return _loopItems[0].rect.height; } } /// 顶部Item高度 private float TopItemHeight { get { return m_TopItem ? m_TopItem.rect.height : 0; } } /// 底部Item高度 private float BottomItemHeight { get { return m_BottomItem ? m_BottomItem.rect.height : 0; } } /// 最后一项宽度 private float LastLoopItemWidth { get { return _loopItems[_loopItems.Count - 1].rect.width; } } /// 第一项宽度 private float FirstLoopItemWidth { get { return _loopItems[0].rect.width; } } /// 最左边Item宽度 private float TopItemWidth { get { return m_TopItem ? m_TopItem.rect.width : 0; } } /// 最右边Item宽度 private float BottomItemWidth { get { return m_BottomItem ? m_BottomItem.rect.width : 0; } } [HideInInspector] public Action onLoopFront; [HideInInspector] public Action onLoopBack; [HideInInspector] public Action onDragBegin; public Action onDragEnd; public Action onDraging; [HideInInspector] public Action onPointerDown; private Vector2 _lastDistance; //最终距离 private Vector2 InertiaDestPointVertical //纵向惯性目标点 { get { Vector2 targetPos = _lastDistance * InertiaStep; if (targetPos.y > ViewHeight / 2) { targetPos.y = ViewHeight / 2; } if (targetPos.y < -ViewHeight / 2) { targetPos.y = -ViewHeight / 2; } return targetPos; } } private Vector2 InertiaDestPointHorizontal //横向惯性目标点 { get { Vector2 targetPos = _lastDistance * InertiaStep; if (targetPos.x > ViewWidth / 2) { targetPos.x = ViewWidth / 2; } if (targetPos.x < -ViewWidth / 2) { targetPos.x = -ViewWidth / 2; } return targetPos; } } private const float InertiaStep = 5f; //惯性距离倍数 private const float InertiaSpeed = 5f; //惯性速度 private const float OutInertiaSpeed = 30f; //出界后 private float _nowInertiaSpeed; //当前惯性 private Vector2 _lastPoint = Vector2.zero; //最后的点 private Vector2 _firstPoint = Vector2.zero; //最先的点 private const float ElasticTime = 0.1f; //弹性 #endregion #region 释放刷新 private const float _releaseBorder = 50; private const string SwipeUpKey = "common_text_089"; //上拉 private const string ReleaseKey = "common_text_084"; //放开刷新 private const string EndOfHistoryKey = "common_text_085"; //已经没有内容 public MonoBehaviour m_ReleaseText; //刷新文本 private bool m_IsRelease; //是否有刷新功能 public ReleasePosition m_ReleasePosition; //放开刷新功能 public ReleaseEndShow m_ReleaseEndShow; //结束刷新后显示 [HideInInspector] public Action onRelease; #endregion #region 子Scroll事件 private List m_ScrollList; private Action m_ScrollBeginDragAction; private Action m_ScrollDragAction; private Action m_ScrollEndDragAction; public void SetScrollBeginDragAction(Action action) { m_ScrollBeginDragAction = action; } public void SetScrollDragAction(Action action) { m_ScrollDragAction = action; } public void SetScrollEndDragAction(Action action) { m_ScrollEndDragAction = action; } public void AddScroll(NScrollRect scroll) { scroll.SetScrollBeginDragAction(OnBeginDrag); scroll.SetScrollDragAction(OnDrag); scroll.SetScrollEndDragAction(OnEndDrag); } #endregion private void Start() { Init(); } private void Init() { //网格每份个数 m_gridLayoutGroup = m_Content.gameObject.GetComponent(); if (m_gridLayoutGroup != null) { gridConstraintCount = m_gridLayoutGroup.constraintCount; gridPadding = m_gridLayoutGroup.spacing; } //释放参数 if (m_TopItem) m_TopItem.SetAsFirstSibling(); if (m_BottomItem) m_BottomItem.SetAsLastSibling(); if (m_ReleasePosition == ReleasePosition.None) return; if (m_ReleaseText == null) { NDebug.LogError("释放刷新找不到Text"); return; } SetReleaseLabel(NeatlyUI.I18NLabel(SwipeUpKey)); } public void OnPointerDown(PointerEventData eventData) { Clean(); onPointerDown?.Invoke(); } public void OnPointerUp(PointerEventData eventData) { m_Inertia = true; if (m_Dragging) onDragEnd?.Invoke(); m_Dragging = false; } public void OnBeginDrag(PointerEventData eventData) { Clean(); m_PointerStartLocalCursor = Vector2.zero; RectTransformUtility.ScreenPointToLocalPointInRectangle(viewRect, eventData.position, eventData.pressEventCamera, out m_PointerStartLocalCursor); m_ContentStartPosition = m_Content.anchoredPosition; m_Dragging = true; m_ScrollBeginDragAction?.Invoke(eventData); onDragBegin?.Invoke(); } public void OnEndDrag(PointerEventData eventData) { UpdateBounds(); Vector2 min = m_ContentBounds.min; Vector2 max = m_ContentBounds.max; if (m_Vertical) { if (max.y < m_ViewBounds.max.y || min.y > m_ViewBounds.min.y) { _nowInertiaSpeed = OutInertiaSpeed; } else { _nowInertiaSpeed = InertiaSpeed; } } if (m_Horizontal) { if (max.x < m_ViewBounds.max.x || min.x > m_ViewBounds.min.x) { _nowInertiaSpeed = OutInertiaSpeed; } else { _nowInertiaSpeed = InertiaSpeed; } } _lastPoint = Vector2.zero; _firstPoint = Vector2.zero; m_Inertia = true; m_Dragging = false; m_ScrollEndDragAction?.Invoke(eventData); } public void OnDrag(PointerEventData eventData) { if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(viewRect, eventData.position, eventData.pressEventCamera, out var localCursor)) return; var pointerDelta = localCursor - m_PointerStartLocalCursor; Vector2 position = m_ContentStartPosition + pointerDelta; _lastDistance = position - ContentPosition; MoveDistance(_lastDistance); onDraging?.Invoke(); m_ScrollDragAction?.Invoke(eventData); } private 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(); } } /// /// 移动 /// /// public bool MoveDistance(Vector2 delta) { UpdateBounds(); bool border = false; Vector2 min = m_ContentBounds.min; Vector2 max = m_ContentBounds.max; if (!m_Horizontal) { delta.x = 0; } if (!m_Vertical) { delta.y = 0; } if (m_Vertical) { if (max.y + delta.y < m_ViewBounds.max.y - m_clampedLength) { border = true; } else if (ViewHeight > ContentHeight) { if (max.y + delta.y > m_ViewBounds.max.y + m_clampedLength) { border = true; } } else if (min.y + delta.y > m_ViewBounds.min.y + m_clampedLength) { border = true; } if (m_ReleasePosition != ReleasePosition.None && m_ReleaseText) { switch (m_ReleasePosition) { case ReleasePosition.Down: var label = NeatlyUI.I18NLabel(min.y > m_ViewBounds.min.y + _releaseBorder ? ReleaseKey : !m_IsRelease && m_ReleaseEndShow == ReleaseEndShow.ShowEndHint ? EndOfHistoryKey : SwipeUpKey); SetReleaseLabel(label); break; } } } if (m_Horizontal) { if (max.x + delta.x < m_ViewBounds.max.x - m_clampedLength) { border = true; } else if (ViewWidth > ContentWidth) { if (max.x + delta.x > m_ViewBounds.max.x + m_clampedLength) { border = true; } } else if (min.x + delta.x > m_ViewBounds.min.x + m_clampedLength) { border = true; } if (m_ReleasePosition != ReleasePosition.None && m_ReleaseText) { switch (m_ReleasePosition) { case ReleasePosition.Down: var label = min.x + delta.x > m_ViewBounds.min.x + _releaseBorder ? ReleaseKey : !m_IsRelease && m_ReleaseEndShow == ReleaseEndShow.ShowEndHint ? EndOfHistoryKey : SwipeUpKey; SetReleaseLabel(label); break; } } } Vector2 position = ContentPosition + delta; delta = CalculateOffset(delta); position += delta; if (delta.y != 0 && m_Vertical) position.y = position.y - RubberDelta(delta.y, m_ViewBounds.size.y); if (delta.x != 0 && m_Horizontal) position.x = position.x - RubberDelta(delta.x, m_ViewBounds.size.x); ContentPosition = position; ResetLoopPosition(); return border; } private Vector2 CalculateOffset(Vector2 delta) { Vector2 offset = Vector2.zero; Vector2 min = m_ContentBounds.min; Vector2 max = m_ContentBounds.max; 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; } if (m_Horizontal) { min.x += delta.x; max.x += delta.x; if (max.x < m_ViewBounds.max.x) offset.x = m_ViewBounds.max.x - max.x; else if (min.x > m_ViewBounds.min.x) offset.x = m_ViewBounds.min.x - min.x; } return offset; } private static float RubberDelta(float overStretching, float viewSize) { return (1 - (1 / ((Mathf.Abs(overStretching) * 0.55f / viewSize) + 1))) * viewSize * Mathf.Sign(overStretching); } /// /// 初始化循环滚动 /// /// /// public void InitLoop(RectTransform[] loopItems, int curIndex) { _loopIndex = curIndex - 1; m_IsRelease = m_ReleasePosition != ReleasePosition.None; _loopItems = new List(loopItems); _loopCacheCount = _loopItems.Count; //初始化缓存里面的预制 for (int i = 0; i < _loopCacheCount; i++) { if (onLoopFront != null && m_IsEnable) { onLoopFront.Invoke(curIndex); } curIndex++; } #if UNITY_EDITOR for (int i = 0; i < _loopCacheCount; i++) { _loopItems[i].gameObject.name = "item" + (i + 1); } #endif if (_loopIndex == 0) { ContentPosition = Vector2.zero; m_ContentStartPosition = Vector2.zero; } } public void SetIndex(int index) { m_Inertia = false; m_Content.SetAnchoredPositionOne(0); _loopIndex = index - 1; } /// /// 重置循环滚动 /// /// public void ResetLoop(RectTransform[] loopItems) { m_IsRelease = m_ReleasePosition != ReleasePosition.None; _loopItems = new List(loopItems); _loopCacheCount = _loopItems.Count; #if UNITY_EDITOR for (int i = 0; i < _loopCacheCount; i++) { _loopItems[i].gameObject.name = "item" + (i + 1); } #endif } /// /// 重置Release /// private void ResetReleasePos() { //无刷新功能 if (!m_IsRelease && m_ReleaseEndShow != ReleaseEndShow.ShowEndHint) return; if (_loopIndex + _loopCacheCount >= _loopMaxCount) { if (m_ReleaseText) { m_ReleaseText.gameObject.SetActive(true); switch (m_ReleasePosition) { case ReleasePosition.None: NDebug.LogError("Scroll设置错误"); break; case ReleasePosition.Top: m_ReleaseText.rectTransform().SetAsFirstSibling(); break; case ReleasePosition.Down: m_ReleaseText.rectTransform().SetAsLastSibling(); break; } } } else { if (m_ReleaseText) { m_ReleaseText.gameObject.SetActive(false); } } } /// /// 设置释放状态 /// /// public void SetReleaseActive(bool active) { m_IsRelease = active; if (m_ReleaseEndShow != ReleaseEndShow.ShowEndHint || active) { m_ReleaseText.transform.SetAsLastSibling(); m_ReleaseText.gameObject.SetActive(active); } else { var label = !m_IsRelease && m_ReleaseEndShow == ReleaseEndShow.ShowEndHint ? EndOfHistoryKey : SwipeUpKey; SetReleaseLabel(label); } } /// 改变当前标记位 public void AddCurrentIndex(int addCount) { _loopIndex = _loopIndex + addCount; } /// 设置循环滚动最大数值 public void SetLoopMax(int loopMax) { _loopMaxCount = loopMax; _useLoop = _loopItems.Count < loopMax; //如果有释放刷新的功能 ResetReleasePos(); } /// 获取循环滚动最大数值 public int GetLoopMax(GameObject go) { int maxCount = 0; if (go) { RectTransform tran = go.GetComponent(); if (tran) maxCount = Mathf.CeilToInt(m_Viewport.rect.height / tran.rect.height); } return maxCount; } /// 检测当前状态是否达到需要重置Item项 public void ResetLoopPosition() { if (!_useLoop) return; UpdateBounds(); Vector2 min = m_ContentBounds.min; Vector2 max = m_ContentBounds.max; if (m_Vertical) { if (_loopIndex + _loopCacheCount < _loopMaxCount && m_ViewBounds.min.y - min.y < LastLoopItemHeight + BottomItemHeight && max.y - m_ViewBounds.max.y > FirstLoopItemHeight) { VerticalUp(); for (int i = 0; i < gridConstraintCount; i++) { _loopIndex++; if (onLoopFront != null && m_IsEnable) { onLoopFront.Invoke(_loopIndex + _loopCacheCount); } } ResetReleasePos(); } else if (_loopIndex > 0 && max.y - m_ViewBounds.max.y < FirstLoopItemHeight + TopItemHeight) { VerticalDown(); for (int i = 0; i < gridConstraintCount; i++) { if (onLoopBack != null && m_IsEnable) { onLoopBack.Invoke(_loopIndex); } _loopIndex--; } ResetReleasePos(); } if (m_HeadPanel) { Vector2 offset = new Vector2(0, m_HeadPanel.rect.height); if (_loopIndex > 0) { m_HeadPanel.gameObject.SetActive(false); ContentPosition = ContentPosition - offset; m_ContentStartPosition = m_ContentStartPosition - offset; } else { m_HeadPanel.gameObject.SetActive(true); m_HeadPanel.SetAsFirstSibling(); ContentPosition = ContentPosition + offset; m_ContentStartPosition = m_ContentStartPosition + offset; } } ResetChoosePanel(); } if (m_Horizontal) { if (_loopIndex + _loopCacheCount < _loopMaxCount && m_ViewBounds.min.x - min.x > LastLoopItemWidth + BottomItemWidth && max.x - m_ViewBounds.max.x < FirstLoopItemWidth) { HorizontalLeft(); for (int i = 0; i < gridConstraintCount; i++) { _loopIndex++; if (onLoopFront != null && m_IsEnable) { onLoopFront.Invoke(_loopIndex + _loopCacheCount); } } ResetReleasePos(); } else if (_loopIndex > 0 && max.x - m_ViewBounds.max.x > FirstLoopItemWidth + TopItemWidth) { HorizontalRight(); for (int i = 0; i < gridConstraintCount; i++) { if (onLoopBack != null && m_IsEnable) { onLoopBack.Invoke(_loopIndex); } _loopIndex--; } ResetReleasePos(); } if (m_HeadPanel) { Vector2 offset = new Vector2(m_HeadPanel.rect.width, 0); if (_loopIndex > 0) { m_HeadPanel.gameObject.SetActive(false); ContentPosition = ContentPosition - offset; m_ContentStartPosition = m_ContentStartPosition - offset; } else { m_HeadPanel.gameObject.SetActive(true); m_HeadPanel.SetAsFirstSibling(); ContentPosition = ContentPosition + offset; m_ContentStartPosition = m_ContentStartPosition + offset; } } } } public void SetChooseIndex(int index) { m_ChooseIndex = index; m_ChoosePanel.gameObject.SetActive(false); ResetChoosePanel(); } private void ResetChoosePanel() { if (m_ChooseIndex >= 0) { Vector2 offset = new Vector2(0, m_ChoosePanel.rect.height); // Debug.Log($"c:{m_ChooseIndex} loop:{_loopIndex} count:{_loopItems.Count}"); if (m_ChooseIndex < _loopIndex) { if (m_ChoosePanel.gameObject.activeInHierarchy) { m_ChoosePanel.gameObject.SetActive(false); ContentPosition = ContentPosition - offset; m_ContentStartPosition = m_ContentStartPosition - offset; } } else if (_loopIndex <= m_ChooseIndex - _loopItems.Count) { m_ChoosePanel.gameObject.SetActive(false); } else { if (!m_ChoosePanel.gameObject.activeInHierarchy) { m_ChoosePanel.gameObject.SetActive(true); int lastIndex = m_ChoosePanel.GetSiblingIndex(); int newIndex = _loopItems[m_ChooseIndex - _loopIndex].GetSiblingIndex(); if (newIndex < lastIndex) { newIndex++; } m_ChoosePanel.SetSiblingIndex(newIndex - 1); } } } } /// /// 向上拖拽(Item下移) /// private void VerticalUp() { RectTransform rt = _loopItems[0]; for (int i = 0; i < gridConstraintCount; i++) { rt = _loopItems[0]; if (m_BottomItem) rt.SetSiblingIndex(m_Content.childCount - 2); else rt.SetAsLastSibling(); _loopItems.RemoveAt(0); _loopItems.Add(rt); } Vector2 offset = new Vector2(0, rt.rect.height + gridPadding.y); ContentPosition = ContentPosition - offset; m_ContentStartPosition = m_ContentStartPosition - offset; } /// /// 向下拖拽(Item上移) /// private void VerticalDown() { int lastIndex = _loopItems.Count - 1; RectTransform rt = _loopItems[lastIndex]; for (int i = 0; i < gridConstraintCount; i++) { rt = _loopItems[lastIndex]; if (m_TopItem) rt.SetSiblingIndex(1); else rt.SetAsFirstSibling(); _loopItems.RemoveAt(lastIndex); _loopItems.Insert(0, rt); } Vector2 offset = new Vector2(0, rt.rect.height + gridPadding.y); ContentPosition = ContentPosition + offset; m_ContentStartPosition = m_ContentStartPosition + offset; } /// /// 向左拖拽(Item右移) /// private void HorizontalLeft() { RectTransform rt = _loopItems[0]; for (int i = 0; i < gridConstraintCount; i++) { rt = _loopItems[0]; if (m_BottomItem) rt.SetSiblingIndex(m_Content.childCount - 2); else rt.SetAsLastSibling(); _loopItems.RemoveAt(0); _loopItems.Add(rt); } Vector2 offset = new Vector2(rt.rect.width + gridPadding.x, 0); ContentPosition = ContentPosition + offset; m_ContentStartPosition = m_ContentStartPosition + offset; } /// /// 向右拖拽(Item左移) /// private void HorizontalRight() { int lastIndex = _loopItems.Count - 1; RectTransform rt = _loopItems[lastIndex]; for (int i = 0; i < gridConstraintCount; i++) { rt = _loopItems[lastIndex]; if (m_TopItem) rt.SetSiblingIndex(1); else rt.SetAsFirstSibling(); _loopItems.RemoveAt(lastIndex); _loopItems.Insert(0, rt); } Vector2 offset = new Vector2(rt.rect.width + gridPadding.x, 0); ContentPosition = ContentPosition - offset; m_ContentStartPosition = m_ContentStartPosition - offset; } private void UpdateBounds() { var rect = viewRect.rect; m_ViewBounds = new Bounds(rect.center, rect.size); m_ContentBounds = GetBounds(); 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() { 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; m_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 UITweenImp _ltDescrVertical; //垂直归位动画. private UITweenImp _ltDescrHorizontal; //水平归位动画. /// /// 归位动画 /// public void TweenBack(bool b_refresh = true) { _inBack = true; Clean(); UpdateBounds(); Vector2 min = m_ContentBounds.min; Vector2 max = m_ContentBounds.max; if (m_Vertical) { if (max.y < m_ViewBounds.max.y) { float distance = m_ViewBounds.max.y - max.y + ContentPosition.y; _ltDescrVertical = UITween.MoveY(m_Content, distance, ElasticTime); } else if (ViewHeight > ContentHeight) { if (max.y > m_ViewBounds.max.y) { float distance = m_ViewBounds.max.y - max.y + ContentPosition.y; _ltDescrVertical = UITween.MoveY(m_Content, distance, ElasticTime); } else { _inBack = false; return; } } else if (min.y > m_ViewBounds.min.y) { float distance = m_ViewBounds.min.y - min.y + ContentPosition.y; _ltDescrVertical = UITween.MoveY(m_Content, distance, ElasticTime); } else { if (b_refresh) { m_Inertia = true; } _inBack = false; return; } if (!b_refresh) return; bool needRefresh = CheckRefresh(); if (needRefresh) { var label = NeatlyUI.I18NLabel(!m_IsRelease && m_ReleaseEndShow == ReleaseEndShow.ShowEndHint ? EndOfHistoryKey : SwipeUpKey); SetReleaseLabel(label); if (m_IsRelease) { _ltDescrVertical.SetOnComplete(() => { if (onRelease != null) { onRelease.Invoke(); _inBack = false; } }); } } } if (m_Horizontal) { if (max.x < m_ViewBounds.max.x) { float distance = m_ViewBounds.max.x - max.x + ContentPosition.x; _ltDescrHorizontal = UITween.MoveX(m_Content, distance, ElasticTime); } else if (ViewWidth > ContentWidth) { if (max.x > m_ViewBounds.max.x) { float distance = m_ViewBounds.max.x - max.x + ContentPosition.x; _ltDescrHorizontal = UITween.MoveX(m_Content, distance, ElasticTime); } else { _inBack = false; return; } } else if (min.x > m_ViewBounds.min.x) { float distance = m_ViewBounds.min.x - min.x + ContentPosition.x; _ltDescrHorizontal = UITween.MoveX(m_Content, distance, ElasticTime); } else { if (b_refresh) { m_Inertia = true; } _inBack = false; return; } if (!b_refresh) return; bool needRefresh = CheckRefresh(); if (needRefresh) { var label = NeatlyUI.I18NLabel(!m_IsRelease && m_ReleaseEndShow == ReleaseEndShow.ShowEndHint ? EndOfHistoryKey : SwipeUpKey); SetReleaseLabel(label); if (m_IsRelease) { _ltDescrHorizontal.SetOnComplete(() => { if (onRelease != null) { onRelease.Invoke(); _inBack = false; } }); } } } } /// 检测是否可以刷新 private bool CheckRefresh() { if (!m_IsRelease && m_ReleaseEndShow == ReleaseEndShow.Hide) return false; UpdateBounds(); Vector2 min = m_ContentBounds.min; Vector2 max = m_ContentBounds.max; switch (m_ReleasePosition) { case ReleasePosition.Down: if (m_Vertical && min.y > m_ViewBounds.min.y + _releaseBorder) { return true; } if (m_Horizontal && min.x > m_ViewBounds.min.x + _releaseBorder) { return true; } break; } return false; } private bool m_Inertia; void LateUpdate() { if (!m_IsEnable || m_Dragging) return; if (m_Inertia) { if (_nowInertiaSpeed == OutInertiaSpeed) { TweenBack(); m_Inertia = false; return; } Vector2 _inertiaDestPoint = m_Vertical ? InertiaDestPointVertical : (m_Horizontal ? InertiaDestPointHorizontal : Vector2.zero); _firstPoint = Vector2.Lerp(_firstPoint, _inertiaDestPoint, _nowInertiaSpeed * Time.deltaTime); Vector2 moveOffset = _firstPoint - _lastPoint; bool border = MoveDistance(moveOffset); _lastPoint = _firstPoint; if ((_firstPoint - _inertiaDestPoint).magnitude < 1 || border) { TweenBack(); m_Inertia = false; } } else { if (!_inBack) { TweenBack(false); } } } private void OnEnable() { m_IsEnable = true; CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); } private void OnDisable() { CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this); m_IsEnable = false; m_Inertia = false; LayoutRebuilder.MarkLayoutForRebuild(transform.rectTransform()); } private void OnCanvasHierarchyChanged() { // bool isShow = CheckIsShow(); // if (isShow) // { // m_IsEnable = true; // } // else // { // m_IsEnable = false; // m_Inertia = false; // } } public void Rebuild(CanvasUpdate executing) { } public void LayoutComplete() { } public void GraphicUpdateComplete() { } /// /// 清理(动画与惯性) /// private void Clean() { m_Inertia = false; if (_ltDescrVertical != null) { UITween.Cancel(gameObject); _ltDescrVertical = null; } if (_ltDescrHorizontal != null) { UITween.Cancel(gameObject); _ltDescrHorizontal = null; } } private void SetReleaseLabel(string label) { var text = m_ReleaseText as TextInterface; text.SetText(label); } }