You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2320 lines
77 KiB
2320 lines
77 KiB
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 (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 (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 |
|
} |
|
} |
|
}
|
|
|