import SuperListItem from "./SuperListItem"; const { ccclass, property } = cc._decorator; @ccclass export default class SuperScrollView extends cc.ScrollView { @property({ type: cc.Node, tooltip: 'item模板', }) pfItemTemplate: cc.Node = null; itemSize: cc.Size = null; itemNodePool: cc.NodePool = null; @property({ tooltip: '分帧加载时间间隔', }) duration: number = 1; // @property({ // tooltip: '最多可以显示多少个Item', // }) visibleNum: number = 12; private curIndex: number = 0; private itemInfoList: any[] = []; private isFrameLoading: boolean = false; private isLoadingFinished: boolean = true; private cbAfterSetData: Function = null; private lastTime: number = 0; onLoad() { this.elastic = true; this.content.on(cc.Node.EventType.CHILD_ADDED, this.childAdded.bind(this)); this.node.on("touchend", this.onTouchEnd, this); this.node.on("scroll-ended", this.onScrollEnded, this); this.node.on("scrolling", this.onScrolling, this); this.node.on("bounce-top", this.onBounceTop, this); this.node.on("bounce-bottom", this.onBounceBottom, this); this.node.on("bounce-left", this.onBounceLeft, this); this.node.on("bounce-right", this.onBounceRight, this); this.initNodePool(); } onDestroy() { this.content.off(cc.Node.EventType.CHILD_ADDED, this.childAdded); this.node.off("touchend", this.onTouchEnd, this); this.node.off("scroll-ended-with-threshold", this.onScrollEnded, this); this.node.off("scrolling", this.onScrolling, this); this.node.off("bounce-top", this.onBounceTop, this); this.node.off("bounce-bottom", this.onBounceBottom, this); this.node.off("bounce-left", this.onBounceLeft, this); this.node.off("bounce-right", this.onBounceRight, this); } private initNodePool() { let itemNode = cc.instantiate(this.pfItemTemplate); let contentSize = itemNode.getContentSize(); this.itemSize = contentSize; let parentSize = this.content.parent.getContentSize(); let layoutComp = this.content.getComponent(cc.Layout); let num = 0; if (layoutComp) { if (layoutComp.type === cc.Layout.Type.VERTICAL && this.vertical && !this.horizontal) { num = Math.ceil(parentSize.height / contentSize.height); } else if (layoutComp.type === cc.Layout.Type.HORIZONTAL && this.horizontal && this.vertical) { num = Math.ceil(parentSize.width / contentSize.width); } else if (layoutComp.type === cc.Layout.Type.GRID) { let rowEleCount = Math.floor((parentSize.width - layoutComp.paddingLeft - layoutComp.paddingRight + layoutComp.spacingX) / (contentSize.width + layoutComp.spacingX)) let colEleCount = Math.floor((parentSize.height - layoutComp.paddingTop - layoutComp.paddingBottom + layoutComp.spacingY) / (contentSize.height + layoutComp.spacingY)) num = rowEleCount * colEleCount; // console.log('最多显示个数:GRID', rowEleCount, colEleCount); } } this.visibleNum = Math.floor(num * 2) + 2; // console.log('最多显示个数', this.visibleNum); if (this.itemNodePool) { //清空 this.itemNodePool.clear(); } else { this.itemNodePool = new cc.NodePool(); } //多放3个 for (let i = 0; i < this.visibleNum + 3; i++) { let itemNode = cc.instantiate(this.pfItemTemplate); this.itemNodePool.put(itemNode); } } //添加节点 private childAdded(itemNode: cc.Node) { this.curIndex++; itemNode.name = "item" + this.curIndex; } public scrollToIndex(index: number, seconds: number = 0.2) { // cc.log("index:", index); let childCount = this.content.childrenCount; // cc.log("childCount:", childCount); if (index < 1) { // cc.log("index 过小"); index = 1; } else if (index > childCount) { // cc.log("index 过大"); index = childCount; } let item = this.content.getChildByName('item' + index); if (item) { let layoutComp: cc.Layout = this.content.getComponent(cc.Layout); if (layoutComp) { if (layoutComp.type === cc.Layout.Type.VERTICAL && this.vertical && !this.horizontal) { this.scrollToPercentVertical((childCount - index) / childCount, seconds); } else if (layoutComp.type === cc.Layout.Type.HORIZONTAL && !this.vertical && this.horizontal) { this.scrollToPercentHorizontal((childCount - index) / childCount, seconds); } else if (layoutComp.type === cc.Layout.Type.GRID) { if (layoutComp.startAxis === cc.Layout.AxisDirection.HORIZONTAL) { let contentSize = this.content.getContentSize(); let itemContentSize = item.getContentSize(); // contentSize.width = layoutComp.paddingLeft + layoutComp.paddingRight + itemContentSize.width * n + layoutComp.spacingX *(n-1) let rowEleCount = Math.floor((contentSize.width - layoutComp.paddingLeft - layoutComp.paddingRight + layoutComp.spacingX) / (itemContentSize.width + layoutComp.spacingX)) // cc.log("每行多少个:", rowEleCount); let hang = Math.ceil(index / rowEleCount); let totalHang = Math.ceil(childCount / rowEleCount); this.scrollToPercentVertical((totalHang - hang) / totalHang, seconds); } else { let contentSize = this.content.getContentSize(); let itemContentSize = item.getContentSize(); let colEleCount = Math.floor((contentSize.height - layoutComp.paddingTop - layoutComp.paddingBottom + layoutComp.spacingY) / (itemContentSize.height + layoutComp.spacingY)) // cc.log("每列多少个:", colEleCount); let lie = Math.ceil(index / colEleCount); let totalLie = Math.ceil(childCount / colEleCount); this.scrollToPercentHorizontal((totalLie - lie) / totalLie, seconds); } } else { // cc.log("cc.Layout.Type不对"); } } } } private onTouchEnd() { return; this.improveDC(); } private onScrollEnded() { // cc.log("onScrollEnded"); this.improveDC(); } private onScrolling() { let now = Date.now(); if (now - this.lastTime < 200) { return; } // cc.log("scolling"); this.lastTime = now; let scrollOffset = this.getScrollOffset(); let offsetX = scrollOffset.x; let offsetY = scrollOffset.y; this.improveDC(); } private onBounceTop() { // cc.log("onBounceTop") } private onBounceBottom() { // cc.log("onBounceBottom") } private onBounceLeft() { // cc.log("onBounceLeft") } private onBounceRight() { // cc.log("onBounceLeft") } private newItemNode(): cc.Node { // let itemNode = this.itemNodePool.get(); // if (!itemNode) { // cc.log("instantiate"); // itemNode = cc.instantiate(this.pfItemTemplate); // } let itemNode = cc.instantiate(this.pfItemTemplate); return itemNode; } // 优化DrawCall public improveDC() { if (this.content.childrenCount == 0) { return; } let svLeftBottomPoint: cc.Vec2 = this.node.parent.convertToWorldSpaceAR( // cc.v2(0,0) cc.v2( this.node.x - this.node.anchorX * this.node.width, this.node.y - this.node.anchorY * this.node.height ) ); // 求出 ScrollView 可视区域在世界坐标系中的矩形(碰撞盒) let svBBoxRect: cc.Rect = cc.rect(svLeftBottomPoint.x, svLeftBottomPoint.y, this.node.width, this.node.height); // 遍历 ScrollView Content 内容节点的子节点,对每个子节点的包围盒做和 ScrollView 可视区域包围盒做碰撞判断 this.content.children.forEach((childNode: cc.Node) => { // 如果相交了,那么就显示,否则就隐藏 let childNodeBBox = childNode.getBoundingBoxToWorld(); if (childNodeBBox.intersects(svBBoxRect)) { if (childNode.opacity === 0) { childNode.opacity = 255; } } else { if (childNode.opacity !== 0) { childNode.opacity = 0; } } }); } public async setData(itemInfoList: any[], isFrameLoading: boolean = false, cb?: Function) { if (!this.isLoadingFinished) { return; } this.curIndex = 0; this.isLoadingFinished = false; this.isFrameLoading = isFrameLoading; this.itemInfoList = itemInfoList; this.cbAfterSetData = cb; this.content.destroyAllChildren(); if (this.isFrameLoading) { await this.executePreFrame(this.getItemGenerator(this.itemInfoList.length), this.duration); } else { for (let i = 0; i < this.itemInfoList.length; i++) { let item = this.newItemNode(); item.parent = this.content; item.getComponent(SuperListItem).setData(itemInfoList[i]); } this.isLoadingFinished = true; this.scheduleOnce(() => { this.cbAfterSetData && this.cbAfterSetData(); this.improveDC(); }); } } //@ts-ignore private executePreFrame(generator: Generator, duration: number) { return new Promise((resolve, reject) => { let gen = generator; // 创建执行函数 let execute = () => { // 执行之前,先记录开始时间 let startTime = new Date().getTime(); // 然后一直从 Generator 中获取已经拆分好的代码段出来执行 for (let iter = gen.next(); ; iter = gen.next()) { // 判断是否已经执行完所有 Generator 的小代码段,如果是的话,那么就表示任务完成 if (iter == null || iter.done) { //@ts-ignore resolve(); return; } // 每执行完一段小代码段,都检查一下是否已经超过我们分配的本帧,这些小代码端的最大可执行时间 if (new Date().getTime() - startTime > duration) { // 如果超过了,那么本帧就不在执行,开定时器,让下一帧再执行 this.scheduleOnce(() => { execute(); }); return; } } }; // 运行执行函数 execute(); }); } private initItem(itemInfo: any) { let itemNode = this.newItemNode(); itemNode.parent = this.content; itemNode.getComponent(SuperListItem).setData(itemInfo); } private *getItemGenerator(length: number) { for (let i = 0; i < length; i++) { yield this.initItem(this.itemInfoList[i]); } this.isLoadingFinished = true; this.scheduleOnce(() => { this.cbAfterSetData && this.cbAfterSetData(); this.improveDC(); }); } public canInputData(): boolean { return this.isLoadingFinished; } //通过index去获取节点 public getItem(index: number): cc.Node { let item = this.content.getChildByName('item' + index); return item || null; } // update (dt) {} }