- Cocos Creator ScrollView 性能优化
- Cocos Creator ScrollView 优化系列-2-
- Cocos Creator ScrollView 长列表优化
- Cocos Creator ScrollView 优化系列-2-
- Cocos Creator ScrollView 优化系列-1-
- cocos creator日常积累--持续更新
- cocos creator--Prefab、Mask、Layou
- Cocos Creator Android 原生启动优化系列 1
- Cocos Creator Android 原生启动优化系列 2
- Cocos Creator 性能优化:DrawCall(全面!)
一. 加载一个超长的列表会导致的问题
1.当场景载入时如果列表很长,那么需要一次性加载所有的条目到列表上,生成条目势必会消耗很长的时间。
2.当列表上的条目都载入后,因为列表上的条目很多,势必会造成Draw call的上升,严重的话导致滑动时会有卡顿感.

二. 解决方案描述,只适用已知子条目高宽及个数的情况
大致意思就是只产生用户屏幕可见区域内的条目,当用户滑动列表时动态生成或从缓存池中获取需要的节点

三.代码部分,这里以简单的单行单个条目的列表来叙述,复杂的例如网格形式的布局也可以用相似的方式实现
条目数据结构:
// 列表滚动条目列表
export default class ListScrollItem {
// y轴值
public y: number = 0
// x轴值
public x: number = 0
// 是否初始化条目
public initedNode: boolean = false
// 条目数据
public data: any = null
// 条目生成后的节点引用
public node: cc.Node = null
private init (x: number, y: number, data: any) {
this.x = x
this.y = y
this.data = data
}
}
- 设置列表高度在已知条目高度和条目个数的情况下,先生成列表的数据结构,同时算出整个List的高度,然后把高度赋值给ScrollView的Content节点
@property(cc.ScrollView)
scrollView: cc.ScrollView = null
// 列表条目预制体
@property(cc.Prefab)
itemPrefab: cc.Prefab = null
@property(number)
listCount: number = 100
// 列表条目数据结构
private scrollItems: ListScrollItem[] = []
start () {
let itemTemplateNode = cc.instantiate(this.itemPrefab)
let itemHeight = itemTemplateNode.height
for (var index = 0; index < this.listCount; index ++) {
let scrollItem = new ListScrollItem()
// 这里以条目锚点从左上开始计算,cocos默认从中心
scrollItem.init(0, index * itemHeight, index)
this.scrollItems.push(scrollItem)
}
this.scrollView.content.height = itemHeight * listCount
// 到此,我们生成了一个有很长高度的ScrollView及列表的数据结构,
// 接下来就动态的加载子节点,即只生成屏幕可见区域内的条目
}
- 动态加载屏幕可见区域内的条目
start () {
// 注册滚动监听函数
this.scrollView.node.on("scrolling", this._onScrolling, this)
}
// scrollView滚动回调
_onScrolling () {
// 可视区域y轴最小值
let visibleAreaMinY = this.scrollView.getScrollOffset().y
// 可视区域y轴最大值
let visibleAreaMaxY = visibleAreaMinY + this.scrollView.node.height
// 循环遍历列表数据结构,检查节点条目的y轴是否处于可见区域内
this.scrollItems.forEach(scrollItem => {
let scrollItemInVisibleArea = scrollItem.y >= visibleAreaMinY && scrollItem.y <= visibleAreaMaxY
if (scrollItemInVisibleArea) {
// 节点在屏幕区域内
if (!scrollItem.initedNode) { // 条目未创建,则创建条目
let itemNode = cc.instantiate(this.itemPrefab)
scrollItem.node = itemNode
scrollItem.initedNode = true // 创建标示置为已创建
// 添加到ScrollView
this.scrollView.content.addChild(node)
}
scrollItem.node.opacity = 255 // 让条目可见
} else {
// 节点不在可见区域内
if (!scrollItem.initedNode) { // 条目未创建,不做操作
return
}
// 条目已创建,隐藏条目,减少Draw call
scrollItem.node.opacity = 0
}
})
}
完成到这一步后基本已经实现了列表的优化,有效的解决了标题提到的两个问题,但还有一些地方可以优化,
三. 再优化
- 创建条目时总是生成了新的条目。
- 每次列表滚动后都是循环遍历检查列表所有的数据结构,感觉还是有点浪费,让我们继续优化它。
先来优化这一项:1. 创建条目时总是生成了新的条目。
// 条目缓存池,用来缓存已经不可见的条目,当需要创建新条目时先从缓存池中查找,如果有那么就不用创建新的了
private scrollItemNodePool: cc.NodePool = new cc.NodePool()
// 修改一下_onScrolling方法
_onScrolling () {
// 可视区域y轴最小值
let visibleAreaMinY = this.scrollView.getScrollOffset().y
// 可视区域y轴最大值
let visibleAreaMaxY = visibleAreaMinY + this.scrollView.node.height
// 循环遍历列表数据结构,检查节点条目的y轴是否处于可见区域内
this.scrollItems.forEach(scrollItem => {
let scrollItemInVisibleArea = scrollItem.y >= visibleAreaMinY && scrollItem.y <= visibleAreaMaxY
if (scrollItemInVisibleArea) {
// 节点在屏幕区域内
if (!scrollItem.initedNode) { // 条目未创建,则创建条目
// 先从缓存池中获取条目
let itemNode = this.scrollItemNodePool.get()
// 如果缓存池中没有条目,那么在创建
if (!itemNode) {
itemNode = cc.instantiate(this.itemPrefab)
}
scrollItem.node = itemNode
scrollItem.initedNode = true // 创建标示置为已创建
// 添加到ScrollView
this.scrollView.content.addChild(node)
}
scrollItem.node.opacity = 255 // 让条目可见
} else {
// 节点不在可见区域内
if (!scrollItem.initedNode) { // 条目未创建,不做操作
return
}
// 条目已创建,隐藏条目,减少Draw call
scrollItem.node.opacity = 0
// 添加到缓存池中
this.scrollItemNodePool.put(scrollItem.node)
scrollItem.initedNode = false // 创建标示置为未创建
}
})
}
修改后的_onScrolling方法有效的重用了条目,不用每次都创建新的.
- 每次列表滚动后都是循环遍历检查列表所有的数据结构,感觉还是有点浪费,让我们继续优化它。 针对这个问题的优化我的解决办法有点复杂,暂时不贴出来了,有想法的朋友可以出出意见。
网友评论