550 lines
24 KiB
TypeScript
550 lines
24 KiB
TypeScript
|
/*
|
||
|
* @Author: steveJobs
|
||
|
* @Email: icipiqkm@gmail.com
|
||
|
* @Date: 2021-8-1 01:15:04
|
||
|
* @Last Modified by: steveJobs
|
||
|
* @Last Modified time: 2021-8-1 14:35:43
|
||
|
* @Description:
|
||
|
*/
|
||
|
import { _decorator, Node, EventTouch, Vec3, Vec2, ScrollView, EventHandler, PageView, EventMouse } from 'cc';
|
||
|
import { SuperLayout } from './super-layout';
|
||
|
const { ccclass, property } = _decorator;
|
||
|
const quintEaseOut = (time: number) => {
|
||
|
time -= 1;
|
||
|
return (time * time * time * time * time + 1)
|
||
|
};
|
||
|
const EPSILON = 1e-4
|
||
|
const OUT_OF_BOUNDARY_BREAKING_FACTOR = 0.015
|
||
|
const _tempVec2 = new Vec2()
|
||
|
export enum ScrollViewDirection {
|
||
|
HORIZONTAL,
|
||
|
VERTICAL,
|
||
|
NONE,
|
||
|
}
|
||
|
@ccclass('SuperScrollview')
|
||
|
export class SuperScrollview extends ScrollView {
|
||
|
private direction: ScrollViewDirection = ScrollViewDirection.NONE
|
||
|
private _layout!: SuperLayout
|
||
|
@property({
|
||
|
tooltip: "注意!向上传递事件只会发送当前滑动相反方向,如果开启horizontal则会发送vertical事件。如果开启vertical则会发送horizontal事件。同时开启horizontal和vertical 不会发送任何事件"
|
||
|
}) isTransmitEvent: boolean = false
|
||
|
@property pullRefresh: boolean = false
|
||
|
@property({
|
||
|
displayName: "顶部偏移量",
|
||
|
tooltip: "下拉时超过此偏移会发送下拉事件",
|
||
|
visible: function () { return (this as any).pullRefresh }
|
||
|
}) headerOutOffset: number = 200
|
||
|
@property({
|
||
|
displayName: "满足触发Header的倍数",
|
||
|
visible: function () { return (this as any).pullRefresh }
|
||
|
}) headerMultiple: number = 2
|
||
|
@property({
|
||
|
displayName: "底部偏移量",
|
||
|
tooltip: "上拉时超过此偏移会发送上拉事件",
|
||
|
visible: function () { return (this as any).pullRefresh }
|
||
|
}) footerOutOffset: number = 200
|
||
|
@property({
|
||
|
displayName: "满足触发Footer的倍数",
|
||
|
visible: function () { return (this as any).pullRefresh }
|
||
|
}) footerMultiple: number = 2
|
||
|
@property({
|
||
|
type: EventHandler,
|
||
|
visible: function () { return (this as any).pullRefresh }
|
||
|
}) headerEvents: EventHandler[] = []
|
||
|
@property({
|
||
|
type: EventHandler,
|
||
|
visible: function () { return (this as any).pullRefresh }
|
||
|
}) footerEvents: EventHandler[] = []
|
||
|
prevLocation: Vec2 = new Vec2()
|
||
|
location: Vec2 = new Vec2()
|
||
|
set autoScrolling(value: boolean) { this._autoScrolling = value }
|
||
|
private _touchBeganPosition = new Vec2()
|
||
|
private _touchEndPosition = new Vec2()
|
||
|
private isMoveHeader: boolean = false
|
||
|
private isMoveFooter: boolean = false
|
||
|
private isLockHeader: boolean = false
|
||
|
private isLockFooter: boolean = false
|
||
|
private headerProgress: number = 0
|
||
|
private footerProgress: number = 0
|
||
|
private isCustomScroll: boolean = false
|
||
|
canTouchMove: boolean = true
|
||
|
onLoad() {
|
||
|
if (this.layout.autoCenter) {
|
||
|
this.brake = 0.7
|
||
|
}
|
||
|
}
|
||
|
public onEnable() {
|
||
|
super.onEnable()
|
||
|
this.node.on(PageView.EventType.SCROLL_ENG_WITH_THRESHOLD, this.dispatchPageTurningEvent, this)
|
||
|
}
|
||
|
public onDisable() {
|
||
|
super.onDisable()
|
||
|
this.node.off(PageView.EventType.SCROLL_ENG_WITH_THRESHOLD, this.dispatchPageTurningEvent, this)
|
||
|
}
|
||
|
get layout() {
|
||
|
if (!this._layout) { this._layout = this.content?.getComponent(SuperLayout)! }
|
||
|
return this._layout
|
||
|
}
|
||
|
private isCallSoonFinish: boolean = false
|
||
|
get curPageIdx() {
|
||
|
return this.layout["_currPageIndex"]
|
||
|
}
|
||
|
getPages() {
|
||
|
return new Array(this.layout.itemTotal)
|
||
|
}
|
||
|
protected _getContentTopBoundary() {
|
||
|
if (!this._content) {
|
||
|
return -1
|
||
|
}
|
||
|
let offset = this.layout.isOfTopBoundary == 0 ? this._topBoundary : this.layout.isOfTopBoundary
|
||
|
return offset
|
||
|
}
|
||
|
protected _getContentBottomBoundary() {
|
||
|
if (!this._content) {
|
||
|
return -1
|
||
|
}
|
||
|
let offset = this.layout.isOfButtomBoundary == 0 ? this._bottomBoundary : this.layout.isOfButtomBoundary
|
||
|
return offset
|
||
|
}
|
||
|
protected _getContentLeftBoundary() {
|
||
|
if (!this._content) {
|
||
|
return -1
|
||
|
}
|
||
|
let offset = this.layout.isOfLeftBoundary == 0 ? this._leftBoundary : this.layout.isOfLeftBoundary
|
||
|
return offset
|
||
|
}
|
||
|
protected _getContentRightBoundary() {
|
||
|
if (!this._content) {
|
||
|
return -1
|
||
|
}
|
||
|
let offset = this.layout.isOfRightBoundary == 0 ? this._rightBoundary : this.layout.isOfRightBoundary
|
||
|
return offset
|
||
|
}
|
||
|
protected _onTouchBegan(event: EventTouch, captureListeners?: Node[]) {
|
||
|
this.isCallSoonFinish = false
|
||
|
this.isCustomScroll = false
|
||
|
this.layout["onTouchBegin"]()
|
||
|
if (!this.canTouchMove) return
|
||
|
this.direction = ScrollViewDirection.NONE
|
||
|
if (this.layout.isPageView) {
|
||
|
event.touch!.getUILocation(_tempVec2)
|
||
|
Vec2.set(this._touchBeganPosition, _tempVec2.x, _tempVec2.y)
|
||
|
}
|
||
|
super._onTouchBegan(event, captureListeners)
|
||
|
if (this.isTransmitEvent) {
|
||
|
this.transmitEvent(event, Node.EventType.TOUCH_START)
|
||
|
}
|
||
|
}
|
||
|
protected _onTouchMoved(event: EventTouch, captureListeners: any) {
|
||
|
this.isCallSoonFinish = false
|
||
|
this.isCustomScroll = false
|
||
|
if (!this.canTouchMove) return
|
||
|
if (this.isTransmitEvent) {
|
||
|
if (this.direction == ScrollViewDirection.NONE) {
|
||
|
var start = event.getStartLocation()
|
||
|
var curre = event.getLocation()
|
||
|
var xOffset = Math.abs(start.x - curre.x)
|
||
|
var yOffset = Math.abs(start.y - curre.y)
|
||
|
if (xOffset > yOffset) {
|
||
|
// 本ScrollView滑动方向过程中达到一定偏移量是也可以向上发送事件
|
||
|
// if (this.vertical) {
|
||
|
// if (xOffset - yOffset > 50) {
|
||
|
// this.direction = UIScrollViewDirection.HORIZONTAL
|
||
|
// }
|
||
|
// }
|
||
|
this.direction = ScrollViewDirection.HORIZONTAL
|
||
|
|
||
|
} else if (yOffset > xOffset) {
|
||
|
// 本ScrollView滑动方向过程中达到一定偏移量是也可以向上发送事件
|
||
|
// if (this.horizontal) {
|
||
|
// if (yOffset - xOffset > 50) {
|
||
|
// this.direction = UIScrollViewDirection.VERTICAL
|
||
|
// }
|
||
|
// }
|
||
|
this.direction = ScrollViewDirection.VERTICAL
|
||
|
}
|
||
|
}
|
||
|
var canTransmit = (this.vertical && this.direction === ScrollViewDirection.HORIZONTAL) || this.horizontal && this.direction == ScrollViewDirection.VERTICAL
|
||
|
if (canTransmit) {
|
||
|
this.transmitEvent(event, Node.EventType.TOUCH_MOVE)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
this.prevLocation = event.touch?.getPreviousLocation()!
|
||
|
this.location = event.touch?.getLocation()!
|
||
|
super._onTouchMoved(event, captureListeners)
|
||
|
if (this.pullRefresh) {
|
||
|
let outOfBoundary = this._getHowMuchOutOfBoundary()
|
||
|
let offset = this.vertical ? outOfBoundary.y : -outOfBoundary.x
|
||
|
if (offset > 0 && !this.isLockHeader && !this.isLockFooter) {
|
||
|
this.headerProgress = offset < EPSILON ? 0 : offset / this.headerOutOffset
|
||
|
this.isMoveHeader = this.headerProgress >= this.headerMultiple
|
||
|
EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: this.headerProgress, stage: this.isMoveHeader ? "wait" : "touch" })
|
||
|
EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: 0, stage: "release" })
|
||
|
} else if (offset < 0 && !this.isLockHeader && !this.isLockFooter) {
|
||
|
this.footerProgress = -offset < EPSILON ? 0 : -offset / this.footerOutOffset
|
||
|
this.isMoveFooter = this.footerProgress >= this.footerMultiple
|
||
|
EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: this.footerProgress, stage: this.isMoveFooter ? "wait" : "touch" })
|
||
|
EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: 0, stage: "release" })
|
||
|
} else if (offset == 0 && !this.isLockHeader && !this.isLockFooter) {
|
||
|
this.clearProgress()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected _onTouchEnded(event: EventTouch, captureListeners: any) {
|
||
|
this.isCallSoonFinish = false
|
||
|
this.isCustomScroll = false
|
||
|
if (!this.canTouchMove) return
|
||
|
if (this.layout.isPageView) {
|
||
|
event.touch!.getUILocation(_tempVec2)
|
||
|
Vec2.set(this._touchEndPosition, _tempVec2.x, _tempVec2.y)
|
||
|
}
|
||
|
super._onTouchEnded(event, captureListeners)
|
||
|
if (this.isTransmitEvent) {
|
||
|
this.transmitEvent(event, Node.EventType.TOUCH_END)
|
||
|
}
|
||
|
}
|
||
|
protected _onTouchCancelled(event: EventTouch, captureListeners: any) {
|
||
|
this.isCallSoonFinish = false
|
||
|
this.isCustomScroll = false
|
||
|
if (!this.canTouchMove) return
|
||
|
if (this.layout.isPageView) {
|
||
|
event.touch!.getUILocation(_tempVec2)
|
||
|
Vec2.set(this._touchEndPosition, _tempVec2.x, _tempVec2.y)
|
||
|
}
|
||
|
if (this.isTransmitEvent) {
|
||
|
this.transmitEvent(event, Node.EventType.TOUCH_CANCEL)
|
||
|
}
|
||
|
super._onTouchCancelled(event, captureListeners)
|
||
|
}
|
||
|
scrollToAny(moveDelta: Vec3, timeInSecond?: number, attenuated: boolean = true) {
|
||
|
this.isCustomScroll = true
|
||
|
if (timeInSecond) {
|
||
|
this._startAutoScroll(moveDelta, timeInSecond, attenuated, true)
|
||
|
} else {
|
||
|
this._moveContent(moveDelta)
|
||
|
}
|
||
|
}
|
||
|
release() {
|
||
|
this.isMoveHeader = false
|
||
|
this.isMoveFooter = false
|
||
|
if (this.isLockHeader || this.isLockFooter) {
|
||
|
this.vertical && this.isLockHeader && (this._topBoundary += this.headerOutOffset)
|
||
|
this.vertical && this.isLockFooter && (this._bottomBoundary -= this.footerOutOffset)
|
||
|
this.horizontal && this.isLockHeader && (this._leftBoundary -= this.headerOutOffset)
|
||
|
this.horizontal && this.isLockFooter && (this._rightBoundary += this.footerOutOffset)
|
||
|
this.clearProgress()
|
||
|
this.layout["onPositionChanged"]()
|
||
|
this.isLockHeader = false
|
||
|
this.isLockFooter = false
|
||
|
this.startAutoScroll()
|
||
|
}
|
||
|
}
|
||
|
startAutoScroll() {
|
||
|
this._autoScrolling = true
|
||
|
this._outOfBoundaryAmountDirty = true
|
||
|
}
|
||
|
protected _startAutoScroll(deltaMove: any, timeInSecond: any, attenuated: any, flag: boolean = false) {
|
||
|
if (this.pullRefresh) { // 如果没有刷新/加载的监听者 则不计算
|
||
|
if (this.isMoveHeader && !this.isLockHeader) {
|
||
|
if (this.vertical) {
|
||
|
this._topBoundary -= this.headerOutOffset
|
||
|
deltaMove.y -= this.headerOutOffset
|
||
|
}
|
||
|
if (this.horizontal) {
|
||
|
this._leftBoundary += this.headerOutOffset
|
||
|
deltaMove.x += this.headerOutOffset
|
||
|
}
|
||
|
this.isLockHeader = true
|
||
|
EventHandler.emitEvents(this.headerEvents, this, { action: true, progress: this.headerProgress, stage: 'lock' })
|
||
|
} else if (this.isMoveFooter && !this.isLockFooter) {
|
||
|
if (this.vertical) {
|
||
|
this._bottomBoundary += this.footerOutOffset
|
||
|
deltaMove.y += this.footerOutOffset
|
||
|
}
|
||
|
if (this.horizontal) {
|
||
|
this._rightBoundary -= this.footerOutOffset
|
||
|
deltaMove.x -= this.footerOutOffset
|
||
|
}
|
||
|
this.isLockFooter = true
|
||
|
EventHandler.emitEvents(this.footerEvents, this, { action: true, progress: this.footerProgress, stage: 'lock' })
|
||
|
}
|
||
|
}
|
||
|
super._startAutoScroll(deltaMove, timeInSecond, attenuated)
|
||
|
if (!flag && this.layout.autoCenter) {
|
||
|
const touchMoveVelocity = this._calculateTouchMoveVelocity()
|
||
|
if (!this.isQuicklyScrollable(touchMoveVelocity)) {
|
||
|
this.soonFinish()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
protected _updateScrollBar(outOfBoundary: any) {
|
||
|
super._updateScrollBar(new Vec2(outOfBoundary.x, outOfBoundary.y))
|
||
|
if (this._autoScrollBraking) return // 自动回弹时不计算 (非手动)
|
||
|
if (!this._autoScrolling) return // 非自动滚动时不计算
|
||
|
if (!this.pullRefresh) return
|
||
|
let offset = this.vertical ? outOfBoundary.y : -outOfBoundary.x
|
||
|
if (offset > 0) { // 下滑
|
||
|
let progress = offset < EPSILON ? 0 : offset / this.headerOutOffset //根据参数 headerOutOffset 计算当前下滑的办百分比
|
||
|
if (this.isLockHeader) {
|
||
|
this.headerProgress = this.headerProgress == 1 ? this.headerProgress : Math.max(progress, 1)
|
||
|
EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: this.headerProgress, stage: "lock" })
|
||
|
} else {
|
||
|
this.headerProgress = progress < this.headerProgress ? progress : this.headerProgress
|
||
|
EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: this.headerProgress, stage: "release" })
|
||
|
}
|
||
|
} else if (offset < 0) {
|
||
|
let progress = -offset < EPSILON ? 0 : -offset / this.footerOutOffset //根据参数 footerOutOffset 计算当前下滑的办百分比
|
||
|
if (this.isLockFooter) {
|
||
|
this.footerProgress = this.footerProgress == 1 ? this.footerProgress : Math.max(progress, 1)
|
||
|
EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: this.footerProgress, stage: "lock" })
|
||
|
} else {
|
||
|
this.footerProgress = progress < this.footerProgress ? progress : this.footerProgress
|
||
|
EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: this.footerProgress, stage: "release" })
|
||
|
}
|
||
|
} else if (offset == 0) {
|
||
|
// 正常滑动时 如果没有锁定头和尾时 释放所有进度
|
||
|
if (!this.isLockHeader && !this.isLockFooter) {
|
||
|
this.clearProgress()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
private clearProgress() {
|
||
|
EventHandler.emitEvents(this.headerEvents, this, { action: false, progress: 0, stage: "release" })
|
||
|
EventHandler.emitEvents(this.footerEvents, this, { action: false, progress: 0, stage: "release" })
|
||
|
}
|
||
|
private dispatchPageTurningEvent() {
|
||
|
if (this.layout["_lastPageIndex"] === this.layout["_currPageIndex"]) return
|
||
|
this.layout["_lastPageIndex"] = this.layout["_currPageIndex"]
|
||
|
EventHandler.emitEvents(this.layout.pageEvents, this, PageView.EventType.PAGE_TURNING)
|
||
|
this.node.emit(PageView.EventType.PAGE_TURNING, this)
|
||
|
}
|
||
|
|
||
|
protected _handleReleaseLogic(touch: any) {
|
||
|
if (this.layout.isPageView) {
|
||
|
this._autoScrollToPage();
|
||
|
if (this._scrolling) {
|
||
|
this._scrolling = false;
|
||
|
if (!this._autoScrolling) {
|
||
|
this._dispatchEvent(ScrollView.EventType.SCROLL_ENDED);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
super._handleReleaseLogic(touch)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
protected _autoScrollToPage() {
|
||
|
const bounceBackStarted = this._startBounceBackIfNeeded();
|
||
|
if (bounceBackStarted) {
|
||
|
const bounceBackAmount = this._getHowMuchOutOfBoundary()
|
||
|
this._clampDelta(bounceBackAmount)
|
||
|
if (bounceBackAmount.x > 0 || bounceBackAmount.y < 0) {
|
||
|
if (this.layout.horizontal) {
|
||
|
if (this.layout.horizontalAxisDirection == SuperLayout.HorizontalAxisDirection.LEFT_TO_RIGHT) {
|
||
|
this.layout["_currPageIndex"] = this.layout.itemTotal === 0 ? 0 : this.layout.itemTotal - 1
|
||
|
} else {
|
||
|
this.layout["_currPageIndex"] = 0
|
||
|
}
|
||
|
} else {
|
||
|
if (this.layout.verticalAxisDirection == SuperLayout.VerticalAxisDirection.TOP_TO_BOTTOM) {
|
||
|
this.layout["_currPageIndex"] = this.layout.itemTotal === 0 ? 0 : this.layout.itemTotal - 1
|
||
|
} else {
|
||
|
this.layout["_currPageIndex"] = 0
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (bounceBackAmount.x < 0 || bounceBackAmount.y > 0) {
|
||
|
if (this.layout.horizontal) {
|
||
|
if (this.layout.horizontalAxisDirection == SuperLayout.HorizontalAxisDirection.LEFT_TO_RIGHT) {
|
||
|
this.layout["_currPageIndex"] = 0
|
||
|
} else {
|
||
|
this.layout["_currPageIndex"] = this.layout.itemTotal === 0 ? 0 : this.layout.itemTotal - 1
|
||
|
}
|
||
|
} else {
|
||
|
if (this.layout.verticalAxisDirection == SuperLayout.VerticalAxisDirection.TOP_TO_BOTTOM) {
|
||
|
this.layout["_currPageIndex"] = 0
|
||
|
} else {
|
||
|
this.layout["_currPageIndex"] = this.layout.itemTotal === 0 ? 0 : this.layout.itemTotal - 1
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (this.layout.indicator) {
|
||
|
this.layout.indicator._changedState()
|
||
|
}
|
||
|
} else {
|
||
|
const moveOffset = new Vec2()
|
||
|
Vec2.subtract(moveOffset, this._touchBeganPosition, this._touchEndPosition)
|
||
|
const index = this.layout["_currPageIndex"]
|
||
|
var nextIndex = index + this.getDragDirection(moveOffset)
|
||
|
var timeInSecond = this.layout.pageTurningSpeed * Math.abs(index - nextIndex)
|
||
|
if (this.layout.footerLoop) {
|
||
|
if (nextIndex >= this.layout.itemTotal) {
|
||
|
nextIndex = 0
|
||
|
}
|
||
|
}
|
||
|
if (this.layout.headerLoop) {
|
||
|
if (nextIndex < 0) {
|
||
|
nextIndex = this.layout.itemTotal - 1
|
||
|
}
|
||
|
}
|
||
|
const count = this.layout.itemTotal
|
||
|
if (nextIndex < count) {
|
||
|
if (this.isScrollable(moveOffset, index, nextIndex)) {
|
||
|
this.scrollToPage(nextIndex, timeInSecond)
|
||
|
return;
|
||
|
} else {
|
||
|
const touchMoveVelocity = this._calculateTouchMoveVelocity()
|
||
|
if (this.isQuicklyScrollable(touchMoveVelocity)) {
|
||
|
this.scrollToPage(nextIndex, timeInSecond)
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
this.scrollToPage(index, timeInSecond)
|
||
|
}
|
||
|
}
|
||
|
savePageIndex(idx: number) {
|
||
|
if (idx < 0 || idx >= this.layout.itemTotal) {
|
||
|
return false
|
||
|
}
|
||
|
this.layout["_currPageIndex"] = idx
|
||
|
if (this.layout.indicator) {
|
||
|
this.layout.indicator._changedState()
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
protected scrollToPage(idx: number, timeInSecond = 0.3) {
|
||
|
if (idx < 0 || idx >= this.layout.itemTotal) {
|
||
|
return
|
||
|
}
|
||
|
if (this.savePageIndex(idx)) {
|
||
|
this.layout.scrollToIndex(idx, timeInSecond)
|
||
|
}
|
||
|
}
|
||
|
// 快速滑动
|
||
|
protected isQuicklyScrollable(touchMoveVelocity: Vec3) {
|
||
|
if (this.horizontal) {
|
||
|
if (Math.abs(touchMoveVelocity.x) > this.layout.autoPageTurningThreshold) {
|
||
|
return true;
|
||
|
}
|
||
|
} else if (this.vertical) {
|
||
|
if (Math.abs(touchMoveVelocity.y) > this.layout.autoPageTurningThreshold) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
protected getDragDirection(moveOffset: Vec2) {
|
||
|
if (this.horizontal) {
|
||
|
if (moveOffset.x === 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
if (this.layout.horizontalAxisDirection == SuperLayout.HorizontalAxisDirection.LEFT_TO_RIGHT) {
|
||
|
return (moveOffset.x > 0 ? this.layout.groupItemTotal : -this.layout.groupItemTotal);
|
||
|
} else {
|
||
|
return (moveOffset.x < 0 ? this.layout.groupItemTotal : -this.layout.groupItemTotal);
|
||
|
}
|
||
|
} else {
|
||
|
// 由于滚动 Y 轴的原点在在右上角所以应该是小于 0
|
||
|
if (moveOffset.y === 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
if (this.layout.verticalAxisDirection == SuperLayout.VerticalAxisDirection.TOP_TO_BOTTOM) {
|
||
|
return (moveOffset.y < 0 ? this.layout.groupItemTotal : -this.layout.groupItemTotal);
|
||
|
} else {
|
||
|
return (moveOffset.y > 0 ? this.layout.groupItemTotal : -this.layout.groupItemTotal);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// 是否超过自动滚动临界值
|
||
|
protected isScrollable(offset: Vec2, index: number, nextIndex: number) {
|
||
|
const viewTrans = this.view
|
||
|
if (!viewTrans) {
|
||
|
return false
|
||
|
}
|
||
|
if (this.horizontal) {
|
||
|
return Math.abs(offset.x) >= viewTrans.width * this.layout.scrollThreshold
|
||
|
} else if (this.vertical) {
|
||
|
return Math.abs(offset.y) >= viewTrans.height * this.layout.scrollThreshold
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
protected transmitEvent(event: EventTouch, eventType: string) {
|
||
|
var e = new EventTouch(event.getTouches(), event.bubbles)
|
||
|
e.type = eventType
|
||
|
e.touch = event.touch
|
||
|
let target: any = event.target!
|
||
|
target.parent.dispatchEvent(e)
|
||
|
}
|
||
|
private soonFinish() {
|
||
|
this.isCallSoonFinish = true
|
||
|
this.layout["soonFinish"]()
|
||
|
}
|
||
|
/**
|
||
|
* 重写此方法 实际上没有任何改动 只是修改了OUT_OF_BOUNDARY_BREAKING_FACTOR 从0.05 改成0.015
|
||
|
* 吐槽一下 变量 OUT_OF_BOUNDARY_BREAKING_FACTOR 定义方式
|
||
|
*/
|
||
|
protected _processAutoScrolling(dt: number) {
|
||
|
const isAutoScrollBrake = this._isNecessaryAutoScrollBrake();
|
||
|
|
||
|
const brakingFactor = isAutoScrollBrake ? OUT_OF_BOUNDARY_BREAKING_FACTOR : 1;
|
||
|
this._autoScrollAccumulatedTime += dt * (1 / brakingFactor);
|
||
|
let percentage = Math.min(1, this._autoScrollAccumulatedTime / this._autoScrollTotalTime);
|
||
|
if (this._autoScrollAttenuate) {
|
||
|
percentage = quintEaseOut(percentage);
|
||
|
}
|
||
|
const clonedAutoScrollTargetDelta = this._autoScrollTargetDelta.clone();
|
||
|
clonedAutoScrollTargetDelta.multiplyScalar(percentage);
|
||
|
const clonedAutoScrollStartPosition = this._autoScrollStartPosition.clone();
|
||
|
clonedAutoScrollStartPosition.add(clonedAutoScrollTargetDelta);
|
||
|
let reachedEnd = Math.abs(percentage - 1) <= EPSILON;
|
||
|
|
||
|
const fireEvent = Math.abs(percentage - 1) <= this.getScrollEndedEventTiming();
|
||
|
if (fireEvent && !this._isScrollEndedWithThresholdEventFired) {
|
||
|
this._dispatchEvent(ScrollView.EventType.SCROLL_ENG_WITH_THRESHOLD);
|
||
|
this._isScrollEndedWithThresholdEventFired = true;
|
||
|
}
|
||
|
|
||
|
if (this.elastic) {
|
||
|
const brakeOffsetPosition = clonedAutoScrollStartPosition.clone();
|
||
|
brakeOffsetPosition.subtract(this._autoScrollBrakingStartPosition);
|
||
|
if (isAutoScrollBrake) {
|
||
|
brakeOffsetPosition.multiplyScalar(brakingFactor);
|
||
|
}
|
||
|
clonedAutoScrollStartPosition.set(this._autoScrollBrakingStartPosition);
|
||
|
clonedAutoScrollStartPosition.add(brakeOffsetPosition);
|
||
|
} else {
|
||
|
const moveDelta = clonedAutoScrollStartPosition.clone();
|
||
|
moveDelta.subtract(this.getContentPosition());
|
||
|
const outOfBoundary = this._getHowMuchOutOfBoundary(moveDelta);
|
||
|
if (!outOfBoundary.equals(Vec3.ZERO, EPSILON)) {
|
||
|
clonedAutoScrollStartPosition.add(outOfBoundary);
|
||
|
reachedEnd = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (reachedEnd) {
|
||
|
this._autoScrolling = false;
|
||
|
}
|
||
|
|
||
|
if (this.layout.autoCenter && !this.isCallSoonFinish && !this.isCustomScroll) {
|
||
|
if (this._autoScrollTotalTime < 2 || percentage >= 0.8) {
|
||
|
this.soonFinish()
|
||
|
}
|
||
|
}
|
||
|
const deltaMove = clonedAutoScrollStartPosition.clone();
|
||
|
deltaMove.subtract(this.getContentPosition());
|
||
|
this._clampDelta(deltaMove);
|
||
|
this._moveContent(deltaMove, reachedEnd);
|
||
|
this._dispatchEvent(ScrollView.EventType.SCROLLING);
|
||
|
|
||
|
if (!this._autoScrolling) {
|
||
|
this._isBouncing = false;
|
||
|
this._scrolling = false;
|
||
|
this._dispatchEvent(ScrollView.EventType.SCROLL_ENDED);
|
||
|
}
|
||
|
}
|
||
|
}
|