import { _decorator, CCFloat, CCInteger, Component, instantiate, Node, NodePool, ScrollView, UITransform } from 'cc'; const { ccclass, property } = _decorator; /** * 动态列表(无限列表) */ @ccclass('ScrollList') export class ScrollList extends Component { @property(ScrollView) private view: ScrollView = null; @property(CCInteger) private over_item_count: number = 2; @property(CCInteger) private space_vertical: number = 0; private ui_view: UITransform; private ui_content: UITransform; private render_record: Map = new Map(); private content_top: number; private item_height: number; private get item_list(){ return [...this.ui_content.node.children].sort((a, b)=>b.position.y-a.position.y); } protected onLoad(){ this.ui_view = this.view.content.parent.getComponent(UITransform); this.ui_content = this.view.content.getComponent(UITransform); this.content_top = this.ui_content.height * (1-this.ui_content.anchorY); this.view.scrollEvents.push(mtec.cc.creatEventHandle({ target: this.view.node, component: 'ScrollList', handler: 'onScroll', })); } start() { this.item_height = this.ui_content.node.children[0].getComponent(UITransform).height; this.ui_content.node.children.forEach((item, index)=>this.moveItemByIndex(item, index)); if(this.data_list){ this.fixItemCount(this.data_list.length); this.ui_content.height = this.data_list.length * this.item_height + (this.data_list.length-1) * this.space_vertical; } } private onScroll(view: ScrollView){ let y = view.getScrollOffset().y; let start_index = Math.ceil(y / (this.item_height + this.space_vertical)); start_index = Math.max(0, start_index - this.over_item_count); let end_index = start_index + this.ui_content.node.children.length - 1; end_index = Math.min(this.data_list.length-1, end_index); let item_map = this.ui_content.node.children.map(node=>{ let index = (this.content_top - node.position.y - 0.5 * this.item_height) / (this.item_height + this.space_vertical); return {node, index}; }).sort((a, b)=>a.index-b.index); item_map .filter(el=>el.index < start_index) .forEach((el, i, ls)=>{ let index = start_index + this.ui_content.node.children.length - ls.length + i; this.moveItemByIndex(el.node, index); }); item_map .filter(el=>el.index > end_index) .forEach((el, i)=>{ let index = end_index - this.ui_content.node.children.length + i + 1; this.moveItemByIndex(el.node, index); }); item_map .filter(el=>el.index >= start_index && el.index <= end_index) .forEach((el, i)=>{ this.moveItemByIndex(el.node, el.index); }); } private item_pool = new NodePool(); private fixItemCount(count: number){ let ui_demo = this.ui_content.node.children[0].getComponent(UITransform); let full_count = Math.ceil(this.ui_view.height / (ui_demo.height + this.space_vertical)); if(count>full_count) count = full_count + this.over_item_count; let diff = count - this.ui_content.node.children.length; if(diff > 0){ for(let i=0; ithis.item_pool.put(el)); } } private moveItemByIndex(item: Node, index: number){ if(!this.data_list){ item.active = false; return void 0; } let data = this.data_list[index]; if(!data){ item.active = false; return void 0; }else{ item.active = true; } let record = this.render_record.get(item); if(!record){ record = {index: -1, key: undefined, timestamp: -1}; this.render_record.set(item, record); } if(record.index==index && record.key==data[this.data_key]){ if((Date.now() - record.timestamp) <= 1000){ return void 0; } } record.key = data[this.data_key]; record.index = index; record.timestamp = Date.now(); item.name = String(record.key); let y = this.content_top - (index+0.5) * this.item_height - this.space_vertical * index; item.setPosition(item.position.x, y, item.position.z); this.render_item_call_list.forEach(render=>{ render(item, this.data_list[index], this.render_item_node_cache); }); } private data_list: any[]; private data_key: string; public setDataList(list: el[], key: keyof el){ this.data_list = list; this.data_key = key as string; this.render_record.forEach(el=>{ el.index = -1; el.key = undefined; el.timestamp = -1; }); this.fixItemCount(list.length); this.ui_content.height = list.length * this.item_height + (list.length-1) * this.space_vertical; } private render_item_node_cache: Map = new Map(); private render_item_call_list: Array<(node: Node, data: any, node_cache: Map)=>any> = []; public onRenderItem(render_call: (node: Node, data: any, node_cache: Map)=>any){ this.render_item_call_list.push(render_call); } public get NodeCache(){ return this.render_item_node_cache; } }