ScrollView下塞的子物体过多,必然造成卡顿,主流解决方案为采用缓存机制,动态刷新内容。
代码可以从我的github上下载
列表的滑动控制依然要借用CocosCreator自带的ScrollView组件。笔者这里暂时实现了上下方向的无尽列表。
列表的动态刷新和加载机制使用EndlessScrollView.ts
子item的具体显示逻辑放到ScrollItem.ts脚本中。建议新建一个类继承自ScrollItem,并重写其Refresh方法。
两个脚本代码如下:
import ScrollItem from "./ScrollItem";
const { ccclass, property } = cc._decorator;
/**
* 目前该脚本只支持从上到下ScrollView
* 同时还起到排版布局的作用,content节点上,无需再附加layout组件,否则会有冲突
*/
@ccclass
export default class EndlessScrollView extends cc.Component {
/**
* view裁剪节点,根据view尺寸,计算可视区域可以显示多少个item
*/
@property(cc.Node)
view: cc.Node;
/**
* 充当items的父物体
*/
@property(cc.Node)
content: cc.Node;
@property(cc.Node)
itemPrefab: cc.Node;
@property(Number)
itemHeight: number = 0;
@property(Number)
paddingTop: number = 10;
@property(Number)
paddingBottom: number = 10;
@property(Number)
spaceY: number = 20;
private viewItemsCount: number = 0;//view区域能填充的物体数
private dataCount: number = 0;
private cacheItems: cc.Node[] = [];
private cacheCount: number = 0;
private contentYMin: number = 0;
private contentYMax: number = 0;
//子物体Y坐标转换到view节点下的阈值
//上滑的时候,若子物体y坐标大于topValue,则把上面的item移到下面
//下滑的时候,若子物体y坐标小于bottomValue,则把下面的item移到上面
private topValue: number = 0;
private bottomValue: number = 0;
private data: any[] = [];
Init(_data: any[]) {
this.data = _data;
this.dataCount = this.data.length;
this.itemHeight = this.itemPrefab.getContentSize().height;
this.viewItemsCount = Math.ceil(this.view.height / (this.itemHeight + this.spaceY));
console.log("dataCount:" + this.dataCount + ",viewItemsCount:" + this.viewItemsCount + ", view.height:" + this.view.height);
//最多加载viewItemsCount+10个Item作为缓冲数据
for (let i = 0; i < this.viewItemsCount + 10; i++) {
if (typeof this.data[i] == 'undefined')
break;
let tmp = cc.instantiate(this.itemPrefab);
tmp.getComponent(ScrollItem).Refresh(this.data[i]);
tmp.getComponent(ScrollItem).DataIndex = i;
this.cacheItems.push(tmp);
tmp.setParent(this.content);
//根据自己的锚点情况来设置x,y坐标
let x = 0;
let y = -((i + 1) * this.itemHeight - this.itemHeight * 0.5) - this.spaceY * i - this.paddingTop;
tmp.position = new cc.Vec2(x, y)
}
this.cacheCount = this.cacheItems.length;
//根据数据总数,以及锚点设置情况来设置content的高度
this.contentYMin = 0;
this.contentYMax = this.dataCount * (this.itemHeight + this.spaceY) - this.spaceY + this.paddingTop + this.paddingBottom;
this.content.height = this.contentYMax;
this.topValue = 5 * this.itemHeight + 4 * this.spaceY;
this.bottomValue = -(this.topValue + this.view.height);
}
private lastYPos: number = 0;
update(dt) {
// console.log("content pos:" + this.content.position);
if (this.content.position.y == this.lastYPos) {
return;
}
if (this.content.position.y < this.contentYMin) {//向下滑动到顶部了
return
}
if (this.content.position.y > this.contentYMax) {//向上滑动到底部了
return;
}
if (this.content.position.y > this.lastYPos) {
console.log("向上");
this.updateItems(false);
}
else {
console.log("向下");
this.updateItems(true);
}
this.lastYPos = this.content.position.y;
}
private updateItems(isDown: boolean) {
for (let i = 0; i < this.cacheItems.length; i++) {
const element = this.cacheItems[i];
let scrollItem = element.getComponent(ScrollItem);
//item坐标转换到view节点坐标
let pos = this.view.convertToNodeSpaceAR(this.content.convertToWorldSpaceAR(element.position));
if (isDown) {
if (pos.y < this.bottomValue) {
let newId = scrollItem.DataIndex - this.cacheCount;
if (newId < 0) {
return;
}
scrollItem.DataIndex = newId;
element.y = element.y + this.cacheCount * this.itemHeight + this.cacheCount * this.spaceY;
scrollItem.Refresh(this.data[newId]);
}
}
else {
if (pos.y > this.topValue) {
let newId = scrollItem.DataIndex + this.cacheCount;
if (newId > this.dataCount - 1) {
return;
}
scrollItem.DataIndex = newId;
element.y = element.y - this.cacheCount * this.itemHeight - this.cacheCount * this.spaceY;
scrollItem.Refresh(this.data[newId]);
}
}
}
}
}
const { ccclass, property } = cc._decorator;
@ccclass
export default class ScrollItem extends cc.Component {
/**
* DataIndex 从0开始,对应EndlessScrollView中Init函数传入的data[]数组的索引
*/
public DataIndex: number = 0;
public Refresh(data: any) {
}
}
参考文档:
CocosCreator开发笔记(5)-ScrollView之动态更新的优化原理
COCOS CREATOR的scroll view性能优化记录