scroll.list.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { _decorator, CCFloat, CCInteger, Component, instantiate, Node, NodePool, ScrollView, UITransform } from 'cc';
  2. const { ccclass, property } = _decorator;
  3. /**
  4. * 动态列表(无限列表)
  5. */
  6. @ccclass('ScrollList')
  7. export class ScrollList extends Component {
  8. @property(ScrollView)
  9. private view: ScrollView = null;
  10. @property(CCInteger)
  11. private over_item_count: number = 2;
  12. @property(CCInteger)
  13. private space_vertical: number = 0;
  14. private ui_view: UITransform;
  15. private ui_content: UITransform;
  16. private render_record: Map<Node, {key: number|string, timestamp: number, index: number}> = new Map();
  17. private content_top: number;
  18. private item_height: number;
  19. private get item_list(){
  20. return [...this.ui_content.node.children].sort((a, b)=>b.position.y-a.position.y);
  21. }
  22. protected onLoad(){
  23. this.ui_view = this.view.content.parent.getComponent(UITransform);
  24. this.ui_content = this.view.content.getComponent(UITransform);
  25. this.content_top = this.ui_content.height * (1-this.ui_content.anchorY);
  26. this.view.scrollEvents.push(mtec.cc.creatEventHandle({
  27. target: this.view.node,
  28. component: 'ScrollList',
  29. handler: 'onScroll',
  30. }));
  31. }
  32. start() {
  33. this.item_height = this.ui_content.node.children[0].getComponent(UITransform).height;
  34. this.ui_content.node.children.forEach((item, index)=>this.moveItemByIndex(item, index));
  35. if(this.data_list){
  36. this.fixItemCount(this.data_list.length);
  37. this.ui_content.height = this.data_list.length * this.item_height + (this.data_list.length-1) * this.space_vertical;
  38. }
  39. }
  40. private onScroll(view: ScrollView){
  41. let y = view.getScrollOffset().y;
  42. let start_index = Math.ceil(y / (this.item_height + this.space_vertical));
  43. start_index = Math.max(0, start_index - this.over_item_count);
  44. let end_index = start_index + this.ui_content.node.children.length - 1;
  45. end_index = Math.min(this.data_list.length-1, end_index);
  46. let item_map = this.ui_content.node.children.map(node=>{
  47. let index = (this.content_top - node.position.y - 0.5 * this.item_height) / (this.item_height + this.space_vertical);
  48. return {node, index};
  49. }).sort((a, b)=>a.index-b.index);
  50. item_map
  51. .filter(el=>el.index < start_index)
  52. .forEach((el, i, ls)=>{
  53. let index = start_index + this.ui_content.node.children.length - ls.length + i;
  54. this.moveItemByIndex(el.node, index);
  55. });
  56. item_map
  57. .filter(el=>el.index > end_index)
  58. .forEach((el, i)=>{
  59. let index = end_index - this.ui_content.node.children.length + i + 1;
  60. this.moveItemByIndex(el.node, index);
  61. });
  62. item_map
  63. .filter(el=>el.index >= start_index && el.index <= end_index)
  64. .forEach((el, i)=>{
  65. this.moveItemByIndex(el.node, el.index);
  66. });
  67. }
  68. private item_pool = new NodePool();
  69. private fixItemCount(count: number){
  70. let ui_demo = this.ui_content.node.children[0].getComponent(UITransform);
  71. let full_count = Math.ceil(this.ui_view.height / (ui_demo.height + this.space_vertical));
  72. if(count>full_count) count = full_count + this.over_item_count;
  73. let diff = count - this.ui_content.node.children.length;
  74. if(diff > 0){
  75. for(let i=0; i<diff; i++) (this.item_pool.get() ?? instantiate(ui_demo.node)).setParent(this.ui_content.node);
  76. }else if(diff < 0){
  77. this.ui_content.node.children.slice(diff).forEach(el=>this.item_pool.put(el));
  78. }
  79. }
  80. private moveItemByIndex(item: Node, index: number){
  81. if(!this.data_list){
  82. item.active = false;
  83. return void 0;
  84. }
  85. let data = this.data_list[index];
  86. if(!data){
  87. item.active = false;
  88. return void 0;
  89. }else{
  90. item.active = true;
  91. }
  92. let record = this.render_record.get(item);
  93. if(!record){
  94. record = {index: -1, key: undefined, timestamp: -1};
  95. this.render_record.set(item, record);
  96. }
  97. if(record.index==index && record.key==data[this.data_key]){
  98. if((Date.now() - record.timestamp) <= 1000){
  99. return void 0;
  100. }
  101. }
  102. record.key = data[this.data_key];
  103. record.index = index;
  104. record.timestamp = Date.now();
  105. item.name = String(record.key);
  106. let y = this.content_top - (index+0.5) * this.item_height - this.space_vertical * index;
  107. item.setPosition(item.position.x, y, item.position.z);
  108. this.render_item_call_list.forEach(render=>{
  109. render(item, this.data_list[index], this.render_item_node_cache);
  110. });
  111. }
  112. private data_list: any[];
  113. private data_key: string;
  114. public setDataList<el>(list: el[], key: keyof el){
  115. this.data_list = list;
  116. this.data_key = key as string;
  117. this.render_record.forEach(el=>{
  118. el.index = -1;
  119. el.key = undefined;
  120. el.timestamp = -1;
  121. });
  122. this.fixItemCount(list.length);
  123. this.ui_content.height = list.length * this.item_height + (list.length-1) * this.space_vertical;
  124. }
  125. private render_item_node_cache: Map<Node, any> = new Map();
  126. private render_item_call_list: Array<(node: Node, data: any, node_cache: Map<Node, any>)=>any> = [];
  127. public onRenderItem(render_call: (node: Node, data: any, node_cache: Map<Node, any>)=>any){
  128. this.render_item_call_list.push(render_call);
  129. }
  130. public get NodeCache(){
  131. return this.render_item_node_cache;
  132. }
  133. }