import ScrollInner from "./ScrollInner"; const { ccclass, property, menu } = cc._decorator; @ccclass @menu('Scroll/ScrollOut') export default class ScrollOut extends cc.ScrollView { @property(ScrollInner) scrollInnerViews: ScrollInner[] = [] scrollInnerView: ScrollInner; canScroll: boolean = true isFirst: boolean = true; m_PlanDir: number; onLoad() { this.m_PlanDir = null; this.scrollInnerViews.forEach(inner => { inner.setOuterScrollView(this); }); } //是否为子物体 //注意,这里递归, 如果child藏的太深, 可能影响效率。其实也还好,只是开始滑动时执行一次。 _isHisChild(child, undeterminedParent) { if (child == undeterminedParent) { return true; } if (child.parent != null) { if (child.parent == undeterminedParent) { return true; } else { return this._isHisChild(child.parent, undeterminedParent); } } return false; } //判断Target是否是InnerScrollView的子物体, 如果是,就返回这个InnerScrollView。 //注意,这里遍历所有InnerScrollView, 如果InnerScrollView数量太多,可能影响效率。其实也还好,只是开始滑动时执行一次。 _findScrollingInnerSv(target) { for (let i = 0; i < this.scrollInnerViews.length; i++) { let isHisChild = this._isHisChild(target, this.scrollInnerViews[i].node); if (isHisChild) { return this.scrollInnerViews[i]; } } return null; } isSame() { return (this.horizontal && this.scrollInnerView.horizontal) || (this.vertical && this.scrollInnerView.vertical) } //#region 重写cc.ScrollView的方法 _hasNestedViewGroup(event, captureListeners) { if (event.eventPhase !== cc.Event.CAPTURING_PHASE) return; //不阻止out上onTouch事件执行。 return false; } _onTouchBegan(event, captureListeners) { if (!this.enabledInHierarchy) return; if (this._hasNestedViewGroup(event, captureListeners)) return; //重置计划方向 this.m_PlanDir = null; this.scrollInnerView = null; this.isFirst = true var touch = event.touch; if (this.content) { this["_handlePressLogic"](touch); } this["_touchMoved"] = false; this["_stopPropagationIfTargetIsMe"](event); } _onTouchMoved(event, captureListeners) { // 答疑:为什么确定 scrollInnerView, 不用captureListeners, 而要用this._findScrollingInnerSv? // 因为,在子ScrollView上拖动时, captureListeners中并不包含该子ScrollView本身。 // cc.log("----------------------------"); // captureListeners.forEach((captureListener) => { // cc.log(captureListener.name); // }); if (!this.enabledInHierarchy) return; if (this._hasNestedViewGroup(event, captureListeners)) return; var touch = event.touch; var deltaMove = touch.getLocation().sub(touch.getStartLocation()); //在滑动时, 设置开始时滑动的方向为计划方向 //为什么在Outer中做这件事? //因为Outer的_onTouchMoved比Inner的早执行, 如果在Inner里做, Outer中就得忽略一帧,体验可能会不好。 if (deltaMove.mag() > 7) { this.scrollInnerView = this._findScrollingInnerSv(event.target); if (this.scrollInnerView != null) { let contentSize = this.scrollInnerView.content.getContentSize(); let scrollViewSize = this.scrollInnerView.node.getContentSize(); if ((this.scrollInnerView.vertical && (contentSize.height > scrollViewSize.height)) || (this.scrollInnerView.horizontal && (contentSize.width > scrollViewSize.width))) { if (this.m_PlanDir == null) { this.m_PlanDir = Math.abs(deltaMove.x) > Math.abs(deltaMove.y) ? 1 : -1; //1水平 -1垂直 } if (this.m_PlanDir == 1) { if (this.isSame()) { if (this.isFirst) { this.isFirst = false //回滚0.1 否则走不下去 if (this.scrollInnerView.getScrollOffset().x >= 0) { this.scrollInnerView.content.x = -0.1 return } if (this.scrollInnerView.getScrollOffset().x <= -this.scrollInnerView.getMaxScrollOffset().x) { this.scrollInnerView.content.x = -this.scrollInnerView.getMaxScrollOffset().x + 0.1 return } } this.canScroll = this.horizontal && (this.scrollInnerView.getScrollOffset().x >= 0 || this.scrollInnerView.getScrollOffset().x <= -this.scrollInnerView.getMaxScrollOffset().x) this.scrollInnerView.canScroll = !this.canScroll } else { this.canScroll = this.horizontal this.scrollInnerView.canScroll = this.scrollInnerView.horizontal } } else { if (this.isSame()) { if (this.isFirst) { this.isFirst = false //回滚0.1 否则走不下去 if (this.scrollInnerView.getScrollOffset().y <= 0) { this.scrollInnerView.content.y = 0.1 return } if (this.scrollInnerView.getScrollOffset().y >= this.scrollInnerView.getMaxScrollOffset().y) { this.scrollInnerView.content.y = this.scrollInnerView.getMaxScrollOffset().y - 0.1 return } } this.canScroll = this.vertical && this.scrollInnerView.getScrollOffset().y <= 0 || this.scrollInnerView.getScrollOffset().y >= this.scrollInnerView.getMaxScrollOffset().y this.scrollInnerView.canScroll = !this.canScroll } else { this.canScroll = this.vertical this.scrollInnerView.canScroll = this.scrollInnerView.vertical } } } else { this.canScroll = true; this.scrollInnerView.canScroll = false } } else { this.canScroll = true; } } if (this.content) { if (this.canScroll) { this["_handleMoveLogic"](touch); } } if (!this.cancelInnerEvents) { return; } //只取消会捕获事件的直接子物体(如Button)上的事件 if (this.scrollInnerView == null) { if (deltaMove.mag() > 7) { if (!this["_touchMoved"] && event.target !== this.node) { var cancelEvent = new cc.Event.EventTouch(event.getTouches(), event.bubbles); cancelEvent.type = cc.Node.EventType.TOUCH_CANCEL; cancelEvent.touch = event.touch; cancelEvent["simulate"] = true; event.target.dispatchEvent(cancelEvent); this["_touchMoved"] = true; } } this["_stopPropagationIfTargetIsMe"](event); } } _onTouchEnded(event, captureListeners) { if (!this.enabledInHierarchy) return; if (this._hasNestedViewGroup(event, captureListeners)) return; this["_dispatchEvent"]('touch-up'); let touch = event.touch; if (this.content) { this["_handleReleaseLogic"](touch); } if (this["_touchMoved"]) { event.stopPropagation(); } else { this["_stopPropagationIfTargetIsMe"](event); } } _onTouchCancelled(event, captureListeners) { if (!this.enabledInHierarchy) return; if (this._hasNestedViewGroup(event, captureListeners)) return; // Filte touch cancel event send from self if (!event.simulate) { let touch = event.touch; if (this.content) { this["_handleReleaseLogic"](touch); } } this["_stopPropagationIfTargetIsMe"](event); } //#endregion }