123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164 |
- /**
- * @Author wutianhao
- * @ctime 2021-07-13
- * @Version
- * ScrollBeta1.0 2021-07-13 ScrollFinal改版 支持带标题的滚动列表 具体用法可参照天赋技能预览界面
- * @Tips
- * 复用滚动轴 用来减少drawcall
- * 与cc.ScrollView组件一同挂载在一个节点上
- * item挂载的脚本必须添加setData方法,用来传递数据
- * item锚点应该在中心
- * 目前Grid类型只支持从左上到右下模式(垂直滚动),其他奇葩模式自己搞定
- * 滚动轴的锚点必须放在滚动列表的起始位置(比如背包grid模式在左上角,成就列表在左上角)
- */
- import AssetMgr from "./AssetMgr";
- // 对象池类型
- let PoolEnum = cc.Enum({
- /**通用道具 */
- ITEM_BASE: 0,
- /**背包道具 */
- ITEM_BAG: 1,
- // /**合成道具 */
- // ITEM_COMPOSE: 2,
- })
- // 滚动类型
- let ScrollDirEnum = cc.Enum({
- /**垂直*/
- VERTICAL: 0,
- /**水平*/
- HORIZONTAL: 1,
- /**背包*/
- GRID: 2
- })
- type ItemList = { index: number, node: cc.Node }
- const { ccclass, property, menu } = cc._decorator;
- @ccclass
- @menu('Scroll/ScrollBeta')
- export default class ScrollBeta extends cc.Component {
- // @property(cc.ScrollView)
- // scroll: cc.ScrollView | null = null
- @property({
- tooltip: "使用对象池"
- })
- useNodePool: boolean = false
- @property({
- type: PoolEnum,
- visible: function () { return this.useNodePool },
- tooltip: "对象池类型"
- })
- private poolType = PoolEnum.ITEM_BASE
- @property({
- type: ScrollDirEnum,
- visible: () => {
- return true
- },
- tooltip: "滚动类型"
- })
- private scrollDir = ScrollDirEnum.VERTICAL // 滚动类型
- @property({ tooltip: "与滚动层的边界-左" })
- private padingX: number = 10
- @property({ tooltip: "与滚动层的边界-上" })
- private padingY: number = 10
- @property({ tooltip: "与滚动层的边界-下" })
- private padingY2: number = 0
- @property({
- visible: function () { return this.scrollDir != ScrollDirEnum.VERTICAL }, tooltip: "item行间距"
- })
- private spacingX: number = 20
- @property({
- visible: function () { return this.scrollDir != ScrollDirEnum.HORIZONTAL }, tooltip: "item列间距"
- })
- private spacingY: number = 20
- @property(cc.Prefab)
- itemPrefab: cc.Prefab = null // item资源加载地址
- @property(cc.Prefab)
- itemPrefabTitle: cc.Prefab = null // item标题资源加载地址
- @property
- itemScript: string = ""// item挂在的脚本名
- @property
- itemScriptTitle: string = ""// item标题挂在的脚本名
- @property
- itemScale: number = 1 // item缩放比例
- @property({
- tooltip: "是否开启滚动惯性"
- })
- inertia: boolean = true
- @property({
- tooltip: "开启惯性后,在用户停止触摸后滚动多块停止,0表示永不停止,1表示立即停止",
- visible: function () { return this.inertia == true },
- })
- brake: number = 0.75
- @property({
- tooltip: "是否允许滚动内容超过边界,并在停止触摸后回弹"
- })
- elastic: boolean = true
- @property({
- tooltip: "滚动行为是否会取消子节点上注册的触摸事件",
- })
- cancelInnerEvents: boolean = true
- @property({
- tooltip: "展示生产动画\n0->不展示\n1->缩放动画"
- })
- showAnim: number = 1
- @property({
- tooltip: "创建item的延迟时间,设为0则为每帧生成。\n注意:在所有item刷新完之前,scroll组件的滚动功能将被关闭"
- })
- ctime: number = 0
- @property({
- tooltip: "每帧生成item的个数"
- })
- cnumber: number = 1
- scrollView: cc.ScrollView
- mask: cc.Node
- content: cc.Node
- itemDataList: any[] = [] // 当前显示阵营的所有数据
- itemDataListMain: any[] = [] // 当前显示主数据的所有数据
- itemDataListTitle: any[] = [] // 当前显示标题的所有数据
- private extraParams: any[] = [] // 额外数据
- private itemListMain: ItemList[] = [] // 实例化的item列表
- private itemListTitle: ItemList[] = [] // 实例化的item标题列表
- private instantiateCount: number = 0 // item实例化数量
- private instantiateCountTitle: number = 0 // item标题实例化数量
- private hangCount: number = 0 // 行个数
- private lieCount: number = 0 // 列个数
- private itemDistanceX: number = 0 // item中心点之间的距离
- private itemDistanceXTitle: number = 0 // item标题中心点之间的距离
- private itemDistanceY: number = 0 // item中心点之间的距离
- private itemDistanceYTitle: number = 0 // item标题中心点之间的距离
- private scrollMaxOffsetX: number = 0 // 最大可滚动区域X
- private scrollMaxOffsetY: number = 0 // 最大可滚动区域Y
- private lastScrollPos: number = 0 //上一次的滚动位置
- private curScrollPos: number = 0 // 当前滚动位置
- private isScrollUp: boolean = false // 当前往哪个方向滚动 左和上是true
- private itemWidth: number = 10 // item宽度
- private itemWidthTitle: number = 10 // item标题宽度
- private itemHeight: number = 10 // item高度
- private itemHeightTitle: number = 10 // item标题高度
- private tagLang: number = 0
- private tagIndex: number = -999 // 999 表示清0状态,此时无【插入标签】,-1表示标签置顶,其他即当前标签的下方(右侧)显示
- private canCreateItem: boolean = false // 可以生成item
- private createIndex: number = 0 // 生成item的数据标签
- private life: number = 0 // 生成item的时间
- private baseIndex: number = 0 // 基础标签位置
- private hasInit: boolean = false
- offset: number = 0 // 偏移量
- onLoad() {
- if (this.node.getComponent(cc.ScrollView) != null) {
- console.error("滚动节点无需挂载scrollView组件了")
- return
- }
- /////////////// 构建滚动轴 ///////////////
- this.scrollView = this.addComponent(cc.ScrollView)
- this.scrollView.horizontal = this.scrollDir == ScrollDirEnum.HORIZONTAL
- this.scrollView.vertical = this.scrollDir != ScrollDirEnum.HORIZONTAL
- this.scrollView.inertia = this.inertia
- this.scrollView.brake = this.brake
- this.scrollView.elastic = this.elastic
- this.scrollView.cancelInnerEvents = this.cancelInnerEvents
- /////////////// 构建滚动遮罩 ///////////////
- this.mask = new cc.Node()
- this.mask.parent = this.node
- this.mask.setContentSize(this.node.getContentSize())
- this.mask.addComponent(cc.Mask)
- this.mask.getComponent(cc.Mask).type = cc.Mask.Type.RECT
- this.mask.anchorX = this.node.anchorX
- this.mask.anchorY = this.node.anchorY
- this.mask.x = 0
- this.mask.y = 0
- // widget不需要加了
- // let maskWidget = mask.addComponent(cc.Widget)
- // maskWidget.isAlignTop = true
- // maskWidget.isAlignBottom = true
- // maskWidget.isAlignLeft = true
- // maskWidget.isAlignRight = true
- // maskWidget.top = 0
- // maskWidget.bottom = 0
- // maskWidget.left = 0
- // maskWidget.right = 0
- /////////////// 构建滚动内容器 ///////////////
- this.content = new cc.Node()
- this.content.parent = this.mask
- this.scrollView.content = this.content
- this.content.setContentSize(this.node.getContentSize())
- this.content.anchorX = this.node.anchorX
- this.content.anchorY = this.node.anchorY
- this.content.x = 0
- this.content.y = 0
- }
- resetSize() {
- if (this.mask) {
- this.mask.setContentSize(this.node.getContentSize())
- }
- if (this.content) {
- this.content.setContentSize(this.node.getContentSize())
- }
- }
- private init() {
- if (this.hasInit) { return }
- this.hasInit = true
- this.itemWidth = this.itemPrefab.data.getContentSize().width
- this.itemHeight = this.itemPrefab.data.getContentSize().height
- this.itemWidthTitle = this.itemPrefabTitle.data.getContentSize().width
- this.itemHeightTitle = this.itemPrefabTitle.data.getContentSize().height
- this.itemDistanceX = this.realItemWidth + this.spacingX
- this.itemDistanceY = this.realItemHeight + this.spacingY
- this.itemDistanceXTitle = this.itemWidthTitle + this.spacingX
- this.itemDistanceYTitle = this.itemHeightTitle + this.spacingY
- this.scrollView.node.on('scrolling', this.onScroll, this)
- //看下有没有不应该有的组件
- // Layout
- if (this.scrollView.content.getComponent(cc.Layout)) {
- console.error("scrollFinal 与 layout 冲突,清删除 content 中的 layout 组件")
- }
- // Widget
- if (this.scrollView.node.getComponent(cc.Widget)) {
- if (this.scrollView.node.getComponent(cc.Widget).isAlignTop &&
- this.scrollView.node.getComponent(cc.Widget).isAlignBottom) {
- console.error("不能用widget做长度适配(因为Widget的延迟),只可用作坐标适配")
- }
- }
- }
- private setInstantCount() {
- if (this.scrollView == null) {
- return
- }
- switch (this.scrollDir) {
- case ScrollDirEnum.VERTICAL:
- this.hangCount = 1
- this.lieCount = Math.ceil(this.scrollView.node.height / (this.itemDistanceY)) + 1
- this.instantiateCount = this.lieCount
- break
- case ScrollDirEnum.HORIZONTAL:
- this.hangCount = Math.ceil(this.scrollView.node.width / (this.itemDistanceX)) + 1
- this.lieCount = 1
- this.instantiateCount = this.hangCount
- break
- case ScrollDirEnum.GRID:
- this.hangCount = Math.floor(this.scrollView.node.width / (this.itemDistanceX))
- this.lieCount = Math.ceil(this.scrollView.node.height / (this.itemDistanceY)) + 1
- this.instantiateCount = this.hangCount * this.lieCount
- break
- }
- this.instantiateCountTitle = this.itemDataListTitle.length
- }
- indexFrom: { [index: number]: number } = {}
- initScrollView(itemDataList: any[] = [], ...args: any[]) {
- // this.scheduleOnce(() => {
- this.init()
- this.clear()
- this.clearTag()
- this.itemDataList = itemDataList
- this.refreshData()
- this.extraParams = args
- this.scrollView.stopAutoScroll()
- this.setInstantCount()
- this.showUI()
- // }, 0)
- }
- refreshData() {
- this.itemDataListMain = []
- this.itemDataListTitle = []
- this.indexFrom = {}
- let count = 0
- this.itemDataList.forEach((element, index) => {
- element[0].forEach(_element => {
- this.itemDataListMain.push(_element)
- });
- this.itemDataListTitle.push(element[1])
- count += element[0].length
- this.indexFrom[index] = count
- });
- }
- private getPositionInView(item: cc.Node): { x: number, y: number } {
- if (this.scrollView == null) { return cc.v2(0, 0) }
- let worldPos = item.parent.convertToWorldSpaceAR(item.position);
- let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
- return viewPos;
- }
- private onScroll() {
- this.curScrollPos = 0
- if (this.scrollDir == ScrollDirEnum.HORIZONTAL) {
- this.curScrollPos = this.scrollView.getScrollOffset().x
- this.isScrollUp = this.curScrollPos < this.lastScrollPos
- } else {
- this.curScrollPos = this.scrollView.getScrollOffset().y
- this.isScrollUp = this.curScrollPos > this.lastScrollPos
- }
- this.lastScrollPos = this.curScrollPos
- if (this.scrollView == null) { return }
- this.itemListMain.forEach(ele => {
- this.tryResetItem(ele)
- })
- // this.itemListTitle.forEach(ele => {
- // this.tryResetItemTitle(ele)
- // })
- }
- private tryResetItem(ele: ItemList) {
- switch (this.scrollDir) {
- case ScrollDirEnum.HORIZONTAL:
- if (this.curScrollPos >= 0 || this.curScrollPos <= this.scrollMaxOffsetX) {
- return
- }
- break
- case ScrollDirEnum.VERTICAL:
- case ScrollDirEnum.GRID:
- if (this.curScrollPos <= -this.realItemHeight / 2 || this.curScrollPos >= this.scrollMaxOffsetY + this.realItemHeight / 2) {
- return
- }
- }
- let scrollWidth = this.scrollView.node.width
- let scrollHeight = this.scrollView.node.height
- let element = ele.node
- switch (this.scrollDir) {
- case ScrollDirEnum.HORIZONTAL:
- if (this.isScrollUp && this.getPositionInView(element).x < -(this.itemDistanceX / 2)) {
- // 超出左边界显示区域
- let idx = ele.index + this.instantiateCount
- if (idx < this.itemDataListMain.length) {
- this.setItemData(element, this.itemDataListMain[idx], idx)
- // element.x = element.x + this.hangCount * this.itemDistanceX
- ele.index = idx
- this.setPosX(idx, element)
- }
- } else if (!this.isScrollUp && this.getPositionInView(element).x > (scrollWidth + this.itemDistanceX / 2)) {
- // 超出右边界显示区域
- let idx = ele.index - this.instantiateCount
- if (idx >= 0) {
- this.setItemData(element, this.itemDataListMain[idx], idx)
- // element.x = element.x - this.hangCount * this.itemDistanceX
- ele.index = idx
- this.setPosX(idx, element)
- }
- }
- break
- case ScrollDirEnum.VERTICAL:
- case ScrollDirEnum.GRID:
- if (this.isScrollUp && this.getPositionInView(element).y > this.itemDistanceY / 2) {
- // 超出上边界显示区域
- let idx = ele.index + this.instantiateCount
- if (idx < this.itemDataListMain.length && this.isScrollUp) {
- this.setItemData(element, this.itemDataListMain[idx], idx)
- ele.index = idx
- this.setPosX(idx, element)
- this.setPosY(idx, element)
- }
- } else if (!this.isScrollUp && this.curScrollPos > -this.itemDistanceY / 2 && this.getPositionInView(element).y < -(scrollHeight + this.itemDistanceY / 2)) {
- // 超出下边界显示区域
- let idx = ele.index - this.instantiateCount
- if (idx >= 0 && this.isScrollUp == false) {
- this.setItemData(element, this.itemDataListMain[idx], idx)
- ele.index = idx
- this.setPosX(idx, element)
- this.setPosY(idx, element)
- }
- }
- break
- }
- }
- private tryResetItemTitle(ele: ItemList) {
- switch (this.scrollDir) {
- case ScrollDirEnum.HORIZONTAL:
- if (this.curScrollPos >= 0 || this.curScrollPos <= this.scrollMaxOffsetX) {
- return
- }
- break
- case ScrollDirEnum.VERTICAL:
- case ScrollDirEnum.GRID:
- if (this.curScrollPos <= -this.itemHeightTitle / 2 || this.curScrollPos >= this.scrollMaxOffsetY + this.itemHeightTitle / 2) {
- return
- }
- }
- let scrollWidth = this.scrollView.node.width
- let scrollHeight = this.scrollView.node.height
- let element = ele.node
- switch (this.scrollDir) {
- case ScrollDirEnum.HORIZONTAL:
- if (this.isScrollUp && this.getPositionInView(element).x < -(this.itemDistanceXTitle / 2)) {
- // 超出左边界显示区域
- let idx = ele.index + this.instantiateCountTitle
- if (idx < this.itemDataListTitle.length) {
- this.addItemNodeTitle(idx, this.itemDataListTitle[idx])
- }
- } else if (!this.isScrollUp && this.getPositionInView(element).x > (scrollWidth + this.itemDistanceX / 2)) {
- // 超出右边界显示区域
- let idx = ele.index - this.instantiateCountTitle
- if (idx >= 0) {
- this.addItemNodeTitle(idx, this.itemDataListTitle[idx])
- }
- }
- break
- case ScrollDirEnum.VERTICAL:
- case ScrollDirEnum.GRID:
- if (this.isScrollUp && this.getPositionInView(element).y > this.itemDistanceYTitle / 2) {
- // 超出上边界显示区域
- let idx = ele.index + this.instantiateCountTitle
- if (idx < this.itemDataListTitle.length && this.isScrollUp) {
- this.addItemNodeTitle(idx, this.itemDataListTitle[idx])
- }
- } else if (!this.isScrollUp && this.curScrollPos > -this.itemDistanceYTitle / 2 && this.getPositionInView(element).y < -(scrollHeight + this.itemDistanceYTitle / 2)) {
- // 超出下边界显示区域
- let idx = ele.index - this.instantiateCountTitle
- if (idx >= 0 && this.isScrollUp == false) {
- this.addItemNodeTitle(idx, this.itemDataListTitle[idx])
- }
- }
- break
- }
- }
- checkIndexFromItem(index: number): number {
- let _index = 0
- for (let i = 0; i < index; i++) {
- const element = this.itemDataList[i][0]
- _index += element.length
- }
- return _index
- }
- checkIndexFromTitle(index: number): number {
- for (let i = 0; i < this.itemDataList.length; i++) {
- if (index < this.indexFrom[i]) {
- return i
- }
- }
- }
- checkAddTitle(index: number): boolean {
- let from = this.checkIndexFromTitle(index)
- if (from > 0) {
- return index == this.indexFrom[from - 1]
- } else {
- return index == 0
- }
- }
- private setPosX(index: number, node?: cc.Node): number {
- let x = 0
- switch (this.scrollDir) {
- case ScrollDirEnum.VERTICAL:
- x = this.scrollView.content.width / 2 + this.padingX//this.realItemWidth / 2 + this.padingX
- break
- case ScrollDirEnum.HORIZONTAL:
- x = index * this.itemDistanceX + this.realItemWidth / 2 + this.padingX + ((this.checkIndexFromTitle(index) + 1) * this.itemDistanceXTitle)
- if (this.tagIndex >= -1 && index > this.tagIndex) {
- x += this.tagLang
- }
- break
- case ScrollDirEnum.GRID:
- let _index = this.checkIndexFromTitle(index)
- let count = _index > 0 ? this.indexFrom[_index - 1] : 0
- x = (index - count) % this.hangCount * this.itemDistanceX + this.realItemWidth / 2 + this.padingX
- break
- }
- if (node) {
- node.x = x
- }
- return x
- }
- private setPosY(index: number, node?: cc.Node): number {
- let y = 0
- switch (this.scrollDir) {
- case ScrollDirEnum.VERTICAL:
- y = -index * this.itemDistanceY - this.realItemHeight / 2 - this.padingY -
- ((this.checkIndexFromTitle(index) + 1) * this.itemDistanceYTitle) + this.offset
- if (this.tagIndex >= -1 && index > this.tagIndex) {
- y -= this.tagLang
- }
- break
- case ScrollDirEnum.HORIZONTAL:
- y = -this.scrollView.content.height / 2 + this.padingY//-this.realItemHeight / 2 - this.padingY
- break
- case ScrollDirEnum.GRID:
- let _index = this.checkIndexFromTitle(index)
- let extra = 0
- if (_index > 0) {
- for (let i = 0; i < _index; i++) {
- extra += Math.ceil(this.itemDataList[i][0].length / this.hangCount)
- }
- }
- let count = _index > 0 ? this.indexFrom[_index - 1] : 0
- y = -(Math.floor((index - count) / this.hangCount) + extra) * this.itemDistanceY - this.realItemHeight / 2 - this.padingY -
- ((this.checkIndexFromTitle(index) + 1) * this.itemDistanceYTitle) + this.offset
- if (this.tagIndex >= -1 && (Math.floor((index - count) / this.hangCount) + extra) > this.tagIndex) {
- y -= this.tagLang
- }
- break
- }
- if (node) {
- node.y = y
- }
- return y
- }
- private setPosXTitle(index: number, node?: cc.Node): number {
- let x = 0
- switch (this.scrollDir) {
- case ScrollDirEnum.VERTICAL:
- x = this.scrollView.content.width / 2 + this.padingX//this.realItemWidth / 2 + this.padingX
- break
- case ScrollDirEnum.HORIZONTAL:
- x = this.checkIndexFromItem(index) * this.itemDistanceX + this.realItemWidth / 2 + this.padingX +
- ((index + 1) * this.itemDistanceXTitle) - this.realItemWidth / 2 - this.spacingX - this.itemWidthTitle / 2
- if (this.tagIndex >= -1 && index > this.tagIndex) {
- x += this.tagLang
- }
- break
- case ScrollDirEnum.GRID:
- x = this.scrollView.content.width / 2
- break
- }
- if (node) {
- node.x = x
- }
- return x
- }
- private setPosYTitle(index: number, node?: cc.Node): number {
- let y = 0
- switch (this.scrollDir) {
- case ScrollDirEnum.VERTICAL:
- y = -this.checkIndexFromItem(index) * this.itemDistanceY - this.realItemHeight / 2 - this.padingY -
- ((index + 1) * this.itemDistanceYTitle) + this.realItemHeight / 2 + this.spacingY + this.itemHeightTitle / 2
- break
- case ScrollDirEnum.HORIZONTAL:
- y = -this.scrollView.content.height / 2 + this.padingY//-this.realItemHeight / 2 - this.padingY
- break
- case ScrollDirEnum.GRID:
- let extra = 0
- if (index > 0) {
- for (let i = 0; i < index; i++) {
- extra += Math.ceil(this.itemDataList[i][0].length / this.hangCount)
- }
- }
- let count = index > 0 ? this.indexFrom[index - 1] : 0
- y = -(Math.floor((this.checkIndexFromItem(index) - count) / this.hangCount) + extra) * this.itemDistanceY - this.realItemHeight / 2 - this.padingY -
- ((index + 1) * this.itemDistanceYTitle) + this.realItemHeight / 2 + this.spacingY + this.itemHeightTitle / 2
- if (this.tagIndex >= -1 && (Math.floor((index - count) / this.hangCount) + extra) > this.tagIndex) {
- y -= this.tagLang
- }
- break
- }
- if (node) {
- node.y = y
- }
- return y
- }
- private setItemData(itemNode: cc.Node, data: any, index: number, isRefresh: boolean = false) {
- try {
- itemNode.getComponent(this.itemScript).setData(data, index, this.extraParams)
- if (isRefresh == false) {
- let addTitle = this.checkAddTitle(index)
- if (addTitle) {
- this.addItemNodeTitle(this.checkIndexFromTitle(index), this.itemDataListTitle[this.checkIndexFromTitle(index)])
- }
- }
- } catch (error) {
- console.error("脚本中缺少setData方法,或者方法报错", error)
- }
- }
- private setItemDataTitle(itemNode: cc.Node, data: any, index: number) {
- try {
- itemNode.getComponent(this.itemScriptTitle).setData(data, index, this.extraParams)
- } catch (error) {
- console.error("脚本中缺少setData方法,或者方法报错", error)
- }
- }
- // refreshItems调用,在刷新时可能需要重置item的index标签
- private resetIndex(index: number): number {
- if (this.itemDataListMain[index] != null) {
- return index
- }
- return this.resetIndex(index - this.instantiateCount)
- }
- // 刷新单独的item
- refreshItem(index: number, data) {
- if (this.itemDataListMain[index] == null) {
- return
- }
- this.itemDataListMain[index] = data
- if (this.getItem(index) == null) {
- return
- }
- this.setItemData(this.getItem(index), this.itemDataListMain[index], index)
- }
- // refreshItem(index: number) {
- // this.setItemData(this.getItem(index), this.itemDataListMain[index], index)
- // }
- refreshItems(itemDataList: any[], ...args: any[]) {
- this.itemDataList = itemDataList
- this.refreshData()
- if (args.length > 0) {
- this.extraParams = args
- }
- this.fixItemNodes()
- // 最终构造完整的 itemList 列表,刷新数据
- this.itemListMain.forEach(element => {
- try {
- let newIndex = this.resetIndex(element.index)
- element.index = newIndex
- this.setItemData(element.node, this.itemDataListMain[element.index], element.index)
- this.setPosX(element.index, element.node)
- this.setPosY(element.index, element.node)
- // element.node.getComponent(this.itemScript).setData(this.itemDataListMain[element.index], element.index, this.extraParams)
- } catch (error) {
- console.warn("脚本中缺少refreshItem方法,或者方法报错", error)
- }
- })
- }
- // 数据新增或减少时,增加或减少item
- fixItemNodes() {
- // 判断是否需要删除 itemList 里的数据
- if (this.itemDataListMain.length < this.instantiateCount && this.itemListMain.length > this.itemDataListMain.length) {
- let needDeleteCount = this.itemListMain.length - this.itemDataListMain.length
- for (let index = this.itemDataListMain.length; index < this.itemDataListMain.length + needDeleteCount; index++) {
- this.itemListMain[index].node.destroy()
- }
- this.itemListMain.splice(this.itemDataListMain.length, needDeleteCount)
- // 判断是否需要增加 itemList 里的数据
- } else if (this.itemListMain.length < this.instantiateCount && this.itemListMain.length < this.itemDataListMain.length) {
- let addCount = Math.min(this.instantiateCount - this.itemListMain.length, this.itemDataListMain.length - this.itemListMain.length)
- let startIndex = 0
- this.itemListMain.forEach(element => {
- startIndex = Math.max(element.index + 1, startIndex)
- });
- for (let addIndex = startIndex; addIndex < (startIndex + addCount); addIndex++) {
- this.addItemNode(addIndex, this.itemDataListMain[addIndex], true)
- }
- }
- // 判断是否需要删除 itemList 里的数据
- if (this.itemDataListTitle.length < this.instantiateCountTitle && this.itemListTitle.length > this.itemDataListTitle.length) {
- let needDeleteCount = this.itemListTitle.length - this.itemDataListTitle.length
- for (let index = this.itemDataListTitle.length; index < this.itemDataListTitle.length + needDeleteCount; index++) {
- this.itemListTitle[index].node.destroy()
- }
- this.itemListTitle.splice(this.itemDataListTitle.length, needDeleteCount)
- }
- this.instantiateCountTitle = this.itemDataListTitle.length
- if (this.content) {
- this.setScrollContentSize()
- }
- }
- private get realItemWidth(): number {
- return this.itemWidth * this.itemScale
- }
- private get realItemHeight(): number {
- return this.itemHeight * this.itemScale
- }
- private initItemNode(): cc.Node {
- return AssetMgr.instantiate(this.node, this.itemPrefab, false)
- // @TODO PoolManager
- // if (this.useNodePool) {
- // if (this.poolType == PoolEnum.ITEM_BAG) {
- // return PoolManager.getItemBag(this.itemPrefab)
- // } else if (this.poolType == PoolEnum.ITEM_BASE) {
- // return PoolManager.getItemBase(this.itemPrefab)
- // }
- // } else {
- // return cc.instantiate(this.itemPrefab)
- // }
- }
- private _initItemNode(): cc.Node {
- return AssetMgr.instantiate(this.node, this.itemPrefabTitle, false)
- // @TODO PoolManager
- // if (this.useNodePool) {
- // if (this.poolType == PoolEnum.ITEM_BAG) {
- // return PoolManager.getItemBag(this.itemPrefab)
- // } else if (this.poolType == PoolEnum.ITEM_BASE) {
- // return PoolManager.getItemBase(this.itemPrefab)
- // }
- // } else {
- // return cc.instantiate(this.itemPrefab)
- // }
- }
- private showUI() {
- if (this.itemPrefab == null || this.itemPrefabTitle == null) {
- console.error("item预制体加载失败")
- return
- }
- if (this.scrollView == null) {
- console.error("没有绑定scroll")
- return
- }
- this.setCreateItems(true)
- this.scrollView.content.setAnchorPoint(0, 1)
- this.scrollView.content.setPosition(-this.scrollView.node.width / 2, this.scrollView.node.height / 2)
- this.setScrollContentSize()
- switch (this.scrollDir) {
- case ScrollDirEnum.VERTICAL:
- this.scrollView.scrollToTop()
- break
- case ScrollDirEnum.HORIZONTAL:
- this.scrollView.scrollToLeft()
- break
- case ScrollDirEnum.GRID:
- this.scrollView.scrollToTop()
- break
- }
- }
- private setCreateItems(bool: boolean) {
- if (bool) {
- this.scrollView.enabled = false
- this.canCreateItem = true
- this.createIndex = 0
- this.baseIndex = 0
- this.life = 0
- // this._canCreateItem = true
- // this._createIndex = 0
- // this._life = 0
- } else {
- this.scrollView.enabled = true
- this.canCreateItem = false
- this.createIndex = 0
- this.baseIndex = 0
- this.life = 0
- // this._canCreateItem = false
- // this._createIndex = 0
- // this._life = 0
- }
- }
- update(dt: number) {
- if (!this.canCreateItem) {
- return
- }
- for (let index = 0; index < this.cnumber; index++) {
- this.updateForCreateItem(dt)
- }
- // if (!this._canCreateItem) {
- // return
- // }
- // for (let index = 0; index < this.cnumber; index++) {
- // this._updateForCreateItem(dt)
- // }
- }
- private updateForCreateItem(dt: number) {
- if (!this.canCreateItem) {
- return
- }
- if (this.life == 0) {
- // 生
- let _itemData = this.itemDataListMain[this.baseIndex]
- if (this.createIndex >= this.instantiateCount || _itemData == null) {
- this.setCreateItems(false)
- return
- }
- this.addItemNode(this.baseIndex, _itemData)
- this.createIndex += 1
- this.baseIndex += 1
- }
- this.life += dt
- if (this.life >= this.ctime) {
- this.life = 0
- }
- }
- private setScrollContentSize() {
- let val = 0
- switch (this.scrollDir) {
- case ScrollDirEnum.VERTICAL:
- for (let i = 0; i < this.itemDataList.length; i++) {
- val += this.itemDistanceYTitle
- for (let j = 0; j < this.itemDataList[i][0].length; j++) {
- val += this.itemDistanceY
- }
- }
- this.scrollView.content.setContentSize(this.scrollView.node.width, val + this.padingY + this.padingY2 + this.tagLang)
- break
- case ScrollDirEnum.HORIZONTAL:
- for (let i = 0; i < this.itemDataList.length; i++) {
- val += this.itemDistanceXTitle
- for (let j = 0; j < this.itemDataList[i][0].length; j++) {
- val += this.itemDistanceX
- }
- }
- this.scrollView.content.setContentSize(val + this.padingX + this.tagLang, this.scrollView.node.height)
- break
- case ScrollDirEnum.GRID:
- for (let i = 0; i < this.itemDataList.length; i++) {
- val += this.itemDistanceYTitle
- for (let j = 0; j < Math.ceil(this.itemDataList[i][0].length / this.hangCount); j++) {
- val += this.itemDistanceY
- }
- }
- this.scrollView.content.setContentSize(this.scrollView.node.width, val + this.padingY + this.padingY2 + this.tagLang)
- break
- }
- this.scrollMaxOffsetX = -this.scrollView.getMaxScrollOffset().x
- this.scrollMaxOffsetY = this.scrollView.getMaxScrollOffset().y
- }
- /**
- * 弹出详情标签,将会重设后续item的坐标。
- * @param index item标签
- * @param lang 坐标偏移量
- */
- setTag(index: number, lang: number) {
- this.tagLang = lang
- switch (this.scrollDir) {
- case ScrollDirEnum.VERTICAL:
- case ScrollDirEnum.HORIZONTAL:
- this.tagIndex = index
- break
- case ScrollDirEnum.GRID:
- this.tagIndex = Math.floor(index / this.hangCount)
- break
- }
- this.setScrollContentSize()
- this.itemListMain.forEach((element) => {
- this.setPosX(element.index, element.node)
- this.setPosY(element.index, element.node)
- });
- }
- /**清除详情标签,恢复item默认坐标 */
- clearTag() {
- this.setTag(-999, 0)
- // 修正index和坐标
- let scrollHeight = this.scrollView.node.height
- this.itemListMain.forEach(ele => {
- // 判断是否超出边界
- if (this.getPositionInView(ele.node).y > this.itemDistanceY / 2) {
- if (ele.index + this.instantiateCount < this.itemDataListMain.length) {
- ele.index += this.instantiateCount
- }
- this.setPosY(ele.index, ele.node)
- this.setItemData(ele.node, this.itemDataListMain[ele.index], ele.index)
- } else if (this.getPositionInView(ele.node).y < -(scrollHeight + this.itemDistanceY / 2)) {
- if (ele.index - this.instantiateCount >= 0) {
- ele.index -= this.instantiateCount
- }
- this.setPosY(ele.index, ele.node)
- this.setItemData(ele.node, this.itemDataListMain[ele.index], ele.index)
- }
- })
- this.setScrollContentSize()
- }
- /**删除某个元素 */
- del(index: number) {
- if (this.itemDataListMain.length < index) { return }
- this.itemDataListMain.splice(index, 1)
- // // 判断下是否需要删除节点
- if (this.itemListMain.length > this.itemDataListMain.length) {
- this.itemListMain.pop().node.destroy()
- }
- this.setScrollContentSize()
- for (let index = 0; index < this.itemListMain.length; index++) {
- let element = this.itemListMain[index]
- if (this.itemDataListMain[element.index] == null) {
- element.index -= this.instantiateCount
- break
- }
- }
- this.itemListMain.forEach((element) => {
- this.setPosX(element.index, element.node)
- this.setPosY(element.index, element.node)
- this.setItemData(element.node, this.itemDataListMain[element.index], element.index)
- });
- }
- /**在末尾添加一个元素 */
- add(data: any) {
- this.itemDataListMain.push(data)
- // 判断是否需要增加item
- if (this.itemListMain.length >= this.instantiateCount) {
- // 不需要
- // 判断下是否需要把最前面的放到最后面:1
- let needUpdate = false
- for (let index = 0; index < this.itemListMain.length; index++) {
- if (this.itemListMain[index].index == this.itemDataListMain.length - 2) {
- needUpdate = true
- break
- }
- }
- if (needUpdate) {
- let minIndex = 1000 //这个是最小值
- let minUpdateIndex = 0// 这个是最小值的标签
- for (let i = 0; i < this.itemListMain.length; i++) {
- if (this.itemListMain[i].index < minIndex) {
- minIndex = this.itemListMain[i].index
- minUpdateIndex = i
- }
- }
- // 判断下是否需要把最前面的放到最后面:2
- let _nodePos = 0
- let _offset = 0
- let needUpdate2 = false
- switch (this.scrollDir) {
- case ScrollDirEnum.HORIZONTAL:
- _nodePos = this.itemListMain[minUpdateIndex].node.x
- _offset = this.scrollView.getScrollOffset().x
- needUpdate2 = _nodePos + _offset > this.itemWidth / 2
- break
- case ScrollDirEnum.VERTICAL:
- case ScrollDirEnum.GRID:
- _nodePos = this.itemListMain[minUpdateIndex].node.y
- _offset = this.scrollView.getScrollOffset().y
- needUpdate2 = _nodePos + _offset > this.itemHeight / 2
- break
- }
- if (needUpdate2) {
- this.itemListMain[minUpdateIndex].index += this.instantiateCount
- this.setItemData(this.itemListMain[minUpdateIndex].node, data, this.itemListMain[minUpdateIndex].index)
- this.setPosX(this.itemListMain[minUpdateIndex].index, this.itemListMain[minUpdateIndex].node)
- this.setPosY(this.itemListMain[minUpdateIndex].index, this.itemListMain[minUpdateIndex].node)
- }
- }
- } else {
- // 需要
- let index = this.itemDataListMain.length - 1
- this.addItemNode(index, data)
- }
- this.setScrollContentSize()
- }
- private addItemNode(index: number, data: any, isRefresh: boolean = false) {
- let _node = this.initItemNode()
- this.scrollView.content.addChild(_node)
- this.setItemData(_node, data, index, isRefresh)
- this.itemListMain.push({ index: index, node: _node })
- this.setPosX(index, _node)
- this.setPosY(index, _node)
- if (isRefresh || this.showAnim == 0) {
- _node.scale = this.itemScale
- } else if (this.showAnim == 1) {
- _node.scale = 0
- cc.tween(_node).
- to(0.1, { scale: this.itemScale + 0.1 }).
- to(0.1, { scale: this.itemScale }).
- start()
- } else {
- _node.scale = this.itemScale
- }
- }
- private addItemNodeTitle(index: number, data: any) {
- if (this.itemListTitle.findIndex(n => n.index == index) == -1) {
- let _node = this._initItemNode()
- this.scrollView.content.addChild(_node)
- this.setItemDataTitle(_node, data, index)
- this.itemListTitle.push({ index: index, node: _node })
- _node.zIndex = -999
- this.setPosXTitle(index, _node)
- this.setPosYTitle(index, _node)
- } else {
- this.setItemDataTitle(this.itemListTitle[index].node, data, index)
- this.setPosXTitle(index, this.itemListTitle[index].node)
- this.setPosYTitle(index, this.itemListTitle[index].node)
- }
- }
- /**
- * @param val 立刻滚动到目标标签,目标标签将在顶部|左侧出现
- * @param type 滚到哪里的类型(1.居中 2.顶部|左侧 3.底部|右侧)
- */
- scrollToIndexNow(val: number, type: number = 1, ...args: any[]) {
- if (val >= this.itemDataListMain.length) {
- val = this.itemDataListMain.length - 1
- }
- if (val < 0) { return }
- this.scrollView.stopAutoScroll()
- this.baseIndex = val
- let scrollPosIndex = this.baseIndex
- if (this.itemDataListMain.length - this.baseIndex < this.instantiateCount) {
- this.baseIndex -= this.instantiateCount - (this.itemDataListMain.length - this.baseIndex)
- } else {
- if (type == 1) {
- this.baseIndex -= Math.ceil(this.instantiateCount / 2)
- } else if (type == 3) {
- this.baseIndex -= (this.lieCount - 1) * this.hangCount
- }
- }
- if (this.scrollDir == ScrollDirEnum.GRID) {
- this.baseIndex -= this.baseIndex % this.hangCount
- }
- this.baseIndex = Math.max(this.baseIndex, 0)
- // 从第几个标签开始显示
- switch (this.scrollDir) {
- case ScrollDirEnum.HORIZONTAL:
- let _x = -(this.setPosX(scrollPosIndex) - this.itemDistanceX / 2)
- // 判断是否超过边界
- if (_x < this.scrollMaxOffsetX) {
- scrollPosIndex = this.itemDataListMain.length - this.instantiateCount
- this.scrollView.content.x = this.scrollMaxOffsetX
- } else {
- if (type == 1) {
- this.scrollView.content.x = Math.min(-(this.setPosX(scrollPosIndex) - this.scrollView.node.width / 2), 0)
- if (-(this.setPosX(scrollPosIndex) - this.scrollView.node.width / 2) >= 0) {
- this.baseIndex = 0
- }
- } else if (type == 2) {
- this.scrollView.content.x = -(this.setPosX(scrollPosIndex) - this.itemDistanceX / 2)
- } else {
- scrollPosIndex++
- this.scrollView.content.x = Math.min(-(this.setPosX(scrollPosIndex) - this.scrollView.node.width - this.itemWidth / 2 - this.spacingY), 0)
- if (-(this.setPosX(scrollPosIndex) - this.scrollView.node.width - this.itemWidth / 2 - this.spacingY) >= 0) {
- this.baseIndex = 0
- }
- }
- }
- break
- case ScrollDirEnum.VERTICAL:
- case ScrollDirEnum.GRID:
- let _y = (Math.abs(this.setPosY(scrollPosIndex)) - this.itemDistanceY / 2)
- if (_y > this.scrollMaxOffsetY) {
- scrollPosIndex = this.itemDataListMain.length - this.instantiateCount
- this.scrollView.content.y = this.scrollMaxOffsetY
- } else {
- if (type == 1) {
- this.scrollView.content.y = Math.max(Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height / 2, 0)
- if (Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height / 2 <= 0) {
- this.baseIndex = 0
- }
- } else if (type == 2) {
- this.scrollView.content.y = Math.abs(this.setPosY(scrollPosIndex)) - this.itemDistanceY / 2
- } else {
- scrollPosIndex = this.scrollDir == ScrollDirEnum.VERTICAL ? (scrollPosIndex + 1) : scrollPosIndex + this.hangCount
- this.scrollView.content.y = Math.max(Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height - this.itemHeight / 2 - this.spacingY, 0)
- if (Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height - this.itemHeight / 2 - this.spacingY <= 0) {
- this.baseIndex = 0
- }
- }
- }
- break
- }
- if (args.length > 0) {
- this.extraParams = args
- }
- this.fixItemNodes()
- // 如果content有子项目,则重制目标点位置
- if (this.itemListMain.length > 0) {
- for (let index = 0; index < this.itemListMain.length; index++) {
- // let i = this.baseIndex + index
- let elemnet = this.itemListMain[index]
- elemnet.index = this.baseIndex + index
- this.setPosX(elemnet.index, elemnet.node)
- this.setPosY(elemnet.index, elemnet.node)
- this.setItemData(elemnet.node, this.itemDataListMain[elemnet.index], elemnet.index)
- }
- }
- }
- /**
- * 尝试滚动到滚动视图中心
- * @param index 标签
- * @param type 滚到哪里的类型(1.居中 2.顶部|左侧 3.底部|右侧)
- * @param time
- */
- scrollToIndex(index: number, type: number = 1, time: number = 1, offset: number = 0) {
- if (this.itemDataListMain[index] == null) {
- console.error("不存在此标签")
- return
- }
- switch (this.scrollDir) {
- case ScrollDirEnum.HORIZONTAL:
- if (type == 1) {
- this.scrollView.scrollToOffset(cc.v2(this.setPosX(index) - this.scrollView.node.width / 2 + offset, this.scrollView.getScrollOffset().y), time)
- } else if (type == 2) {
- this.scrollView.scrollToOffset(cc.v2(this.setPosX(index) - this.itemDistanceX / 2 + offset, this.scrollView.getScrollOffset().y), time)
- } else {
- index++
- this.scrollView.scrollToOffset(cc.v2(this.setPosX(index) - this.scrollView.node.width + offset, this.scrollView.getScrollOffset().y - this.itemWidth / 2 - this.spacingX), time)
- }
- break
- case ScrollDirEnum.VERTICAL:
- case ScrollDirEnum.GRID:
- if (type == 1) {
- this.scrollView.scrollToOffset(cc.v2(this.scrollView.getScrollOffset().x, Math.abs(this.setPosY(index)) - this.scrollView.node.height / 2 + offset), time)
- } else if (type == 2) {
- this.scrollView.scrollToOffset(cc.v2(this.scrollView.getScrollOffset().x, Math.abs(this.setPosY(index)) - this.itemDistanceY / 2 + offset), time)
- } else {
- index = this.scrollDir == ScrollDirEnum.VERTICAL ? (index + 1) : index + this.hangCount
- this.scrollView.scrollToOffset(cc.v2(this.scrollView.getScrollOffset().x, Math.abs(this.setPosY(index)) - this.scrollView.node.height - this.itemHeight / 2 - this.spacingY + offset), time)
- }
- break
- }
- // this.scheduleOnce(() => {
- // this.refreshItem(index)
- // }, time)
- }
- getItem(index: number): cc.Node | null {
- for (const key in this.itemListMain) {
- if (this.itemListMain[key].index == index) {
- return this.itemListMain[key].node
- }
- }
- return
- }
- // 使用对象池时,在切换界面时必须使用这个方法,将所有对象放到池中
- // @TODO PoolManager
- clear() {
- if (this.scrollView && this.scrollView.content) {
- this.scrollView.content.removeAllChildren()
- }
- // if (this.useNodePool) {
- // this.itemListMain.forEach(element => {
- // if (this.poolType == PoolEnum.ITEM_BAG) {
- // PoolManager.putItemBag(element.node)
- // } else if (this.poolType == PoolEnum.ITEM_BASE) {
- // PoolManager.putItemBase(element.node)
- // }
- // });
- // } else {
- // if (this.scrollView && this.scrollView.content) {
- // this.scrollView.content.removeAllChildren()
- // }
- // }
- this.itemDataList = []
- this.itemListMain = []
- this.itemListTitle = []
- this.itemDataListMain = []
- this.itemDataListTitle = []
- this.indexFrom = {}
- }
- }
|