drawcall优化 - 无限滚动容器
这是一个前端项目常见的一个问题dc优化。比如一个复杂的背包容器500个元素,如果使用粗暴的实例化500个元素进去,最终的dc无疑会是灾难的。在内存管理和性能角度上讲也是铺张浪费的。
看见效果如下 dc 1091

但实际上500个元素itemCell,在一个屏幕视界内能看到和交互的也就6~8个。从效能上考虑,只要能无缝衔接和复用少量的元素就能满足用户需求,根本不需要填鸭式的创建并使用500个itemCell。
基于这个优化方向,做一下简单的规则设计。
把屏幕分成3个区块 上下红色是过渡区,用来缓冲蓝色方块的itemCell, 中间绿色是可见有效区,实时一致的显示给到用户的itemcell
我们需要做的几件事是:
1.创建少量的itemCell置于content节点下,暂定18个
2.计算并控制好滑动区域(绿色区域)itemCell的间距大小,按照滑动行程,每2行(4个itemCell)的高度进行一次更新
3.更新的操作是重绘当前的容器子节点,并同时执行ScrollView.scrollToOffset,对齐子节点位置,让用户无感的衔接到重绘后的容器布局和位置,保证观感一致
4.校验并处理滑动上下边界问题。

以下是演示效果 dc优化到96 (实际占用只有40左右 ,每个itemCell 占用2)

废话不多说,上代码
import { _decorator, instantiate, Label, Node, ScrollView,UITransform, Vec2 } from 'cc';
import { UIbase } from '../UIbase';
import { itemCell } from '../../../module_recovery/itemCell';
const { ccclass, property } = _decorator;
@ccclass('UINotice7')
export class UINotice7 extends UIbase {
@property({ type: Node, displayName: "滑动器" })
private scrollNode: Node = null;
@property({ type: Label, displayName: "滚动条真实y值" })
private offsetLabel: Label = null;
@property({ displayName: "元素数量" })
private cellNum: number = 50;
@property({ displayName: "无限滚动备选item数量" })
private itemActualCount: number = 18;
@property({ displayName: "每一页item数量" })
private itemPageCount: number = 4;
private scroll;
//拖动窗口高度
private viewHeigh = 0;
private celllHeigh = 0;
public static Instance: UINotice7 = null;
//元素池
// private cellPool: any = [];
//元素下标
private cellIndex = 0;
//滚动条换算起点y
// private transOffsetY = 0;
//数据池
private dataPool: any = [];
//容器触底
private isBottom = false;
_onLoad(): void {
UINotice7.Instance = this;
this.node.active = false;
// CMF.Instance.zIndex(this.node, UIZindex.UINotice2);
this.scroll = this.scrollNode.getComponent(ScrollView)
const cellNode = this.scroll.node.children[0].children[0].children[0];
this.celllHeigh = cellNode.getComponent(UITransform).height;
//生成数据池
for (let index = 0; index < this.cellNum; index++) {
this.dataPool.push(index)
}
console.log("初始化 this.dataPool " + this.dataPool.length)
// //动态创建阵列节点 全部
// for (let index = 0; index < this.cellNum - 1; index++) {
// let insNode = instantiate(cellNode);
// insNode.setParent(this.scroll.node.children[0].children[0]);
// }
//动态创建 有限的阵列节点 ,n = itemActualCount
for (let index = 0; index < this.itemActualCount - 1; index++) {
let insNode = instantiate(cellNode);
insNode.setParent(this.scroll.node.children[0].children[0]);
}
this.initItemCellData()
this.scroll.node.on("scrolling", this.scrolling, this);
this.scroll.node.on("scroll-began", this.scrollBegan, this);
// this.scroll.node.on("scroll-to-top", () => { console.log("scroll-to-top") }, this);
// this.scroll.node.on("scroll-to-bottom", () => { console.log("scroll-to-bottom") }, this);
this.scroll.node.on("scroll-ended", this.scrollEnd, this);
this.viewHeigh = this.scroll.node.children[0].getComponent(UITransform).height;
console.log("窗口可以显示 n行元素: " + this.viewHeigh / this.celllHeigh)
}
//初始化item数据
initItemCellData() {
let contentNode = this.scrollNode.getChildByPath("view/content");
console.log("更新cell 数据下标 " + this.cellIndex)
for (let index = 0; index < contentNode.children.length; index++) {
let element = contentNode.children[index];
if (this.dataPool.length > this.cellIndex + index) {
let dataId = this.dataPool[this.cellIndex + index]
let itemC = element.getComponent(itemCell);
itemC.init(dataId);
element.active = true;
}
else {
element.active = false;
this.isBottom = true;
}
}
}
_onDestroy(): void {
UINotice7.Instance = null;
}
scrolling(scrollview: ScrollView, eventType, customEventData) {
// let tmpPercent = 0.5 //翻页触发百分比
let offsetY = this.scrollNode.getComponent(ScrollView).getScrollOffset().y;
let changeHeigh = this.celllHeigh * 2
let Redundant = this.viewHeigh / 2//冗余的Y空间
// console.log("scrolling " + "," + offsetY_percent.toFixed(2));
if (!this.isBottom && offsetY >= changeHeigh + Redundant) {
//刷新,数据下标 向下翻动一页
this.cellIndex += this.itemPageCount;
console.log("cellIndex + ", this.itemPageCount, this.cellIndex)
this.refreshContent(- changeHeigh);
}
else if (this.cellIndex >= this.itemPageCount && offsetY <= (Redundant - changeHeigh)) {
//刷新,数据下标 向下翻动一页
this.cellIndex -= this.itemPageCount;
this.refreshContent(changeHeigh);
this.isBottom = false;
}
}
scrollBegan(scrollview: ScrollView, eventType, customEventData) {
// this.lastoffsetY = this.scrollNode.getComponent(ScrollView).getScrollOffset().y;
// console.log("scrollBegan " + "," + this.lastoffsetY);
}
scrollEnd(scrollview: ScrollView, eventType, customEventData) {
let offsetY = this.scrollNode.getComponent(ScrollView).getScrollOffset().y;
console.log("scrollEnd " + "," + offsetY);
}
protected update(dt: number): void {
this.offsetLabel.string = "Y坐标 " + this.scrollNode.getComponent(ScrollView).getScrollOffset().y.toFixed(2);
}
//动态刷新容器阵列
private refreshContent(toOffsetY) {
toOffsetY = toOffsetY - 20
if (this.cellIndex > this.dataPool.length - this.itemPageCount) {
this.cellIndex = this.dataPool.length - this.itemPageCount;
}
if (this.cellIndex < 0)
this.cellIndex = 0;
console.log("动态更新阵列 元素下标" + this.cellIndex, "翻页重定位 deltaY " + toOffsetY)
//动态刷新容器阵列
this.initItemCellData()
//更新滚动条y
let offsetY = this.scrollNode.getComponent(ScrollView).getScrollOffset().y;
this.scrollNode.getComponent(ScrollView).scrollToOffset(new Vec2(0, offsetY + toOffsetY))
}
}

浙公网安备 33010602011771号