cocoscreator ScrollView 性能优化

零、ScrollView的短板

    在cocoscreator内,ScrollView控件封装的挺完美的了,不过对于一些对性能要求比较高的场景,会存在问题,以top100排行榜排行榜举例子

   1、应用卡顿甚至崩溃

    按照官方用例使用ScrollView,插入100个玩家的item,理所当然的需要new 100次item。根据设备不同,这一过程可能持续数秒甚至导致程序崩溃

    2、多余的内存占用

    用户永远只看得到其中10个以下的item,其余的90个item将会被mask遮挡在外面,但是每个item依然会产生性能消耗。




一、优化思路分析

    假设一个ScrollView内,用户同时最多可以看到8个item,那么理论上,我们只需要建立8个Item,当触发滑动切换Item展示的时候,将移动到界面外item的数据设置成新Item的数据。

    如此一来,既不用担心同一时间new 100个对象会导致卡顿和崩溃,因为我们最多只new 8个item,也不用担心这除了这8个item会有额外的性能消耗了。

item重复利用



二、代码实现

    1、确定scroll内最大item数量

let maskView = this.node.getChildByName("view")        

// 这个scorllview的长度 ,除以 单个item的长度,得出可以容纳的item数量

let showMaxCount = maskView[this.__diConfig[2]] / (this.item.data[this.__diConfig[2]] + this.space)

// 向下取证,再+2个item,为真实的再大展示数量

// 因为可能存在情况,content在中部显示,底部、顶部两个item各显示一半

showMaxCount = Math.floor(showMaxCount) + 2

this.__showMaxCount = showMaxCount


最大显示n+2


    2、添加layout,使scrollview自动适配大小

let layout = this.__content.getComponent(cc.Layout)

if (!layout) {

    layout = this.__content.addComponent(cc.Layout)

    // 根据scroll的滑动方向,判断layout的排列方向

    layout.type = this.__scrollView.vertical ? 2 : 1

    // 给layout设置spacing

    layout["spacing" + this.__diConfig[1]] = this.space

    // scrollview根据children自动适配size

    layout.resizeMode = 1

} else {

    this.space = layout["spacing" + this.__diConfig[1]]

}


    3、监听scrollEvents事件

// diffPos:两次之间的坐标差, 由此判断用户是往上滑还是往下滑 

let diffPos = pos - this.__lastPosition

if (diffPos < 0) {

    // 向下滑动 

    // 到达顶部,并且保证data前面还有数据

    if (realPos < 0 && this.__drawIndex > 0) {

        this.showItem(true)

    }

} else {

    //向上滑动

    let __maxDiff = this.__content.height - this.__content.parent.height

    let index = this.__drawIndex + this.__showMaxCount

    // 触底,并且保证data后面还有数据

    if (realPos > __maxDiff && index < this.__data.length) {

        this.showItem(false)

    }

}

// showItem的实现

// 单个item的长度或高度

let intervalDisTance = this.item.data[this.__diConfig[2]] + this.space

// 如果是上滑触顶,则将底部的item移动到顶部并设置数据

// 如果是下滑触底,则将顶部的item移动到底部并设置数据

this.__drawIndex = this.__drawIndex + (flag ? -1 : 1)

let nextIndex = flag ? (this.__content.children.length - 1) : 0

let nextChild = this.__content.children[nextIndex]

let index = flag ? this.__drawIndex : (this.__drawIndex + this.__showMaxCount - 1)

let itemData = this.__data[index]

// 这里给item设置数据,并且设置zIndex

this.setItemDate(itemData, index, nextChild)

// 当然由于content边缘被塞入了item,会造成坐标跳跃,此时需要回归跳跃坐标

this.__content[this.__diConfig[0]] += (intervalDisTance * (flag ? 1 : -1))

// scrollview会根据zIndex自动更新children绘制顺序

// 并且由于layout组件存在,会对刷新后的item自动刷新坐标

this.__content.sortAllChildren()

    至此scrollEvents的处理代码已经说明完毕,

    上面的内容比较多,还是针对一些比较重要的点说明下

    a、scrollEvent事件内,根据记录前后两次坐标差值,判断正在上滑还是下滑

    b、如果下滑,并且content坐标为0,说明已经滑倒了顶部,

        如果上滑,并且content坐标 大于最底部坐标,说明已经滑倒了底部,

        此时只要inputData还有数据,则取出数据插入item

    c、插入item的时候,

        如果顶部插入,则拿最底部的item放到最顶部

        如果底部插入,则拿最顶部的item放到最底部

        根据inputData内数据的index值,按顺序设置item的zIndex值,然后this.__content.sortAllChildren()

        那么scrollview将会立马根据index值刷新item的绘制顺序

        而layout组件则会根据item的绘制顺序,自动排列item的坐标

    d、假如目前顶部item是8,触顶后顶部item 7是突然插入进来的,那么整个content的坐标必然会自动跳跃1个itemDistance

        所以content需要去修正1个itemDistance的距离,这样才不会造成视觉上的跳跃,整个滑动呈现是顺畅的



三、你有足够的理由使用这个方案

    我推荐你使用我的方案去实现scrollview的性能优化,理由如下

    1、核心逻辑,只会在滑动的时候触发,甚至我们可以再加上条件if (this.__scrollView.isScrolling(),让核心逻辑只在用户手动拖动scorllview的时候触发,这样scrollview回弹的时候不会浪费性能去计算核心逻辑

    我当初打算做scrollview优化的时候,是打算用cocos论坛的一些高赞方案的。但实际看过源码之后还是决定按自己思路写,因为首先基本所有scrollview的优化组件是通过监听update事件完成的,update事件是每时每刻都执行的,其性能可能比优化前更差。而且组件代码动辄上1、2千行,维护成本实在过于庞大。我希望能够以简单、可读性高的代码,去解决一些技术难点。

    2、由于layout的存在,我们减少了一些核心代码的编写,比如item坐标的维护,content大小的维护,对于代码维护是十分有帮助的,并且由于layout是引擎自带组件,我们可以信任它的稳定性。

    3、由于在触顶和触底的时候,插入item的同时,会刷新content的坐标,

        原本在触顶或触底导致不能继续滑动,但是在刷新坐标后,会多出一个itemDistance的滑动距离,所以不会造成scorllview触发边界回弹的现象,

        而scrollEvents因为是毫秒级的,所以整个滑动是顺畅的

    4、上述方案设计出的脚本使用及其简单,只需要将脚本放置于scrollview的节点之上,并且绑定item的prefab

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容

  • ScrollView是比较常用的UI组件之一,游戏中的任务榜、排行榜都少不了它,实际使用中存在一个问题,例如:在排...
    potato865阅读 3,934评论 0 4
  • 项目中用到了商品详情展示效果,所以立马想到借鉴天猫商品详情界面,看了天猫的详情页面想到了两套解决方案。1,使用Li...
    ___ayo阅读 9,606评论 10 23
  • Documentation Supported OS & SDK Versions 支持的OS & SDK版本 S...
    c5550ea746f8阅读 4,345评论 0 2
  • 1.badgeVaule气泡提示 2.git终端命令方法> pwd查看全部 >cd>ls >之后桌面找到文件夹内容...
    i得深刻方得S阅读 4,654评论 1 9
  • 分工 ▌故事:一位年轻的炮兵军官上任后,到下属部队视察操练情况,发现有几个部队操练时有一个共同的情况:在操练中,总...
    洱海Amy阅读 191评论 0 0