2025-06-07 17:43:34 +08:00

2321 lines
77 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using UnityEngine;
using FairyGUI.Utils;
namespace FairyGUI
{
/// <summary>
///
/// </summary>
public class ScrollPane : EventDispatcher
{
/// <summary>
/// 当前被拖拽的滚动面板。同一时间只能有一个在进行此操作。
/// </summary>
public static ScrollPane draggingPane { get; private set; }
ScrollType _scrollType;
float _scrollStep;
float _decelerationRate;
Margin _scrollBarMargin;
bool _bouncebackEffect;
bool _touchEffect;
bool _scrollBarDisplayAuto;
bool _vScrollNone;
bool _hScrollNone;
bool _needRefresh;
int _refreshBarAxis;
bool _displayOnLeft;
bool _snapToItem;
internal bool _displayInDemand;
bool _mouseWheelEnabled;
bool _softnessOnTopOrLeftSide;
bool _pageMode;
Vector2 _pageSize;
bool _inertiaDisabled;
bool _maskDisabled;
bool _floating;
bool _dontClipMargin;
float _xPos;
float _yPos;
Vector2 _viewSize;
Vector2 _contentSize;
Vector2 _overlapSize;
Vector2 _containerPos;
Vector2 _beginTouchPos;
Vector2 _lastTouchPos;
Vector2 _lastTouchGlobalPos;
Vector2 _velocity;
float _velocityScale;
float _lastMoveTime;
bool _dragged;
bool _isHoldAreaDone;
int _aniFlag;
internal int _loop;
int _headerLockedSize;
int _footerLockedSize;
bool _hover;
int _tweening;
Vector2 _tweenStart;
Vector2 _tweenChange;
Vector2 _tweenTime;
Vector2 _tweenDuration;
Action _refreshDelegate;
TimerCallback _tweenUpdateDelegate;
GTweenCallback1 _hideScrollBarDelegate;
GComponent _owner;
Container _maskContainer;
Container _container;
GScrollBar _hzScrollBar;
GScrollBar _vtScrollBar;
GComponent _header;
GComponent _footer;
Controller _pageController;
EventListener _onScroll;
EventListener _onScrollEnd;
EventListener _onPullDownRelease;
EventListener _onPullUpRelease;
static int _gestureFlag;
public static float TWEEN_TIME_GO = 0.3f; //调用SetPos(ani)时使用的缓动时间
public static float TWEEN_TIME_DEFAULT = 0.3f; //惯性滚动的最小缓动时间
public static float PULL_RATIO = 0.5f; //下拉过顶或者上拉过底时允许超过的距离占显示区域的比例
public ScrollPane(GComponent owner)
{
_onScroll = new EventListener(this, "onScroll");
_onScrollEnd = new EventListener(this, "onScrollEnd");
_scrollStep = UIConfig.defaultScrollStep;
_softnessOnTopOrLeftSide = UIConfig.allowSoftnessOnTopOrLeftSide;
_decelerationRate = UIConfig.defaultScrollDecelerationRate;
_touchEffect = UIConfig.defaultScrollTouchEffect;
_bouncebackEffect = UIConfig.defaultScrollBounceEffect;
_mouseWheelEnabled = true;
_pageSize = Vector2.one;
_refreshDelegate = Refresh;
_tweenUpdateDelegate = TweenUpdate;
_hideScrollBarDelegate = __barTweenComplete;
_owner = owner;
_maskContainer = new Container();
_owner.rootContainer.AddChild(_maskContainer);
_container = _owner.container;
_container.SetXY(0, 0);
_maskContainer.AddChild(_container);
_owner.rootContainer.onMouseWheel.Add(__mouseWheel);
_owner.rootContainer.onTouchBegin.Add(__touchBegin);
_owner.rootContainer.onTouchMove.Add(__touchMove);
_owner.rootContainer.onTouchEnd.Add(__touchEnd);
}
public void Setup(ByteBuffer buffer)
{
_scrollType = (ScrollType)buffer.ReadByte();
ScrollBarDisplayType scrollBarDisplay = (ScrollBarDisplayType)buffer.ReadByte();
int flags = buffer.ReadInt();
if (buffer.ReadBool())
{
_scrollBarMargin.top = buffer.ReadInt();
_scrollBarMargin.bottom = buffer.ReadInt();
_scrollBarMargin.left = buffer.ReadInt();
_scrollBarMargin.right = buffer.ReadInt();
}
string vtScrollBarRes = buffer.ReadS();
string hzScrollBarRes = buffer.ReadS();
string headerRes = buffer.ReadS();
string footerRes = buffer.ReadS();
_displayOnLeft = (flags & 1) != 0;
_snapToItem = (flags & 2) != 0;
_displayInDemand = (flags & 4) != 0;
_pageMode = (flags & 8) != 0;
if ((flags & 16) != 0)
_touchEffect = true;
else if ((flags & 32) != 0)
_touchEffect = false;
if ((flags & 64) != 0)
_bouncebackEffect = true;
else if ((flags & 128) != 0)
_bouncebackEffect = false;
_inertiaDisabled = (flags & 256) != 0;
_maskDisabled = (flags & 512) != 0;
_floating = (flags & 1024) != 0;
_dontClipMargin = (flags & 2048) != 0;
if (scrollBarDisplay == ScrollBarDisplayType.Default)
{
if (Application.isMobilePlatform)
scrollBarDisplay = ScrollBarDisplayType.Auto;
else
scrollBarDisplay = UIConfig.defaultScrollBarDisplay;
}
if (scrollBarDisplay != ScrollBarDisplayType.Hidden)
{
if (_scrollType == ScrollType.Both || _scrollType == ScrollType.Vertical)
{
string res = vtScrollBarRes != null ? vtScrollBarRes : UIConfig.verticalScrollBar;
if (!string.IsNullOrEmpty(res))
{
_vtScrollBar = UIPackage.CreateObjectFromURL(res) as GScrollBar;
if (_vtScrollBar == null)
Debug.LogWarning("FairyGUI: cannot create scrollbar from " + res);
else
{
_vtScrollBar.SetScrollPane(this, true);
_owner.rootContainer.AddChild(_vtScrollBar.displayObject);
}
}
}
if (_scrollType == ScrollType.Both || _scrollType == ScrollType.Horizontal)
{
string res = hzScrollBarRes != null ? hzScrollBarRes : UIConfig.horizontalScrollBar;
if (!string.IsNullOrEmpty(res))
{
_hzScrollBar = UIPackage.CreateObjectFromURL(res) as GScrollBar;
if (_hzScrollBar == null)
Debug.LogWarning("FairyGUI: cannot create scrollbar from " + res);
else
{
_hzScrollBar.SetScrollPane(this, false);
_owner.rootContainer.AddChild(_hzScrollBar.displayObject);
}
}
}
_scrollBarDisplayAuto = scrollBarDisplay == ScrollBarDisplayType.Auto;
if (_scrollBarDisplayAuto)
{
if (_vtScrollBar != null)
_vtScrollBar.displayObject.visible = false;
if (_hzScrollBar != null)
_hzScrollBar.displayObject.visible = false;
_owner.rootContainer.onRollOver.Add(__rollOver);
_owner.rootContainer.onRollOut.Add(__rollOut);
}
}
else
_mouseWheelEnabled = false;
if (Application.isPlaying)
{
if (headerRes != null)
{
_header = (GComponent)UIPackage.CreateObjectFromURL(headerRes);
if (_header == null)
Debug.LogWarning("FairyGUI: cannot create scrollPane header from " + headerRes);
}
if (footerRes != null)
{
_footer = (GComponent)UIPackage.CreateObjectFromURL(footerRes);
if (_footer == null)
Debug.LogWarning("FairyGUI: cannot create scrollPane footer from " + footerRes);
}
if (_header != null || _footer != null)
_refreshBarAxis = (_scrollType == ScrollType.Both || _scrollType == ScrollType.Vertical) ? 1 : 0;
}
SetSize(owner.width, owner.height);
}
/// <summary>
///
/// </summary>
public void Dispose()
{
RemoveEventListeners();
if (_tweening != 0)
Timers.inst.Remove(_tweenUpdateDelegate);
if (draggingPane == this)
draggingPane = null;
_pageController = null;
if (_hzScrollBar != null)
_hzScrollBar.Dispose();
if (_vtScrollBar != null)
_vtScrollBar.Dispose();
if (_header != null)
_header.Dispose();
if (_footer != null)
_footer.Dispose();
}
/// <summary>
/// Dispatched when scrolling.
/// 在滚动时派发该事件。
/// </summary>
public EventListener onScroll
{
get { return _onScroll ?? (_onScroll = new EventListener(this, "onScroll")); }
}
/// <summary>
/// 在滚动结束时派发该事件。
/// </summary>
public EventListener onScrollEnd
{
get { return _onScrollEnd ?? (_onScrollEnd = new EventListener(this, "onScrollEnd")); }
}
/// <summary>
/// 向下拉过上边缘后释放则派发该事件。
/// </summary>
public EventListener onPullDownRelease
{
get { return _onPullDownRelease ?? (_onPullDownRelease = new EventListener(this, "onPullDownRelease")); }
}
/// <summary>
/// 向上拉过下边缘后释放则派发该事件。
/// </summary>
public EventListener onPullUpRelease
{
get { return _onPullUpRelease ?? (_onPullUpRelease = new EventListener(this, "onPullUpRelease")); }
}
/// <summary>
///
/// </summary>
public GComponent owner
{
get { return _owner; }
}
/// <summary>
///
/// </summary>
public GScrollBar hzScrollBar
{
get { return _hzScrollBar; }
}
/// <summary>
///
/// </summary>
public GScrollBar vtScrollBar
{
get { return _vtScrollBar; }
}
/// <summary>
///
/// </summary>
public GComponent header
{
get { return _header; }
}
/// <summary>
///
/// </summary>
public GComponent footer
{
get { return _footer; }
}
/// <summary>
/// 滚动到达边缘时是否允许回弹效果。
/// </summary>
public bool bouncebackEffect
{
get { return _bouncebackEffect; }
set { _bouncebackEffect = value; }
}
/// <summary>
/// 是否允许拖拽内容区域进行滚动。
/// </summary>
public bool touchEffect
{
get { return _touchEffect; }
set { _touchEffect = value; }
}
/// <summary>
/// 是否允许惯性滚动。
/// </summary>
public bool inertiaDisabled
{
get { return _inertiaDisabled; }
set { _inertiaDisabled = value; }
}
/// <summary>
/// 是否允许在左/上边缘显示虚化效果。
/// </summary>
public bool softnessOnTopOrLeftSide
{
get { return _softnessOnTopOrLeftSide; }
set { _softnessOnTopOrLeftSide = value; }
}
/// <summary>
/// 当调用ScrollPane.scrollUp/Down/Left/Right时或者点击滚动条的上下箭头时滑动的距离。
/// </summary>
public float scrollStep
{
get { return _scrollStep; }
set
{
_scrollStep = value;
if (_scrollStep == 0)
_scrollStep = UIConfig.defaultScrollStep;
}
}
/// <summary>
/// 滚动位置是否保持贴近在某个元件的边缘。
/// </summary>
public bool snapToItem
{
get { return _snapToItem; }
set { _snapToItem = value; }
}
/// <summary>
/// 是否页面滚动模式。
/// </summary>
public bool pageMode
{
get { return _pageMode; }
set { _pageMode = value; }
}
/// <summary>
///
/// </summary>
public Controller pageController
{
get { return _pageController; }
set { _pageController = value; }
}
/// <summary>
/// 是否允许使用鼠标滚轮进行滚动。
/// </summary>
public bool mouseWheelEnabled
{
get { return _mouseWheelEnabled; }
set { _mouseWheelEnabled = value; }
}
/// <summary>
/// 当处于惯性滚动时减速的速率。默认值是UIConfig.defaultScrollDecelerationRate。
/// 越接近1减速越慢意味着滑动的时间和距离更长。
/// </summary>
public float decelerationRate
{
get { return _decelerationRate; }
set { _decelerationRate = value; }
}
/// <summary>
/// </summary>
public bool isDragged
{
get { return _dragged; }
}
/// <summary>
/// 当前X轴滚动位置百分比0~1包含
/// </summary>
public float percX
{
get { return _overlapSize.x == 0 ? 0 : _xPos / _overlapSize.x; }
set { SetPercX(value, false); }
}
/// <summary>
/// 设置当前X轴滚动位置百分比0~1包含
/// </summary>
/// <param name="value"></param>
/// <param name="ani">是否使用缓动到达目标。</param>
public void SetPercX(float value, bool ani)
{
_owner.EnsureBoundsCorrect();
SetPosX(_overlapSize.x * Mathf.Clamp01(value), ani);
}
/// <summary>
/// 当前Y轴滚动位置百分比0~1包含
/// </summary>
public float percY
{
get { return _overlapSize.y == 0 ? 0 : _yPos / _overlapSize.y; }
set { SetPercY(value, false); }
}
/// <summary>
/// 设置当前Y轴滚动位置百分比0~1包含
/// </summary>
/// <param name="value"></param>
/// <param name="ani">是否使用缓动到达目标。</param>
public void SetPercY(float value, bool ani)
{
_owner.EnsureBoundsCorrect();
SetPosY(_overlapSize.y * Mathf.Clamp01(value), ani);
}
/// <summary>
/// 当前X轴滚动位置值范围是viewWidth与contentWidth之差。
/// </summary>
public float posX
{
get { return _xPos; }
set { SetPosX(value, false); }
}
/// <summary>
/// 设置当前X轴滚动位置。
/// </summary>
/// <param name="value"></param>
/// <param name="ani">是否使用缓动到达目标。</param>
public void SetPosX(float value, bool ani)
{
_owner.EnsureBoundsCorrect();
if (_loop == 1)
LoopCheckingNewPos(ref value, 0);
value = Mathf.Clamp(value, 0, _overlapSize.x);
if (value != _xPos)
{
_xPos = value;
PosChanged(ani);
}
}
/// <summary>
/// 当前Y轴滚动位置值范围是viewHeight与contentHeight之差。
/// </summary>
public float posY
{
get { return _yPos; }
set { SetPosY(value, false); }
}
/// <summary>
/// 设置当前Y轴滚动位置。
/// </summary>
/// <param name="value"></param>
/// <param name="ani">是否使用缓动到达目标。</param>
public void SetPosY(float value, bool ani)
{
_owner.EnsureBoundsCorrect();
if (_loop == 2)
LoopCheckingNewPos(ref value, 1);
value = Mathf.Clamp(value, 0, _overlapSize.y);
if (value != _yPos)
{
_yPos = value;
PosChanged(ani);
}
}
/// <summary>
/// 返回当前滚动位置是否在最下边。
/// </summary>
public bool isBottomMost
{
get { return _yPos == _overlapSize.y || _overlapSize.y == 0; }
}
/// <summary>
/// 返回当前滚动位置是否在最右边。
/// </summary>
public bool isRightMost
{
get { return _xPos == _overlapSize.x || _overlapSize.x == 0; }
}
/// <summary>
/// 如果处于分页模式返回当前在X轴的页码。
/// </summary>
public int currentPageX
{
get
{
if (!_pageMode)
return 0;
int page = Mathf.FloorToInt(_xPos / _pageSize.x);
if (_xPos - page * _pageSize.x > _pageSize.x * 0.5f)
page++;
return page;
}
set
{
if (!_pageMode)
return;
_owner.EnsureBoundsCorrect();
if (_overlapSize.x > 0)
this.SetPosX(value * _pageSize.x, false);
}
}
/// <summary>
/// 如果处于分页模式可设置X轴的页码。
/// </summary>
/// <param name="value"></param>
/// <param name="ani">是否使用缓动到达目标。</param>
public void SetCurrentPageX(int value, bool ani)
{
if (!_pageMode)
return;
_owner.EnsureBoundsCorrect();
if (_overlapSize.x > 0)
this.SetPosX(value * _pageSize.x, ani);
}
/// <summary>
/// 如果处于分页模式返回当前在Y轴的页码。
/// </summary>
public int currentPageY
{
get
{
if (!_pageMode)
return 0;
int page = Mathf.FloorToInt(_yPos / _pageSize.y);
if (_yPos - page * _pageSize.y > _pageSize.y * 0.5f)
page++;
return page;
}
set
{
if (!_pageMode)
return;
_owner.EnsureBoundsCorrect();
if (_overlapSize.y > 0)
this.SetPosY(value * _pageSize.y, false);
}
}
/// <summary>
/// 如果处于分页模式可设置Y轴的页码。
/// </summary>
/// <param name="value"></param>
/// <param name="ani">是否使用缓动到达目标。</param>
public void SetCurrentPageY(int value, bool ani)
{
if (!_pageMode)
return;
_owner.EnsureBoundsCorrect();
if (_overlapSize.y > 0)
this.SetPosY(value * _pageSize.y, ani);
}
/// <summary>
/// 这个值与PosX不同在于他反映的是实时位置而PosX在有缓动过程的情况下只是终值。
/// </summary>
public float scrollingPosX
{
get
{
return Mathf.Clamp(-_container.x, 0, _overlapSize.x);
}
}
/// <summary>
/// 这个值与PosY不同在于他反映的是实时位置而PosY在有缓动过程的情况下只是终值。
/// </summary>
public float scrollingPosY
{
get
{
return Mathf.Clamp(-_container.y, 0, _overlapSize.y);
}
}
/// <summary>
/// 显示内容宽度。
/// </summary>
public float contentWidth
{
get
{
return _contentSize.x;
}
}
/// <summary>
/// 显示内容高度。
/// </summary>
public float contentHeight
{
get
{
return _contentSize.y;
}
}
/// <summary>
/// 显示区域宽度。
/// </summary>
public float viewWidth
{
get { return _viewSize.x; }
set
{
value = value + _owner.margin.left + _owner.margin.right;
if (_vtScrollBar != null && !_floating)
value += _vtScrollBar.width;
_owner.width = value;
}
}
/// <summary>
/// 显示区域高度。
/// </summary>
public float viewHeight
{
get { return _viewSize.y; }
set
{
value = value + _owner.margin.top + _owner.margin.bottom;
if (_hzScrollBar != null && !_floating)
value += _hzScrollBar.height;
_owner.height = value;
}
}
/// <summary>
///
/// </summary>
public void ScrollTop()
{
ScrollTop(false);
}
/// <summary>
///
/// </summary>
/// <param name="ani"></param>
public void ScrollTop(bool ani)
{
this.SetPercY(0, ani);
}
/// <summary>
///
/// </summary>
public void ScrollBottom()
{
ScrollBottom(false);
}
/// <summary>
///
/// </summary>
/// <param name="ani"></param>
public void ScrollBottom(bool ani)
{
this.SetPercY(1, ani);
}
/// <summary>
///
/// </summary>
public void ScrollUp()
{
ScrollUp(1, false);
}
/// <summary>
///
/// </summary>
/// <param name="ratio"></param>
/// <param name="ani"></param>
public void ScrollUp(float ratio, bool ani)
{
if (_pageMode)
SetPosY(_yPos - _pageSize.y * ratio, ani);
else
SetPosY(_yPos - _scrollStep * ratio, ani);
}
/// <summary>
///
/// </summary>
public void ScrollDown()
{
ScrollDown(1, false);
}
/// <summary>
///
/// </summary>
/// <param name="ratio"></param>
/// <param name="ani"></param>
public void ScrollDown(float ratio, bool ani)
{
if (_pageMode)
SetPosY(_yPos + _pageSize.y * ratio, ani);
else
SetPosY(_yPos + _scrollStep * ratio, ani);
}
/// <summary>
///
/// </summary>
public void ScrollLeft()
{
ScrollLeft(1, false);
}
/// <summary>
///
/// </summary>
/// <param name="speed"></param>
/// <param name="ani"></param>
public void ScrollLeft(float ratio, bool ani)
{
if (_pageMode)
SetPosX(_xPos - _pageSize.x * ratio, ani);
else
SetPosX(_xPos - _scrollStep * ratio, ani);
}
/// <summary>
///
/// </summary>
public void ScrollRight()
{
ScrollRight(1, false);
}
/// <summary>
///
/// </summary>
/// <param name="ratio"></param>
/// <param name="ani"></param>
public void ScrollRight(float ratio, bool ani)
{
if (_pageMode)
SetPosX(_xPos + _pageSize.x * ratio, ani);
else
SetPosX(_xPos + _scrollStep * ratio, ani);
}
/// <summary>
///
/// </summary>
/// <param name="obj">obj can be any object on stage, not limited to the direct child of this container.</param>
public void ScrollToView(GObject obj)
{
ScrollToView(obj, false);
}
/// <summary>
///
/// </summary>
/// <param name="obj">obj can be any object on stage, not limited to the direct child of this container.</param>
/// <param name="ani">If moving to target position with animation</param>
public void ScrollToView(GObject obj, bool ani)
{
ScrollToView(obj, ani, false);
}
/// <summary>
///
/// </summary>
/// <param name="obj">obj can be any object on stage, not limited to the direct child of this container.</param>
/// <param name="ani">If moving to target position with animation</param>
/// <param name="setFirst">If true, scroll to make the target on the top/left; If false, scroll to make the target any position in view.</param>
public void ScrollToView(GObject obj, bool ani, bool setFirst)
{
_owner.EnsureBoundsCorrect();
if (_needRefresh)
Refresh();
Rect rect = new Rect(obj.x, obj.y, obj.width, obj.height);
if (obj.parent != _owner)
rect = obj.parent.TransformRect(rect, _owner);
ScrollToView(rect, ani, setFirst);
}
/// <summary>
///
/// </summary>
/// <param name="rect">Rect in local coordinates</param>
/// <param name="ani">If moving to target position with animation</param>
/// <param name="setFirst">If true, scroll to make the target on the top/left; If false, scroll to make the target any position in view.</param>
public void ScrollToView(Rect rect, bool ani, bool setFirst)
{
_owner.EnsureBoundsCorrect();
if (_needRefresh)
Refresh();
if (_overlapSize.y > 0)
{
float bottom = _yPos + _viewSize.y;
if (setFirst || rect.y <= _yPos || rect.height >= _viewSize.y)
{
if (!setFirst && rect.yMax >= bottom) //if an item size is large than viewSize, dont scroll
return;
if (_pageMode)
this.SetPosY(Mathf.Floor(rect.y / _pageSize.y) * _pageSize.y, ani);
else
SetPosY(rect.y, ani);
}
else if (rect.yMax > bottom)
{
if (_pageMode)
this.SetPosY(Mathf.Floor(rect.y / _pageSize.y) * _pageSize.y, ani);
else if (rect.height <= _viewSize.y / 2)
SetPosY(rect.y + rect.height * 2 - _viewSize.y, ani);
else
SetPosY(rect.y + Mathf.Min(rect.height - _viewSize.y, 0), ani);
}
}
if (_overlapSize.x > 0)
{
float right = _xPos + _viewSize.x;
if (setFirst || rect.x <= _xPos || rect.width >= _viewSize.x)
{
if (!setFirst && rect.xMax >= right) //if an item size is large than viewSize, dont scroll
return;
if (_pageMode)
this.SetPosX(Mathf.Floor(rect.x / _pageSize.x) * _pageSize.x, ani);
SetPosX(rect.x, ani);
}
else if (rect.xMax > right)
{
if (_pageMode)
this.SetPosX(Mathf.Floor(rect.x / _pageSize.x) * _pageSize.x, ani);
else if (rect.width <= _viewSize.x / 2)
SetPosX(rect.x + rect.width * 2 - _viewSize.x, ani);
else
SetPosX(rect.x + Mathf.Min(rect.width - _viewSize.x, 0), ani);
}
}
if (!ani && _needRefresh)
Refresh();
}
/// <summary>
///
/// </summary>
/// <param name="obj">obj must be the direct child of this container</param>
/// <returns></returns>
public bool IsChildInView(GObject obj)
{
if (_overlapSize.y > 0)
{
float dist = obj.y + _container.y;
if (dist <= -obj.height || dist >= _viewSize.y)
return false;
}
if (_overlapSize.x > 0)
{
float dist = obj.x + _container.x;
if (dist <= -obj.width || dist >= _viewSize.x)
return false;
}
return true;
}
/// <summary>
/// 当滚动面板处于拖拽滚动状态或即将进入拖拽状态时,可以调用此方法停止或禁止本次拖拽。
/// </summary>
public void CancelDragging()
{
Stage.inst.RemoveTouchMonitor(_owner.rootContainer);
if (draggingPane == this)
draggingPane = null;
_gestureFlag = 0;
_dragged = false;
}
/// <summary>
/// 设置Header固定显示。如果size为0则取消固定显示。
/// </summary>
/// <param name="size">Header显示的大小</param>
public void LockHeader(int size)
{
if (_headerLockedSize == size)
return;
_headerLockedSize = size;
if (!isDispatching("onPullDownRelease") && _container.xy[_refreshBarAxis] >= 0)
{
_tweenStart = _container.xy;
_tweenChange = Vector2.zero;
_tweenChange[_refreshBarAxis] = _headerLockedSize - _tweenStart[_refreshBarAxis];
_tweenDuration = new Vector2(TWEEN_TIME_DEFAULT, TWEEN_TIME_DEFAULT);
StartTween(2);
}
}
/// <summary>
/// 设置Footer固定显示。如果size为0则取消固定显示。
/// </summary>
/// <param name="size"></param>
public void LockFooter(int size)
{
if (_footerLockedSize == size)
return;
_footerLockedSize = size;
if (!isDispatching("onPullUpRelease") && _container.xy[_refreshBarAxis] <= -_overlapSize[_refreshBarAxis])
{
_tweenStart = _container.xy;
_tweenChange = Vector2.zero;
float max = _overlapSize[_refreshBarAxis];
if (max == 0)
max = Mathf.Max(_contentSize[_refreshBarAxis] + _footerLockedSize - _viewSize[_refreshBarAxis], 0);
else
max += _footerLockedSize;
_tweenChange[_refreshBarAxis] = -max - _tweenStart[_refreshBarAxis];
_tweenDuration = new Vector2(TWEEN_TIME_DEFAULT, TWEEN_TIME_DEFAULT);
StartTween(2);
}
}
internal void OnOwnerSizeChanged()
{
SetSize(_owner.width, _owner.height);
PosChanged(false);
}
internal void HandleControllerChanged(Controller c)
{
if (_pageController == c)
{
if (_scrollType == ScrollType.Horizontal)
this.SetCurrentPageX(c.selectedIndex, true);
else
this.SetCurrentPageY(c.selectedIndex, true);
}
}
void UpdatePageController()
{
if (_pageController != null && !_pageController.changing)
{
int index;
if (_scrollType == ScrollType.Horizontal)
index = this.currentPageX;
else
index = this.currentPageY;
if (index < _pageController.pageCount)
{
Controller c = _pageController;
_pageController = null; //防止HandleControllerChanged的调用
c.selectedIndex = index;
_pageController = c;
}
}
}
internal void AdjustMaskContainer()
{
float mx, my;
if (_displayOnLeft && _vtScrollBar != null && !_floating)
mx = Mathf.FloorToInt(_owner.margin.left + _vtScrollBar.width);
else
mx = _owner.margin.left;
my = _owner.margin.top;
mx += _owner._alignOffset.x;
my += _owner._alignOffset.y;
_maskContainer.SetXY(mx, my);
}
void SetSize(float aWidth, float aHeight)
{
AdjustMaskContainer();
if (_hzScrollBar != null)
{
_hzScrollBar.y = aHeight - _hzScrollBar.height;
if (_vtScrollBar != null)
{
_hzScrollBar.width = aWidth - _vtScrollBar.width - _scrollBarMargin.left - _scrollBarMargin.right;
if (_displayOnLeft)
_hzScrollBar.x = _scrollBarMargin.left + _vtScrollBar.width;
else
_hzScrollBar.x = _scrollBarMargin.left;
}
else
{
_hzScrollBar.width = aWidth - _scrollBarMargin.left - _scrollBarMargin.right;
_hzScrollBar.x = _scrollBarMargin.left;
}
}
if (_vtScrollBar != null)
{
if (!_displayOnLeft)
_vtScrollBar.x = aWidth - _vtScrollBar.width;
if (_hzScrollBar != null)
_vtScrollBar.height = aHeight - _hzScrollBar.height - _scrollBarMargin.top - _scrollBarMargin.bottom;
else
_vtScrollBar.height = aHeight - _scrollBarMargin.top - _scrollBarMargin.bottom;
_vtScrollBar.y = _scrollBarMargin.top;
}
_viewSize.x = aWidth;
_viewSize.y = aHeight;
if (_hzScrollBar != null && !_floating)
_viewSize.y -= _hzScrollBar.height;
if (_vtScrollBar != null && !_floating)
_viewSize.x -= _vtScrollBar.width;
_viewSize.x -= (_owner.margin.left + _owner.margin.right);
_viewSize.y -= (_owner.margin.top + _owner.margin.bottom);
_viewSize.x = Mathf.Max(1, _viewSize.x);
_viewSize.y = Mathf.Max(1, _viewSize.y);
_pageSize.x = _viewSize.x;
_pageSize.y = _viewSize.y;
HandleSizeChanged();
}
internal void SetContentSize(float aWidth, float aHeight)
{
if (Mathf.Approximately(_contentSize.x, aWidth) && Mathf.Approximately(_contentSize.y, aHeight))
return;
_contentSize.x = aWidth;
_contentSize.y = aHeight;
HandleSizeChanged();
}
/// <summary>
/// 内部使用。由虚拟列表调用。在滚动时修改显示内容的大小,需要进行修正,避免滚动跳跃。
/// </summary>
/// <param name="deltaWidth"></param>
/// <param name="deltaHeight"></param>
/// <param name="deltaPosX"></param>
/// <param name="deltaPosY"></param>
internal void ChangeContentSizeOnScrolling(float deltaWidth, float deltaHeight, float deltaPosX, float deltaPosY)
{
bool isRightmost = _xPos == _overlapSize.x;
bool isBottom = _yPos == _overlapSize.y;
_contentSize.x += deltaWidth;
_contentSize.y += deltaHeight;
HandleSizeChanged();
if (_tweening == 1)
{
//如果原来滚动位置是贴边,加入处理继续贴边。
if (deltaWidth != 0 && isRightmost && _tweenChange.x < 0)
{
_xPos = _overlapSize.x;
_tweenChange.x = -_xPos - _tweenStart.x;
}
if (deltaHeight != 0 && isBottom && _tweenChange.y < 0)
{
_yPos = _overlapSize.y;
_tweenChange.y = -_yPos - _tweenStart.y;
}
}
else if (_tweening == 2)
{
//重新调整起始位置,确保能够顺滑滚下去
if (deltaPosX != 0)
{
_container.x -= deltaPosX;
_tweenStart.x -= deltaPosX;
_xPos = -_container.x;
}
if (deltaPosY != 0)
{
_container.y -= deltaPosY;
_tweenStart.y -= deltaPosY;
_yPos = -_container.y;
}
}
else if (_dragged)
{
if (deltaPosX != 0)
{
_container.x -= deltaPosX;
_containerPos.x -= deltaPosX;
_xPos = -_container.x;
}
if (deltaPosY != 0)
{
_container.y -= deltaPosY;
_containerPos.y -= deltaPosY;
_yPos = -_container.y;
}
}
else
{
//如果原来滚动位置是贴边,加入处理继续贴边。
if (deltaWidth != 0 && isRightmost)
{
_xPos = _overlapSize.x;
_container.x = -_xPos;
}
if (deltaHeight != 0 && isBottom)
{
_yPos = _overlapSize.y;
_container.y = -_yPos;
}
}
if (_pageMode)
UpdatePageController();
}
void HandleSizeChanged()
{
if (_displayInDemand)
{
_vScrollNone = _contentSize.y <= _viewSize.y;
_hScrollNone = _contentSize.x <= _viewSize.x;
if (_vtScrollBar != null && _hzScrollBar != null)
{
if (!_hScrollNone)
_vtScrollBar.height = _owner.height - _hzScrollBar.height - _scrollBarMargin.top - _scrollBarMargin.bottom;
else
_vtScrollBar.height = _owner.height - _scrollBarMargin.top - _scrollBarMargin.bottom;
if (!_vScrollNone)
_hzScrollBar.width = _owner.width - _vtScrollBar.width - _scrollBarMargin.left - _scrollBarMargin.right;
else
_hzScrollBar.width = _owner.width - _scrollBarMargin.left - _scrollBarMargin.right;
}
}
if (_vtScrollBar != null)
{
if (_contentSize.y == 0)
_vtScrollBar.SetDisplayPerc(0);
else
_vtScrollBar.SetDisplayPerc(Mathf.Min(1, _viewSize.y / _contentSize.y));
}
if (_hzScrollBar != null)
{
if (_contentSize.x == 0)
_hzScrollBar.SetDisplayPerc(0);
else
_hzScrollBar.SetDisplayPerc(Mathf.Min(1, _viewSize.x / _contentSize.x));
}
UpdateScrollBarVisible();
if (!_maskDisabled)
{
Rect rect = new Rect(-_owner._alignOffset.x, -_owner._alignOffset.y, _viewSize.x, _viewSize.y);
if (_vScrollNone && _vtScrollBar != null)
rect.width += _vtScrollBar.width;
if (_hScrollNone && _hzScrollBar != null)
rect.height += _hzScrollBar.height;
if (_dontClipMargin)
{
rect.x -= _owner.margin.left;
rect.width += (_owner.margin.left + _owner.margin.right);
rect.y -= _owner.margin.top;
rect.height += (_owner.margin.top + _owner.margin.bottom);
}
_maskContainer.clipRect = rect;
}
if (_scrollType == ScrollType.Horizontal || _scrollType == ScrollType.Both)
_overlapSize.x = Mathf.CeilToInt(Math.Max(0, _contentSize.x - _viewSize.x));
else
_overlapSize.x = 0;
if (_scrollType == ScrollType.Vertical || _scrollType == ScrollType.Both)
_overlapSize.y = Mathf.CeilToInt(Math.Max(0, _contentSize.y - _viewSize.y));
else
_overlapSize.y = 0;
//边界检查
_xPos = Mathf.Clamp(_xPos, 0, _overlapSize.x);
_yPos = Mathf.Clamp(_yPos, 0, _overlapSize.y);
float max = _overlapSize[_refreshBarAxis];
if (max == 0)
max = Mathf.Max(_contentSize[_refreshBarAxis] + _footerLockedSize - _viewSize[_refreshBarAxis], 0);
else
max += _footerLockedSize;
if (_refreshBarAxis == 0)
_container.SetXY(Mathf.Clamp(_container.x, -max, _headerLockedSize), Mathf.Clamp(_container.y, -_overlapSize.y, 0));
else
_container.SetXY(Mathf.Clamp(_container.x, -_overlapSize.x, 0), Mathf.Clamp(_container.y, -max, _headerLockedSize));
if (_header != null)
{
if (_refreshBarAxis == 0)
_header.height = _viewSize.y;
else
_header.width = _viewSize.x;
}
if (_footer != null)
{
if (_refreshBarAxis == 0)
_footer.height = _viewSize.y;
else
_footer.width = _viewSize.x;
}
UpdateScrollBarPos();
if (_pageMode)
UpdatePageController();
}
private void PosChanged(bool ani)
{
//只要有1处要求不要缓动那就不缓动
if (_aniFlag == 0)
_aniFlag = ani ? 1 : -1;
else if (_aniFlag == 1 && !ani)
_aniFlag = -1;
_needRefresh = true;
UpdateContext.OnBegin -= _refreshDelegate;
UpdateContext.OnBegin += _refreshDelegate;
}
private void Refresh()
{
_needRefresh = false;
UpdateContext.OnBegin -= _refreshDelegate;
if (_owner.displayObject == null || _owner.displayObject.isDisposed)
return;
if (_pageMode || _snapToItem)
{
Vector2 pos = new Vector2(-_xPos, -_yPos);
AlignPosition(ref pos, false);
_xPos = -pos.x;
_yPos = -pos.y;
}
Refresh2();
_onScroll.Call();
if (_needRefresh) //在onScroll事件里开发者可能修改位置这里再刷新一次避免闪烁
{
_needRefresh = false;
UpdateContext.OnBegin -= _refreshDelegate;
Refresh2();
}
UpdateScrollBarPos();
_aniFlag = 0;
}
void Refresh2()
{
if (_aniFlag == 1 && !_dragged)
{
Vector2 pos = new Vector2();
if (_overlapSize.x > 0)
pos.x = -(int)_xPos;
else
{
if (_container.x != 0)
_container.x = 0;
pos.x = 0;
}
if (_overlapSize.y > 0)
pos.y = -(int)_yPos;
else
{
if (_container.y != 0)
_container.y = 0;
pos.y = 0;
}
if (pos.x != _container.x || pos.y != _container.y)
{
_tweenDuration = new Vector2(TWEEN_TIME_GO, TWEEN_TIME_GO);
_tweenStart = _container.xy;
_tweenChange = pos - _tweenStart;
StartTween(1);
}
else if (_tweening != 0)
KillTween();
}
else
{
if (_tweening != 0)
KillTween();
_container.SetXY((int)-_xPos, (int)-_yPos);
LoopCheckingCurrent();
}
if (_pageMode)
UpdatePageController();
}
private void __touchBegin(EventContext context)
{
if (!_touchEffect)
return;
InputEvent evt = context.inputEvent;
if (evt.button != 0)
return;
context.CaptureTouch();
Vector2 pt = _owner.GlobalToLocal(evt.position);
if (_tweening != 0)
{
KillTween();
Stage.inst.CancelClick(evt.touchId);
//立刻停止惯性滚动可能位置不对齐设定这个标志使touchEnd时归位
_dragged = true;
}
else
_dragged = false;
_containerPos = _container.xy;
_beginTouchPos = _lastTouchPos = pt;
_lastTouchGlobalPos = evt.position;
_isHoldAreaDone = false;
_velocity = Vector2.zero;
_velocityScale = 1;
_lastMoveTime = Time.unscaledTime;
}
private void __touchMove(EventContext context)
{
if (!_touchEffect || draggingPane != null && draggingPane != this || GObject.draggingObject != null) //已经有其他拖动
return;
InputEvent evt = context.inputEvent;
Vector2 pt = _owner.GlobalToLocal(evt.position);
if (float.IsNaN(pt.x))
return;
int sensitivity;
if (Stage.touchScreen)
sensitivity = UIConfig.touchScrollSensitivity;
else
sensitivity = 8;
float diff;
bool sv = false, sh = false;
if (_scrollType == ScrollType.Vertical)
{
if (!_isHoldAreaDone)
{
//表示正在监测垂直方向的手势
_gestureFlag |= 1;
diff = Mathf.Abs(_beginTouchPos.y - pt.y);
if (diff < sensitivity)
return;
if ((_gestureFlag & 2) != 0) //已经有水平方向的手势在监测,那么我们用严格的方式检查是不是按垂直方向移动,避免冲突
{
float diff2 = Mathf.Abs(_beginTouchPos.x - pt.x);
if (diff < diff2) //不通过则不允许滚动了
return;
}
}
sv = true;
}
else if (_scrollType == ScrollType.Horizontal)
{
if (!_isHoldAreaDone)
{
_gestureFlag |= 2;
diff = Mathf.Abs(_beginTouchPos.x - pt.x);
if (diff < sensitivity)
return;
if ((_gestureFlag & 1) != 0)
{
float diff2 = Mathf.Abs(_beginTouchPos.y - pt.y);
if (diff < diff2)
return;
}
}
sh = true;
}
else
{
_gestureFlag = 3;
if (!_isHoldAreaDone)
{
diff = Mathf.Abs(_beginTouchPos.y - pt.y);
if (diff < sensitivity)
{
diff = Mathf.Abs(_beginTouchPos.x - pt.x);
if (diff < sensitivity)
return;
}
}
sv = sh = true;
}
Vector2 newPos = _containerPos + pt - _beginTouchPos;
newPos.x = (int)newPos.x;
newPos.y = (int)newPos.y;
if (sv)
{
if (newPos.y > 0)
{
if (!_bouncebackEffect)
_container.y = 0;
else if (_header != null && _header.maxHeight != 0)
_container.y = (int)Mathf.Min(newPos.y * 0.5f, _header.maxHeight);
else
_container.y = (int)Mathf.Min(newPos.y * 0.5f, _viewSize.y * PULL_RATIO);
}
else if (newPos.y < -_overlapSize.y)
{
if (!_bouncebackEffect)
_container.y = -_overlapSize.y;
else if (_footer != null && _footer.maxHeight > 0)
_container.y = (int)Mathf.Max((newPos.y + _overlapSize.y) * 0.5f, -_footer.maxHeight) - _overlapSize.y;
else
_container.y = (int)Mathf.Max((newPos.y + _overlapSize.y) * 0.5f, -_viewSize.y * PULL_RATIO) - _overlapSize.y;
}
else
_container.y = newPos.y;
}
if (sh)
{
if (newPos.x > 0)
{
if (!_bouncebackEffect)
_container.x = 0;
else if (_header != null && _header.maxWidth != 0)
_container.x = (int)Mathf.Min(newPos.x * 0.5f, _header.maxWidth);
else
_container.x = (int)Mathf.Min(newPos.x * 0.5f, _viewSize.x * PULL_RATIO);
}
else if (newPos.x < 0 - _overlapSize.x)
{
if (!_bouncebackEffect)
_container.x = -_overlapSize.x;
else if (_footer != null && _footer.maxWidth > 0)
_container.x = (int)Mathf.Max((newPos.x + _overlapSize.x) * 0.5f, -_footer.maxWidth) - _overlapSize.x;
else
_container.x = (int)Mathf.Max((newPos.x + _overlapSize.x) * 0.5f, -_viewSize.x * PULL_RATIO) - _overlapSize.x;
}
else
_container.x = newPos.x;
}
//更新速度
float deltaTime = Time.unscaledDeltaTime;
float elapsed = (Time.unscaledTime - _lastMoveTime) * 60 - 1;
if (elapsed > 1) //速度衰减
_velocity = _velocity * Mathf.Pow(0.833f, elapsed);
Vector2 deltaPosition = pt - _lastTouchPos;
if (!sh)
deltaPosition.x = 0;
if (!sv)
deltaPosition.y = 0;
_velocity = Vector2.Lerp(_velocity, deltaPosition / deltaTime, deltaTime * 10);
/*速度计算使用的是本地位移,但在后续的惯性滚动判断中需要用到屏幕位移,所以这里要记录一个位移的比例。
*后续的处理要使用这个比例但不使用坐标转换的方法的原因是在曲面UI等异形UI中还无法简单地进行屏幕坐标和本地坐标的转换。
*/
Vector2 deltaGlobalPosition = _lastTouchGlobalPos - evt.position;
if (deltaPosition.x != 0)
_velocityScale = Mathf.Abs(deltaGlobalPosition.x / deltaPosition.x);
else if (deltaPosition.y != 0)
_velocityScale = Mathf.Abs(deltaGlobalPosition.y / deltaPosition.y);
_lastTouchPos = pt;
_lastTouchGlobalPos = evt.position;
_lastMoveTime = Time.unscaledTime;
//同步更新pos值
if (_overlapSize.x > 0)
_xPos = Mathf.Clamp(-_container.x, 0, _overlapSize.x);
if (_overlapSize.y > 0)
_yPos = Mathf.Clamp(-_container.y, 0, _overlapSize.y);
//循环滚动特别检查
if (_loop != 0)
{
newPos = _container.xy;
if (LoopCheckingCurrent())
_containerPos += _container.xy - newPos;
}
draggingPane = this;
_isHoldAreaDone = true;
_dragged = true;
UpdateScrollBarPos();
UpdateScrollBarVisible();
if (_pageMode)
UpdatePageController();
_onScroll.Call();
}
private void __touchEnd(EventContext context)
{
if (draggingPane == this)
draggingPane = null;
_gestureFlag = 0;
if (!_dragged || !_touchEffect)
{
_dragged = false;
return;
}
_dragged = false;
_tweenStart = _container.xy;
Vector2 endPos = _tweenStart;
bool flag = false;
if (_container.x > 0)
{
endPos.x = 0;
flag = true;
}
else if (_container.x < -_overlapSize.x)
{
endPos.x = -_overlapSize.x;
flag = true;
}
if (_container.y > 0)
{
endPos.y = 0;
flag = true;
}
else if (_container.y < -_overlapSize.y)
{
endPos.y = -_overlapSize.y;
flag = true;
}
if (flag)
{
_tweenChange = endPos - _tweenStart;
if (_tweenChange.x < -UIConfig.touchDragSensitivity || _tweenChange.y < -UIConfig.touchDragSensitivity)
DispatchEvent("onPullDownRelease", null);
else if (_tweenChange.x > UIConfig.touchDragSensitivity || _tweenChange.y > UIConfig.touchDragSensitivity)
DispatchEvent("onPullUpRelease", null);
if (_headerLockedSize > 0 && endPos[_refreshBarAxis] == 0)
{
endPos[_refreshBarAxis] = _headerLockedSize;
_tweenChange = endPos - _tweenStart;
}
else if (_footerLockedSize > 0 && endPos[_refreshBarAxis] == -_overlapSize[_refreshBarAxis])
{
float max = _overlapSize[_refreshBarAxis];
if (max == 0)
max = Mathf.Max(_contentSize[_refreshBarAxis] + _footerLockedSize - _viewSize[_refreshBarAxis], 0);
else
max += _footerLockedSize;
endPos[_refreshBarAxis] = -max;
_tweenChange = endPos - _tweenStart;
}
_tweenDuration.Set(TWEEN_TIME_DEFAULT, TWEEN_TIME_DEFAULT);
}
else
{
//更新速度
if (!_inertiaDisabled)
{
float elapsed = (Time.unscaledTime - _lastMoveTime) * 60 - 1;
if (elapsed > 1)
_velocity = _velocity * Mathf.Pow(0.833f, elapsed);
//根据速度计算目标位置和需要时间
endPos = UpdateTargetAndDuration(_tweenStart);
}
else
_tweenDuration.Set(TWEEN_TIME_DEFAULT, TWEEN_TIME_DEFAULT);
Vector2 oldChange = endPos - _tweenStart;
//调整目标位置
LoopCheckingTarget(ref endPos);
if (_pageMode || _snapToItem)
AlignPosition(ref endPos, true);
_tweenChange = endPos - _tweenStart;
if (_tweenChange.x == 0 && _tweenChange.y == 0)
{
UpdateScrollBarVisible();
return;
}
//如果目标位置已调整,随之调整需要时间
if (_pageMode || _snapToItem)
{
FixDuration(0, oldChange.x);
FixDuration(1, oldChange.y);
}
}
StartTween(2);
}
private void __mouseWheel(EventContext context)
{
if (!_mouseWheelEnabled)
return;
InputEvent evt = context.inputEvent;
float delta = evt.mouseWheelDelta / Stage.devicePixelRatio;
if (_snapToItem && Mathf.Abs(delta) < 1)
delta = Mathf.Sign(delta);
if (_overlapSize.x > 0 && _overlapSize.y == 0)
{
float step = _pageMode ? _pageSize.x : _scrollStep;
SetPosX(_xPos + step * delta, false);
}
else
{
float step = _pageMode ? _pageSize.y : _scrollStep;
SetPosY(_yPos + step * delta, false);
}
}
private void __rollOver()
{
_hover = true;
UpdateScrollBarVisible();
}
private void __rollOut()
{
_hover = false;
UpdateScrollBarVisible();
}
internal void UpdateClipSoft()
{
Vector2 softness = _owner.clipSoftness;
if (softness.x != 0 || softness.y != 0)
{
_maskContainer.clipSoftness = new Vector4(
(_container.x >= 0 || !_softnessOnTopOrLeftSide) ? 0 : softness.x,
(_container.y >= 0 || !_softnessOnTopOrLeftSide) ? 0 : softness.y,
(-_container.x - _overlapSize.x >= 0) ? 0 : softness.x,
(-_container.y - _overlapSize.y >= 0) ? 0 : softness.y);
}
else
_maskContainer.clipSoftness = null;
}
private void UpdateScrollBarPos()
{
if (_vtScrollBar != null)
_vtScrollBar.setScrollPerc(_overlapSize.y == 0 ? 0 : Mathf.Clamp(-_container.y, 0, _overlapSize.y) / _overlapSize.y);
if (_hzScrollBar != null)
_hzScrollBar.setScrollPerc(_overlapSize.x == 0 ? 0 : Mathf.Clamp(-_container.x, 0, _overlapSize.x) / _overlapSize.x);
UpdateClipSoft();
CheckRefreshBar();
}
public void UpdateScrollBarVisible()
{
if (_vtScrollBar != null)
{
if (_viewSize.y <= _vtScrollBar.minSize || _vScrollNone)
_vtScrollBar.displayObject.visible = false;
else
UpdateScrollBarVisible2(_vtScrollBar);
}
if (_hzScrollBar != null)
{
if (_viewSize.x <= _hzScrollBar.minSize || _hScrollNone)
_hzScrollBar.displayObject.visible = false;
else
UpdateScrollBarVisible2(_hzScrollBar);
}
}
private void UpdateScrollBarVisible2(GScrollBar bar)
{
if (_scrollBarDisplayAuto)
GTween.Kill(bar, TweenPropType.Alpha, false);
if (_scrollBarDisplayAuto && !_hover && _tweening == 0 && !_dragged && !bar.gripDragging)
{
if (bar.displayObject.visible)
GTween.To(1, 0, 0.5f).SetDelay(0.5f).OnComplete(_hideScrollBarDelegate).SetTarget(bar, TweenPropType.Alpha);
}
else
{
bar.alpha = 1;
bar.displayObject.visible = true;
}
}
private void __barTweenComplete(GTweener tweener)
{
GObject bar = (GObject)tweener.target;
bar.alpha = 1;
bar.displayObject.visible = false;
}
float GetLoopPartSize(float division, int axis)
{
return (_contentSize[axis] + (axis == 0 ? ((GList)_owner).columnGap : ((GList)_owner).lineGap)) / division;
}
/// <summary>
/// 对当前的滚动位置进行循环滚动边界检查。当到达边界时,回退一半内容区域(循环滚动内容大小通常是真实内容大小的偶数倍)。
/// </summary>
/// <returns></returns>
bool LoopCheckingCurrent()
{
bool changed = false;
if (_loop == 1 && _overlapSize.x > 0)
{
if (_xPos < 0.001f)
{
_xPos += GetLoopPartSize(2, 0);
changed = true;
}
else if (_xPos >= _overlapSize.x)
{
_xPos -= GetLoopPartSize(2, 0);
changed = true;
}
}
else if (_loop == 2 && _overlapSize.y > 0)
{
if (_yPos < 0.001f)
{
_yPos += GetLoopPartSize(2, 1);
changed = true;
}
else if (_yPos >= _overlapSize.y)
{
_yPos -= GetLoopPartSize(2, 1);
changed = true;
}
}
if (changed)
_container.SetXY((int)-_xPos, (int)-_yPos);
return changed;
}
/// <summary>
/// 对目标位置进行循环滚动边界检查。当到达边界时,回退一半内容区域(循环滚动内容大小通常是真实内容大小的偶数倍)。
/// </summary>
/// <param name="endPos"></param>
void LoopCheckingTarget(ref Vector2 endPos)
{
if (_loop == 1)
LoopCheckingTarget(ref endPos, 0);
if (_loop == 2)
LoopCheckingTarget(ref endPos, 1);
}
void LoopCheckingTarget(ref Vector2 endPos, int axis)
{
if (endPos[axis] > 0)
{
float halfSize = GetLoopPartSize(2, axis);
float tmp = _tweenStart[axis] - halfSize;
if (tmp <= 0 && tmp >= -_overlapSize[axis])
{
endPos[axis] -= halfSize;
_tweenStart[axis] = tmp;
}
}
else if (endPos[axis] < -_overlapSize[axis])
{
float halfSize = GetLoopPartSize(2, axis);
float tmp = _tweenStart[axis] + halfSize;
if (tmp <= 0 && tmp >= -_overlapSize[axis])
{
endPos[axis] += halfSize;
_tweenStart[axis] = tmp;
}
}
}
void LoopCheckingNewPos(ref float value, int axis)
{
if (_overlapSize[axis] == 0)
return;
float pos = axis == 0 ? _xPos : _yPos;
bool changed = false;
if (value < 0.001f)
{
value += GetLoopPartSize(2, axis);
if (value > pos)
{
float v = GetLoopPartSize(6, axis);
v = Mathf.CeilToInt((value - pos) / v) * v;
pos = Mathf.Clamp(pos + v, 0, _overlapSize[axis]);
changed = true;
}
}
else if (value >= _overlapSize[axis])
{
value -= GetLoopPartSize(2, axis);
if (value < pos)
{
float v = GetLoopPartSize(6, axis);
v = Mathf.CeilToInt((pos - value) / v) * v;
pos = Mathf.Clamp(pos - v, 0, _overlapSize[axis]);
changed = true;
}
}
if (changed)
{
if (axis == 0)
_container.x = -(int)pos;
else
_container.y = -(int)pos;
}
}
/// <summary>
/// 从oldPos滚动至pos调整pos位置对齐页面、对齐item等如果需要
/// </summary>
/// <param name="pos"></param>
/// <param name="inertialScrolling"></param>
void AlignPosition(ref Vector2 pos, bool inertialScrolling)
{
if (_pageMode)
{
pos.x = AlignByPage(pos.x, 0, inertialScrolling);
pos.y = AlignByPage(pos.y, 1, inertialScrolling);
}
else if (_snapToItem)
{
float tmpX = -pos.x;
float tmpY = -pos.y;
float xDir = 0;
float yDir = 0;
if (inertialScrolling)
{
xDir = pos.x - _containerPos.x;
yDir = pos.y - _containerPos.y;
}
_owner.GetSnappingPositionWithDir(ref tmpX, ref tmpY, xDir, yDir);
if (pos.x < 0 && pos.x > -_overlapSize.x)
pos.x = -tmpX;
if (pos.y < 0 && pos.y > -_overlapSize.y)
pos.y = -tmpY;
}
}
/// <summary>
/// 从oldPos滚动至pos调整目标位置到对齐页面。
/// </summary>
/// <param name="pos"></param>
/// <param name="axis"></param>
/// <param name="inertialScrolling"></param>
/// <returns></returns>
float AlignByPage(float pos, int axis, bool inertialScrolling)
{
int page;
if (pos > 0)
page = 0;
else if (pos < -_overlapSize[axis])
page = Mathf.CeilToInt(_contentSize[axis] / _pageSize[axis]) - 1;
else
{
page = Mathf.FloorToInt(-pos / _pageSize[axis]);
float change = inertialScrolling ? (pos - _containerPos[axis]) : (pos - _container.xy[axis]);
float testPageSize = Mathf.Min(_pageSize[axis], _contentSize[axis] - (page + 1) * _pageSize[axis]);
float delta = -pos - page * _pageSize[axis];
//页面吸附策略
if (Mathf.Abs(change) > _pageSize[axis])//如果滚动距离超过1页,则需要超过页面的一半,才能到更下一页
{
if (delta > testPageSize * 0.5f)
page++;
}
else //否则只需要页面的1/3当然需要考虑到左移和右移的情况
{
if (delta > testPageSize * (change < 0 ? UIConfig.defaultScrollPagingThreshold : (1 - UIConfig.defaultScrollPagingThreshold)))
page++;
}
//重新计算终点
pos = -page * _pageSize[axis];
if (pos < -_overlapSize[axis]) //最后一页未必有pageSize那么大
pos = -_overlapSize[axis];
}
//惯性滚动模式下,会增加判断尽量不要滚动超过一页
if (inertialScrolling)
{
float oldPos = _tweenStart[axis];
int oldPage;
if (oldPos > 0)
oldPage = 0;
else if (oldPos < -_overlapSize[axis])
oldPage = Mathf.CeilToInt(_contentSize[axis] / _pageSize[axis]) - 1;
else
oldPage = Mathf.FloorToInt(-oldPos / _pageSize[axis]);
int startPage = Mathf.FloorToInt(-_containerPos[axis] / _pageSize[axis]);
if (Mathf.Abs(page - startPage) > 1 && Mathf.Abs(oldPage - startPage) <= 1)
{
if (page > startPage)
page = startPage + 1;
else
page = startPage - 1;
pos = -page * _pageSize[axis];
}
}
return pos;
}
/// <summary>
/// 根据当前速度,计算滚动的目标位置,以及到达时间。
/// </summary>
/// <param name="orignPos"></param>
/// <returns></returns>
Vector2 UpdateTargetAndDuration(Vector2 orignPos)
{
Vector2 ret = Vector2.zero;
ret.x = UpdateTargetAndDuration(orignPos.x, 0);
ret.y = UpdateTargetAndDuration(orignPos.y, 1);
return ret;
}
float UpdateTargetAndDuration(float pos, int axis)
{
float v = _velocity[axis];
float duration = 0;
if (pos > 0)
pos = 0;
else if (pos < -_overlapSize[axis])
pos = -_overlapSize[axis];
else
{
//以屏幕像素为基准
float v2 = Mathf.Abs(v) * _velocityScale;
//在移动设备上需要对不同分辨率做一个适配我们的速度判断以1136分辨率为基准
if (Stage.touchScreen)
v2 *= 1136f / Mathf.Max(Screen.width, Screen.height);
//这里有一些阈值的处理,因为在低速内,不希望产生较大的滚动(甚至不滚动)
float ratio = 0;
if (_pageMode || !Stage.touchScreen)
{
if (v2 > 500)
ratio = Mathf.Pow((v2 - 500) / 500, 2);
}
else
{
if (v2 > 1000)
ratio = Mathf.Pow((v2 - 1000) / 1000, 2);
}
if (ratio != 0)
{
if (ratio > 1)
ratio = 1;
v2 *= ratio;
v *= ratio;
_velocity[axis] = v;
//算法v*_decelerationRate的n次幂= 60即在n帧后速度降为60假设每秒60帧
duration = Mathf.Log(60 / v2, _decelerationRate) / 60;
//计算距离要使用本地速度
//理论公式貌似滚动的距离不够,改为经验公式
//float change = (int)((v/ 60 - 1) / (1 - _decelerationRate));
float change = (int)(v * duration * 0.4f);
pos += change;
}
}
if (duration < TWEEN_TIME_DEFAULT)
duration = TWEEN_TIME_DEFAULT;
_tweenDuration[axis] = duration;
return pos;
}
/// <summary>
/// 根据修改后的tweenChange重新计算减速时间。
/// </summary>
void FixDuration(int axis, float oldChange)
{
if (_tweenChange[axis] == 0 || Mathf.Abs(_tweenChange[axis]) >= Mathf.Abs(oldChange))
return;
float newDuration = Mathf.Abs(_tweenChange[axis] / oldChange) * _tweenDuration[axis];
if (newDuration < TWEEN_TIME_DEFAULT)
newDuration = TWEEN_TIME_DEFAULT;
_tweenDuration[axis] = newDuration;
}
void StartTween(int type)
{
_tweenTime.Set(0, 0);
_tweening = type;
Timers.inst.AddUpdate(_tweenUpdateDelegate);
UpdateScrollBarVisible();
}
void KillTween()
{
if (_tweening == 1) //取消类型为1的tween需立刻设置到终点
{
_container.xy = _tweenStart + _tweenChange;
_onScroll.Call();
}
_tweening = 0;
Timers.inst.Remove(_tweenUpdateDelegate);
UpdateScrollBarVisible();
_onScrollEnd.Call();
}
void CheckRefreshBar()
{
if (_header == null && _footer == null)
return;
float pos = _container.xy[_refreshBarAxis];
if (_header != null)
{
if (pos > 0)
{
if (_header.displayObject.parent == null)
_maskContainer.AddChildAt(_header.displayObject, 0);
Vector2 vec;
vec = _header.size;
vec[_refreshBarAxis] = pos;
_header.size = vec;
}
else
{
if (_header.displayObject.parent != null)
_maskContainer.RemoveChild(_header.displayObject);
}
}
if (_footer != null)
{
float max = _overlapSize[_refreshBarAxis];
if (pos < -max || max == 0 && _footerLockedSize > 0)
{
if (_footer.displayObject.parent == null)
_maskContainer.AddChildAt(_footer.displayObject, 0);
Vector2 vec;
vec = _footer.xy;
if (max > 0)
vec[_refreshBarAxis] = pos + _contentSize[_refreshBarAxis];
else
vec[_refreshBarAxis] = Mathf.Max(Mathf.Min(pos + _viewSize[_refreshBarAxis], _viewSize[_refreshBarAxis] - _footerLockedSize), _viewSize[_refreshBarAxis] - _contentSize[_refreshBarAxis]);
_footer.xy = vec;
vec = _footer.size;
if (max > 0)
vec[_refreshBarAxis] = -max - pos;
else
vec[_refreshBarAxis] = _viewSize[_refreshBarAxis] - _footer.xy[_refreshBarAxis];
_footer.size = vec;
}
else
{
if (_footer.displayObject.parent != null)
_maskContainer.RemoveChild(_footer.displayObject);
}
}
}
void TweenUpdate(object param)
{
if (_owner.displayObject == null || _owner.displayObject.isDisposed)
{
Timers.inst.Remove(_tweenUpdateDelegate);
return;
}
float nx = RunTween(0);
float ny = RunTween(1);
_container.SetXY(nx, ny);
if (_tweening == 2)
{
if (_overlapSize.x > 0)
_xPos = Mathf.Clamp(-nx, 0, _overlapSize.x);
if (_overlapSize.y > 0)
_yPos = Mathf.Clamp(-ny, 0, _overlapSize.y);
if (_pageMode)
UpdatePageController();
}
if (_tweenChange.x == 0 && _tweenChange.y == 0)
{
_tweening = 0;
Timers.inst.Remove(_tweenUpdateDelegate);
LoopCheckingCurrent();
UpdateScrollBarPos();
UpdateScrollBarVisible();
_onScroll.Call();
_onScrollEnd.Call();
}
else
{
UpdateScrollBarPos();
_onScroll.Call();
}
}
float RunTween(int axis)
{
float newValue;
if (_tweenChange[axis] != 0)
{
_tweenTime[axis] += Time.unscaledDeltaTime;
if (_tweenTime[axis] >= _tweenDuration[axis])
{
newValue = _tweenStart[axis] + _tweenChange[axis];
_tweenChange[axis] = 0;
}
else
{
float ratio = EaseFunc(_tweenTime[axis], _tweenDuration[axis]);
newValue = _tweenStart[axis] + (int)(_tweenChange[axis] * ratio);
}
float threshold1 = 0;
float threshold2 = -_overlapSize[axis];
if (_headerLockedSize > 0 && _refreshBarAxis == axis)
threshold1 = _headerLockedSize;
if (_footerLockedSize > 0 && _refreshBarAxis == axis)
{
float max = _overlapSize[_refreshBarAxis];
if (max == 0)
max = Mathf.Max(_contentSize[_refreshBarAxis] + _footerLockedSize - _viewSize[_refreshBarAxis], 0);
else
max += _footerLockedSize;
threshold2 = -max;
}
if (_tweening == 2 && _bouncebackEffect)
{
if (newValue > 20 + threshold1 && _tweenChange[axis] > 0
|| newValue > threshold1 && _tweenChange[axis] == 0)//开始回弹
{
_tweenTime[axis] = 0;
_tweenDuration[axis] = TWEEN_TIME_DEFAULT;
_tweenChange[axis] = -newValue + threshold1;
_tweenStart[axis] = newValue;
}
else if (newValue < threshold2 - 20 && _tweenChange[axis] < 0
|| newValue < threshold2 && _tweenChange[axis] == 0)//开始回弹
{
_tweenTime[axis] = 0;
_tweenDuration[axis] = TWEEN_TIME_DEFAULT;
_tweenChange[axis] = threshold2 - newValue;
_tweenStart[axis] = newValue;
}
}
else
{
if (newValue > threshold1)
{
newValue = threshold1;
_tweenChange[axis] = 0;
}
else if (newValue < threshold2)
{
newValue = threshold2;
_tweenChange[axis] = 0;
}
}
}
else
newValue = _container.xy[axis];
return newValue;
}
static float EaseFunc(float t, float d)
{
return (t = t / d - 1) * t * t + 1;//cubicOut
}
}
}