| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- 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<Node, {key: number|string, timestamp: number, index: number}> = 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; i<diff; i++) (this.item_pool.get() ?? instantiate(ui_demo.node)).setParent(this.ui_content.node);
- }else if(diff < 0){
- this.ui_content.node.children.slice(diff).forEach(el=>this.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<el>(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<Node, any> = new Map();
- private render_item_call_list: Array<(node: Node, data: any, node_cache: Map<Node, any>)=>any> = [];
- public onRenderItem(render_call: (node: Node, data: any, node_cache: Map<Node, any>)=>any){
- this.render_item_call_list.push(render_call);
- }
- public get NodeCache(){
- return this.render_item_node_cache;
- }
- }
|