ScrollFinal.ts 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. /**
  2. * @Author huangxin
  3. * @ctime 2020-06-10
  4. * @Version
  5. * ScrollFinal1.0 2020-06-10 beta升级,使用挂载预制体
  6. * ScrollFinal1.1 2020-08-11 新增对象池类大法
  7. * ScrollFinal1.2 2020-08-17 1.新增延迟刷新item,防止在某一帧生成过量item导致卡顿的问题
  8. * 2.初始的控件属性设置(init方法)改为由initScrollView触发(原本的onLoad触发在适配模式下会不准确)
  9. * ScrollFinal1.3 2021-05-26 现在的滚动节点不需要手动添加scrollView组件了
  10. * ScrollFinal1.4 2022-04-14 现在可以在初始化或refresh时,立刻滚动到某个位置了
  11. * ScrollFinal1.5 2023-06-22 新增 adapterItem , 可在index首次出现时设定它的高度(后续把动态修改补上)
  12. * @Tips
  13. * 复用滚动轴 用来减少drawcall
  14. * 与cc.ScrollView组件一同挂载在一个节点上
  15. * item挂载的脚本必须添加setData方法,用来传递数据
  16. * item锚点应该在中心
  17. * 目前Grid类型只支持从左上到右下模式(垂直滚动),其他奇葩模式自己搞定
  18. * 滚动轴的锚点必须放在滚动列表的起始位置(比如背包grid模式在左上角,成就列表在左上角)
  19. *
  20. * @adapterItem item如有不定高度时,编辑器中设定的item需要为最小高度,确保instance的个数最大值是正确的
  21. * 支持不规则高度item 谨用,有缺陷:
  22. * 1.同一个item再次设置不同高度时会出现问题(若要修改,需要做一个链表来关联前后item)
  23. * 2.仅支持垂直和水平模式,背包模式不支持(水平模式未测试)
  24. */
  25. import { gameMethod } from "../common/gameMethod";
  26. import { CC_NODE_EVENT } from "../data/const/TypeConst";
  27. import ScrollInner from "./ScrollInner";
  28. import ScrollOut from "./ScrollOut";
  29. // 对象池类型
  30. let PoolEnum = cc.Enum({
  31. /**通用道具 */
  32. ITEM_BASE: 0,
  33. /**背包道具 */
  34. ITEM_BAG: 1,
  35. // /**合成道具 */
  36. // ITEM_COMPOSE: 2,
  37. })
  38. // 滚动类型
  39. let ScrollDirEnum = cc.Enum({
  40. /**垂直*/
  41. VERTICAL: 0,
  42. /**水平*/
  43. HORIZONTAL: 1,
  44. /**背包*/
  45. GRID: 2
  46. })
  47. // 滚动类型
  48. let ScrollOutInner = cc.Enum({
  49. /**外层scroll*/
  50. DEFAULT: 0,
  51. /**外层scroll*/
  52. OUT: 1,
  53. /**内层scroll*/
  54. INNER: 2,
  55. })
  56. interface AdapterList {
  57. x: number,
  58. y: number,
  59. width: number,
  60. height: number
  61. }
  62. type ItemList = { index: number, node: cc.Node }
  63. const { ccclass, property, menu } = cc._decorator;
  64. @ccclass
  65. @menu('Scroll/ScrollFinal')
  66. export default class ScrollFinal extends cc.Component {
  67. // @property(cc.ScrollView)
  68. // scroll: cc.ScrollView | null = null
  69. @property({
  70. tooltip: "使用对象池"
  71. })
  72. useNodePool: boolean = false
  73. @property({
  74. type: PoolEnum,
  75. visible: function () { return this.useNodePool },
  76. tooltip: "对象池类型"
  77. })
  78. private poolType = PoolEnum.ITEM_BASE
  79. @property({
  80. type: ScrollDirEnum,
  81. visible: () => {
  82. return true
  83. },
  84. tooltip: "滚动类型"
  85. })
  86. private scrollDir = ScrollDirEnum.VERTICAL // 滚动类型
  87. @property({
  88. type: ScrollOutInner,
  89. tooltip: "0=>常规scroll\n1=>外层scroll\n2=>内层scroll"
  90. })
  91. private outInner = ScrollOutInner.DEFAULT
  92. @property({ tooltip: "与滚动层的边界-左" })
  93. padingX: number = 10
  94. @property({ tooltip: "与滚动层的边界-上" })
  95. padingY: number = 10
  96. @property({ tooltip: "与滚动层的边界-下" })
  97. padingY2: number = 0
  98. @property({
  99. visible: function () { return this.scrollDir != ScrollDirEnum.VERTICAL }, tooltip: "item行间距"
  100. })
  101. spacingX: number = 20
  102. @property({
  103. visible: function () { return this.scrollDir != ScrollDirEnum.HORIZONTAL }, tooltip: "item列间距"
  104. })
  105. spacingY: number = 20
  106. @property(cc.Prefab)
  107. itemPrefab: cc.Prefab = null // item资源加载地址
  108. @property
  109. itemScript: string = "" // item挂在的脚本名
  110. @property
  111. itemScale: number = 1 // item缩放比例
  112. @property({
  113. tooltip: "是否开启滚动惯性"
  114. })
  115. inertia: boolean = true
  116. @property({
  117. tooltip: "开启惯性后,在用户停止触摸后滚动多块停止,0表示永不停止,1表示立即停止",
  118. visible: function () { return this.inertia == true },
  119. })
  120. brake: number = 0.75
  121. @property({
  122. tooltip: "是否允许滚动内容超过边界,并在停止触摸后回弹"
  123. })
  124. elastic: boolean = true
  125. @property({
  126. tooltip: "滚动行为是否会取消子节点上注册的触摸事件",
  127. })
  128. cancelInnerEvents: boolean = true
  129. @property({
  130. tooltip: "不支持背包,默认不激活", // 激活后,无法在初始化时直接滚动到未展示过的index标签位置,即 scrollToIndexNow会受到限制
  131. displayName: "不固定item尺寸"
  132. })
  133. adapterItem: boolean = false
  134. adapterList: { [idx: string]: AdapterList } = {} // 记录适配的坐标列表
  135. adapterContentLength: number = 0 // 激活适配后的content高度
  136. // @property({
  137. // tooltip: "启动widget模式(注意激活后不可与原生cc.widget同时使用)"
  138. // })
  139. // useWidget: boolean = false
  140. // @property({ tooltip: "适配顶部", visible: function () { return this.useWidget == true } })
  141. // useAlignTop: boolean = false
  142. // @property({ tooltip: "距离父节点顶部", visible: function () { return this.useAlignTop == true } })
  143. // widgetTop: number = 0
  144. // @property({ tooltip: "适配底部", visible: function () { return this.useWidget == true } })
  145. // useAlignBottom: boolean = false
  146. // @property({ tooltip: "距离父节点底部", visible: function () { return this.useAlignBottom == true } })
  147. // widgetBottom: number = 0
  148. // @property({ tooltip: "适配左侧", visible: function () { return this.useWidget == true } })
  149. // useAlignLeft: boolean = false
  150. // @property({ tooltip: "距离父节点左侧", visible: function () { return this.useAlignLeft == true } })
  151. // widgetLeft: number = 0
  152. // @property({ tooltip: "适配右侧", visible: function () { return this.useWidget == true } })
  153. // useAlignRight: boolean = false
  154. // @property({ tooltip: "距离父节点右侧", visible: function () { return this.useAlignRight == true } })
  155. // widgetRight: number = 0
  156. @property({
  157. tooltip: "展示生产动画\n0->不展示\n1->缩放动画\n2->x方向压扁拉伸\n3->y方向压扁拉伸\n4->慢缩放"
  158. })
  159. showAnim: number = 0
  160. @property({
  161. tooltip: "单个动画播放速度\n最佳播放速度参考:\n缩放动画->0.1\nx方向压扁拉伸->0.1\ny方向压扁拉伸->0.25\n慢缩放->0.3"
  162. })
  163. animSpeed: number = 0.15
  164. @property({
  165. tooltip: "创建item的延迟时间,设为0则为每帧生成。\n注意:在所有item刷新完之前,scroll组件的滚动功能将被关闭"
  166. })
  167. ctime: number = 0
  168. @property({
  169. tooltip: "每帧生成item的个数"
  170. })
  171. cnumber: number = 1
  172. scrollView: cc.ScrollView | ScrollOut | ScrollInner
  173. mask: cc.Node
  174. content: cc.Node
  175. isScrollUp: boolean = false // 当前往哪个方向滚动 左和上是true
  176. private _itemDataList: any[] = [] // 当前显示阵营的所有数据
  177. private extraParams: any[] = [] // 额外数据
  178. private itemList: ItemList[] = [] // 实例化的item列表
  179. private instantiateCount: number = 0 // item实例化数量
  180. private hangCount: number = 0 // 行个数
  181. private lieCount: number = 0 // 列个数
  182. private itemDistanceX: number = 0 // item中心点之间的距离
  183. private itemDistanceY: number = 0 // item中心点之间的距离
  184. private scrollMaxOffsetX: number = 0 // 最大可滚动区域X
  185. private scrollMaxOffsetY: number = 0 // 最大可滚动区域Y
  186. private scrollIndex: number = -1 // scroll参数
  187. private lastScrollPos: number = 0 //上一次的滚动位置
  188. private curScrollPos: number = 0 // 当前滚动位置
  189. private itemWidth: number = 10 // item宽度
  190. private itemHeight: number = 10 // item高度
  191. private tagLang: number = 0
  192. private tagIndex: number = -999 // 999 表示清0状态,此时无【插入标签】,-1表示标签置顶,其他即当前标签的下方(右侧)显示
  193. private canCreateItem: boolean = false // 可以生成item
  194. private createIndex: number = 0 // 生成item的数据标签
  195. private life: number = 0 // 生成item的时间
  196. private baseIndex: number = 0 // 基础标签位置
  197. private hasInit: boolean = false
  198. onLoad() {
  199. // if (this.node.getComponent(cc.ScrollView) != null) {
  200. // console.error("滚动节点无需挂载scrollView组件了")
  201. // return
  202. // }
  203. this.init()
  204. }
  205. resetSize() {
  206. if (this.mask) {
  207. this.mask.setContentSize(this.node.getContentSize())
  208. }
  209. if (this.content) {
  210. // this.content.setContentSize(this.node.getContentSize())
  211. this.setScrollContentSize()
  212. }
  213. this.setInstantCount()
  214. }
  215. private init() {
  216. if (this.hasInit) { return }
  217. /////////////// 构建滚动轴 ///////////////
  218. this.scrollView = this.outInner == ScrollOutInner.DEFAULT ? this.addComponent(cc.ScrollView) :
  219. this.outInner == ScrollOutInner.OUT ? this.addComponent(ScrollOut) : this.addComponent(ScrollInner)
  220. // this.scrollView = this.addComponent(cc.ScrollView)
  221. this.scrollView.horizontal = this.scrollDir == ScrollDirEnum.HORIZONTAL
  222. this.scrollView.vertical = this.scrollDir != ScrollDirEnum.HORIZONTAL
  223. this.scrollView.inertia = this.inertia
  224. this.scrollView.brake = this.brake
  225. this.scrollView.elastic = this.elastic
  226. this.scrollView.cancelInnerEvents = this.cancelInnerEvents
  227. /////////////// 检测是否需要重新适配 ///////////////
  228. if (this.node.getComponent(cc.Widget)) {
  229. this.node.getComponent(cc.Widget).updateAlignment()
  230. }
  231. /////////////// 构建滚动遮罩 ///////////////
  232. this.mask = new cc.Node()
  233. this.mask.parent = this.node
  234. this.mask.name = "scrollMask"
  235. this.mask.setContentSize(this.node.getContentSize())
  236. this.mask.addComponent(cc.Widget)
  237. this.mask.getComponent(cc.Widget).isAlignTop = true
  238. this.mask.getComponent(cc.Widget).isAlignBottom = true
  239. this.mask.getComponent(cc.Widget).top = 0
  240. this.mask.getComponent(cc.Widget).bottom = 0
  241. this.mask.addComponent(cc.Mask)
  242. this.mask.getComponent(cc.Mask).type = cc.Mask.Type.RECT
  243. this.mask.anchorX = this.node.anchorX
  244. this.mask.anchorY = this.node.anchorY
  245. this.mask.x = 0
  246. this.mask.y = 0
  247. // widget不需要加了
  248. // let maskWidget = mask.addComponent(cc.Widget)
  249. // maskWidget.isAlignTop = true
  250. // maskWidget.isAlignBottom = true
  251. // maskWidget.isAlignLeft = true
  252. // maskWidget.isAlignRight = true
  253. // maskWidget.top = 0
  254. // maskWidget.bottom = 0
  255. // maskWidget.left = 0
  256. // maskWidget.right = 0
  257. /////////////// 构建滚动内容器 ///////////////
  258. this.content = new cc.Node()
  259. this.content.parent = this.mask
  260. this.content.name = "scrollContent"
  261. this.scrollView.content = this.content
  262. this.content.setContentSize(this.node.getContentSize())
  263. this.content.anchorX = this.node.anchorX
  264. this.content.anchorY = this.node.anchorY
  265. this.content.x = 0
  266. this.content.y = 0
  267. this.hasInit = true
  268. this.itemWidth = this.itemPrefab.data.getContentSize().width
  269. this.itemHeight = this.itemPrefab.data.getContentSize().height
  270. // SCROLL_BOUNCE_BOTTOM = 'bounce-bottom',//滚动视图滚动到顶部边界并且开始回弹时发出的事件
  271. // SCROLL_BOUNCE_LEFT = 'bounce-left',//滚动视图滚动到底部边界并且开始回弹时发出的事件
  272. // SCROLL_BOUNCE_RIGHT = 'bounce-right',//滚动视图滚动到左边界并且开始回弹时发出的事件
  273. // SCROLL_BOUNCE_TOP = 'bounce-top',//滚动视图滚动到右边界并且开始回弹时发出的事件
  274. this.scrollView.node.on(CC_NODE_EVENT.SCROLLING, this.onScroll, this)
  275. this.scrollView.node.on(CC_NODE_EVENT.SCROLL_BEGAN, this.onScrollBegan, this)
  276. this.scrollView.node.on(CC_NODE_EVENT.SCROLL_BOUNCE_TOP, this.onBounceTop, this)
  277. this.scrollView.node.on(CC_NODE_EVENT.SCROLL_BOUNCE_BOTTOM, () => { }, this)
  278. this.scrollView.node.on(CC_NODE_EVENT.SCROLL_BOUNCE_LEFT, () => { }, this)
  279. this.scrollView.node.on(CC_NODE_EVENT.SCROLL_BOUNCE_RIGHT, () => { }, this)
  280. this.scrollView.node.on(CC_NODE_EVENT.SCROLL_BOUNCE_TOP, () => { }, this)
  281. this.itemDistanceX = this.realItemWidth + this.spacingX
  282. this.itemDistanceY = this.realItemHeight + this.spacingY
  283. this.setInstantCount()
  284. // //看下有没有不应该有的组件
  285. // // Layout
  286. // if (this.scrollView.content.getComponent(cc.Layout)) {
  287. // console.error("scrollFinal 与 layout 冲突,清删除 content 中的 layout 组件")
  288. // }
  289. // // Widget
  290. // if (this.scrollView.node.getComponent(cc.Widget)) {
  291. // if (this.scrollView.node.getComponent(cc.Widget).isAlignTop &&
  292. // this.scrollView.node.getComponent(cc.Widget).isAlignBottom) {
  293. // console.error("不能用widget做长度适配(因为Widget的延迟),只可用作坐标适配")
  294. // }
  295. // }
  296. }
  297. private setInstantCount() {
  298. if (this.scrollView == null) {
  299. return
  300. }
  301. switch (this.scrollDir) {
  302. case ScrollDirEnum.VERTICAL:
  303. this.hangCount = 1
  304. this.lieCount = Math.ceil(this.scrollView.node.height / (this.itemDistanceY)) + 1
  305. this.instantiateCount = this.lieCount
  306. break
  307. case ScrollDirEnum.HORIZONTAL:
  308. this.hangCount = Math.ceil(this.scrollView.node.width / (this.itemDistanceX)) + 1
  309. this.lieCount = 1
  310. this.instantiateCount = this.hangCount
  311. break
  312. case ScrollDirEnum.GRID:
  313. this.hangCount = Math.floor(this.scrollView.node.width / (this.itemDistanceX))
  314. this.lieCount = Math.ceil(this.scrollView.node.height / (this.itemDistanceY)) + 1
  315. this.instantiateCount = this.hangCount * this.lieCount
  316. break
  317. }
  318. }
  319. initScrollView(list: any[] = [], ...args: any[]) {
  320. // this.scheduleOnce(() => {
  321. this.init()
  322. this.clear()
  323. this.clearTag()
  324. this.adapterList = {}
  325. this.itemDataList = list
  326. this.extraParams = args
  327. if (this.outInner == ScrollOutInner.OUT) {
  328. this.extraParams.push(this.scrollView)
  329. }
  330. this.scrollView.stopAutoScroll()
  331. this.showUI()
  332. // }, 0)
  333. }
  334. set itemDataList(list: any[]) {
  335. this._itemDataList = list
  336. if (!this.adapterItem) {
  337. return
  338. }
  339. // 适配模式,需要去掉记录的信息
  340. let newAdapterList: { [idx: string]: AdapterList } = {}
  341. for (let index = 0; index < list.length; index++) {
  342. if (this.adapterList[index]) {
  343. newAdapterList[index] = this.adapterList[index]
  344. }
  345. }
  346. this.adapterList = newAdapterList
  347. switch (this.scrollDir) {
  348. case ScrollDirEnum.VERTICAL:
  349. this.adapterContentLength = 0
  350. for (const idx in this.adapterList) {
  351. this.adapterContentLength = Math.max(Math.abs(this.adapterList[idx].y) + this.adapterList[idx].height / 2)
  352. }
  353. break
  354. case ScrollDirEnum.HORIZONTAL:
  355. this.adapterContentLength = 0
  356. for (const idx in this.adapterList) {
  357. this.adapterContentLength = Math.max(this.adapterList[idx].x + this.adapterList[idx].width / 2)
  358. }
  359. break
  360. }
  361. }
  362. get itemDataList(): any[] {
  363. return this._itemDataList
  364. }
  365. private getPositionInView(item: cc.Node): { x: number, y: number } {
  366. if (this.scrollView == null) { return cc.v2(0, 0) }
  367. let worldPos = item.parent.convertToWorldSpaceAR(item.position);
  368. let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
  369. return viewPos;
  370. }
  371. private onBounceTop() {
  372. switch (this.scrollDir) {
  373. case ScrollDirEnum.HORIZONTAL:
  374. break
  375. case ScrollDirEnum.VERTICAL:
  376. case ScrollDirEnum.GRID:
  377. this.checkScrollState()
  378. if (this.adapterItem) {
  379. // @TODO
  380. }
  381. break
  382. }
  383. this.scrollIndex = -1
  384. }
  385. private onScroll() {
  386. this.curScrollPos = 0
  387. if (this.scrollDir == ScrollDirEnum.HORIZONTAL) {
  388. this.curScrollPos = this.scrollView.getScrollOffset().x
  389. this.isScrollUp = this.curScrollPos < this.lastScrollPos
  390. } else {
  391. this.curScrollPos = this.scrollView.getScrollOffset().y
  392. this.isScrollUp = this.curScrollPos > this.lastScrollPos
  393. }
  394. this.lastScrollPos = this.curScrollPos
  395. if (this.scrollView == null) { return }
  396. this.itemList.forEach(ele => {
  397. this.tryResetItem(ele)
  398. })
  399. }
  400. private tryResetItem(ele: ItemList) {
  401. switch (this.scrollDir) {
  402. case ScrollDirEnum.HORIZONTAL:
  403. if (this.curScrollPos >= 0 || this.curScrollPos <= this.scrollMaxOffsetX) {
  404. return
  405. }
  406. break
  407. case ScrollDirEnum.VERTICAL:
  408. case ScrollDirEnum.GRID:
  409. if (this.curScrollPos <= -this.realItemHeight / 2 || this.curScrollPos >= this.scrollMaxOffsetY + this.realItemHeight / 2) {
  410. return
  411. }
  412. }
  413. let scrollWidth = this.scrollView.node.width
  414. let scrollHeight = this.scrollView.node.height
  415. let element = ele.node
  416. switch (this.scrollDir) {
  417. case ScrollDirEnum.HORIZONTAL:
  418. if (this.isScrollUp && this.getPositionInView(element).x < -(scrollWidth * this.node.anchorX + ele.node.width / 2)) {
  419. // 超出左边界显示区域
  420. let idx = ele.index + this.instantiateCount
  421. if (idx < this.itemDataList.length) {
  422. this.setItemData(element, this.itemDataList[idx], idx)
  423. // element.x = element.x + this.hangCount * ele.node.width
  424. ele.index = idx
  425. this.setPosX(idx, element)
  426. }
  427. } else if (!this.isScrollUp && this.getPositionInView(element).x > scrollWidth * (1 - this.node.anchorX) + ele.node.width / 2) {
  428. // 超出右边界显示区域
  429. let idx = ele.index - this.instantiateCount
  430. if (idx >= 0) {
  431. this.setItemData(element, this.itemDataList[idx], idx)
  432. // element.x = element.x - this.hangCount * ele.node.width
  433. ele.index = idx
  434. this.setPosX(idx, element)
  435. }
  436. }
  437. break
  438. case ScrollDirEnum.VERTICAL:
  439. case ScrollDirEnum.GRID:
  440. if (this.isScrollUp && this.getPositionInView(element).y > scrollHeight * (1 - this.node.anchorY) + ele.node.height / 2) {
  441. // 超出上边界显示区域
  442. let idx = ele.index + this.instantiateCount
  443. if (idx < this.itemDataList.length && this.isScrollUp) {
  444. this.setItemData(element, this.itemDataList[idx], idx)
  445. ele.index = idx
  446. this.setPosY(idx, element)
  447. }
  448. } else if (!this.isScrollUp && this.curScrollPos > -ele.node.height / 2 && this.getPositionInView(element).y < -(scrollHeight * this.node.anchorY + ele.node.height / 2)) {
  449. // 超出下边界显示区域
  450. let idx = ele.index - this.instantiateCount
  451. if (idx >= 0 && this.isScrollUp == false) {
  452. this.setItemData(element, this.itemDataList[idx], idx)
  453. ele.index = idx
  454. this.setPosY(idx, element)
  455. }
  456. }
  457. break
  458. }
  459. }
  460. private setPosX(index: number, node?: cc.Node): number {
  461. let x = 0
  462. if (this.adapterItem) {
  463. if (this.adapterList[index] && this.adapterList[index].x != null) {
  464. if (node) {
  465. node.x = this.adapterList[index].x
  466. }
  467. return this.adapterList[index].x
  468. } else {
  469. let lastX = index == 0 ? 0 : this.adapterList[index - 1].x
  470. let lastWidth = index == 0 ? 0 : this.adapterList[index - 1].width
  471. // 上一个坐标 + 当前node偏移量
  472. switch (this.scrollDir) {
  473. case ScrollDirEnum.VERTICAL:
  474. x = this.scrollView.content.width / 2 + this.padingX//this.realItemWidth / 2 + this.padingX
  475. break
  476. case ScrollDirEnum.HORIZONTAL:
  477. x += node.width / 2 + this.padingX + lastX + lastWidth / 2 + this.spacingX
  478. this.adapterContentLength = Math.max(x + node.width / 2)
  479. this.setScrollContentSize()
  480. break
  481. case ScrollDirEnum.GRID:
  482. x = index % this.hangCount * this.itemDistanceX + this.realItemWidth / 2 + this.padingX
  483. break
  484. }
  485. if (this.adapterList[index] == null) { this.adapterList[index] = { x: null, y: null, width: null, height: null } }
  486. this.adapterList[index].x = x
  487. this.adapterList[index].width = node.width
  488. if (node) { node.x = x }
  489. return x
  490. }
  491. } else {
  492. switch (this.scrollDir) {
  493. case ScrollDirEnum.VERTICAL:
  494. x = this.scrollView.content.width / 2 + this.padingX//this.realItemWidth / 2 + this.padingX
  495. break
  496. case ScrollDirEnum.HORIZONTAL:
  497. x = index * this.itemDistanceX + this.realItemWidth / 2 + this.padingX
  498. if (this.tagIndex >= -1 && index > this.tagIndex) {
  499. x += this.tagLang
  500. }
  501. break
  502. case ScrollDirEnum.GRID:
  503. x = index % this.hangCount * this.itemDistanceX + this.realItemWidth / 2 + this.padingX
  504. break
  505. }
  506. if (node) {
  507. node.x = x
  508. }
  509. return x
  510. }
  511. }
  512. private setPosY(index: number, node?: cc.Node): number {
  513. let y = 0
  514. if (this.adapterItem) {
  515. if (this.adapterList[index] && this.adapterList[index].y != null) {
  516. if (node) {
  517. node.y = this.adapterList[index].y
  518. }
  519. return this.adapterList[index].y
  520. } else {
  521. let lastY = index == 0 ? 0 : this.adapterList[index - 1].y
  522. let lastHeight = index == 0 ? 0 : this.adapterList[index - 1].height
  523. switch (this.scrollDir) {
  524. case ScrollDirEnum.VERTICAL:
  525. y = lastY - this.padingY - node.height / 2 - lastHeight / 2 - this.spacingY
  526. this.adapterContentLength = Math.max(Math.abs(y) + node.height / 2)
  527. this.setScrollContentSize()
  528. // y = -index * this.itemDistanceY - this.realItemHeight / 2 - this.padingY
  529. // if (this.tagIndex >= -1 && index > this.tagIndex) {
  530. // y -= this.tagLang
  531. // }
  532. break
  533. case ScrollDirEnum.HORIZONTAL:
  534. y = -this.scrollView.content.height / 2 + this.padingY//-this.realItemHeight / 2 - this.padingY
  535. break
  536. case ScrollDirEnum.GRID:
  537. y = -Math.floor((index) / this.hangCount) * this.itemDistanceY - this.realItemHeight / 2 - this.padingY
  538. if (this.tagIndex >= -1 && (Math.floor(index / this.hangCount)) > this.tagIndex) {
  539. y -= this.tagLang
  540. }
  541. break
  542. }
  543. if (this.adapterList[index] == null) { this.adapterList[index] = { x: null, y: null, width: null, height: null } }
  544. this.adapterList[index].y = y
  545. this.adapterList[index].height = node.height
  546. if (node) { node.y = y }
  547. return y
  548. }
  549. } else {
  550. switch (this.scrollDir) {
  551. case ScrollDirEnum.VERTICAL:
  552. y = -index * this.itemDistanceY - this.realItemHeight / 2 - this.padingY
  553. if (this.tagIndex >= -1 && index > this.tagIndex) {
  554. y -= this.tagLang
  555. }
  556. break
  557. case ScrollDirEnum.HORIZONTAL:
  558. y = -this.scrollView.content.height / 2 + this.padingY//-this.realItemHeight / 2 - this.padingY
  559. break
  560. case ScrollDirEnum.GRID:
  561. y = -Math.floor((index) / this.hangCount) * this.itemDistanceY - this.realItemHeight / 2 - this.padingY
  562. if (this.tagIndex >= -1 && (Math.floor(index / this.hangCount)) > this.tagIndex) {
  563. y -= this.tagLang
  564. }
  565. break
  566. }
  567. if (node) {
  568. node.y = y
  569. }
  570. return y
  571. }
  572. }
  573. private setItemData(itemNode: cc.Node, data: any, index: number) {
  574. try {
  575. itemNode.getComponent(this.itemScript).setData(data, index, this.extraParams)
  576. } catch (error) {
  577. console.error("脚本中缺少setData方法,或者方法报错", error)
  578. }
  579. }
  580. // refreshItems调用,在刷新时可能需要重置item的index标签
  581. private resetIndex(index: number): number {
  582. if (this.itemDataList[index] != null) {
  583. return index
  584. }
  585. return this.resetIndex(index - this.instantiateCount)
  586. }
  587. // 刷新单独的item
  588. refreshItem(index: number, data) {
  589. if (this.itemDataList[index] == null) {
  590. return
  591. }
  592. this.itemDataList[index] = data
  593. if (this.getItem(index) == null) {
  594. return
  595. }
  596. this.setItemData(this.getItem(index), this.itemDataList[index], index)
  597. }
  598. // refreshItem(index: number) {
  599. // this.setItemData(this.getItem(index), this.itemDataList[index], index)
  600. // }
  601. refreshItems(itemDataList: any[], ...args: any[]) {
  602. this.itemDataList = itemDataList
  603. if (args.length > 0) {
  604. this.extraParams = args
  605. }
  606. if (this.outInner == ScrollOutInner.OUT) {
  607. this.extraParams.push(this.scrollView)
  608. }
  609. this.fixItemNodes()
  610. // 最终构造完整的 itemList 列表,刷新数据
  611. this.itemList.forEach(element => {
  612. try {
  613. let newIndex = this.resetIndex(element.index)
  614. element.index = newIndex
  615. this.setItemData(element.node, this.itemDataList[element.index], element.index)
  616. this.setPosX(element.index, element.node)
  617. this.setPosY(element.index, element.node)
  618. // element.node.getComponent(this.itemScript).setData(this.itemDataList[element.index], element.index, this.extraParams)
  619. } catch (error) {
  620. console.warn("脚本中缺少refreshItem方法,或者方法报错", error)
  621. }
  622. })
  623. }
  624. // 数据新增或减少时,增加或减少item
  625. fixItemNodes() {
  626. // 判断是否需要删除 itemList 里的数据
  627. if (this.itemDataList.length < this.instantiateCount && this.itemList.length > this.itemDataList.length) {
  628. let needDeleteCount = this.itemList.length - this.itemDataList.length
  629. for (let index = this.itemDataList.length; index < this.itemDataList.length + needDeleteCount; index++) {
  630. this.itemList[index].node.destroy()
  631. }
  632. this.itemList.splice(this.itemDataList.length, needDeleteCount)
  633. // 判断是否需要增加 itemList 里的数据
  634. } else if (this.itemList.length < this.instantiateCount && this.itemList.length < this.itemDataList.length) {
  635. let addCount = Math.min(this.instantiateCount - this.itemList.length, this.itemDataList.length - this.itemList.length)
  636. let startIndex = 0
  637. this.itemList.forEach(element => {
  638. startIndex = Math.max(element.index + 1, startIndex)
  639. });
  640. for (let addIndex = startIndex; addIndex < (startIndex + addCount); addIndex++) {
  641. this.addItemNode(addIndex, this.itemDataList[addIndex], true)
  642. }
  643. }
  644. if (this.content) {
  645. this.setScrollContentSize()
  646. }
  647. }
  648. private get realItemWidth(): number {
  649. return this.itemWidth * this.itemScale
  650. }
  651. private get realItemHeight(): number {
  652. return this.itemHeight * this.itemScale
  653. }
  654. private initItemNode(): cc.Node {
  655. if (this.useNodePool) {
  656. // @TODO PoolManager
  657. return cc.instantiate(this.itemPrefab)
  658. // if (this.poolType == PoolEnum.ITEM_BAG) {
  659. // return PoolManager.getItemBag(this.itemPrefab)
  660. // } else if (this.poolType == PoolEnum.ITEM_BASE) {
  661. // return PoolManager.getItemBase(this.itemPrefab)
  662. // }
  663. } else {
  664. return cc.instantiate(this.itemPrefab)
  665. }
  666. }
  667. // back
  668. private onScrollBegan() {
  669. this.scrollIndex = 0
  670. }
  671. private showUI() {
  672. if (this.itemPrefab == null) {
  673. console.error("item预制体加载失败")
  674. return
  675. }
  676. if (this.scrollView == null) {
  677. console.error("没有绑定scroll")
  678. return
  679. }
  680. this.setCreateItems(true)
  681. this.scrollIndex = -1
  682. this.scrollView.content.setAnchorPoint(0, 1)
  683. this.scrollView.content.setPosition(-this.scrollView.node.width / 2, this.scrollView.node.height / 2)
  684. this.setScrollContentSize()
  685. switch (this.scrollDir) {
  686. case ScrollDirEnum.VERTICAL:
  687. this.scrollView.scrollToTop()
  688. break
  689. case ScrollDirEnum.HORIZONTAL:
  690. this.scrollView.scrollToLeft()
  691. break
  692. case ScrollDirEnum.GRID:
  693. this.scrollView.scrollToTop()
  694. break
  695. }
  696. }
  697. private setCreateItems(bool: boolean) {
  698. if (bool) {
  699. this.scrollView.enabled = false
  700. this.canCreateItem = true
  701. this.createIndex = 0
  702. this.baseIndex = 0
  703. this.life = 0
  704. } else {
  705. this.scrollView.enabled = true
  706. this.canCreateItem = false
  707. this.createIndex = 0
  708. this.baseIndex = 0
  709. this.life = 0
  710. }
  711. }
  712. update(dt: number) {
  713. if (this.scrollIndex >= 0) { this.scrollIndex += dt }
  714. if (!this.canCreateItem) {
  715. return
  716. }
  717. for (let index = 0; index < this.cnumber; index++) {
  718. this.updateForCreateItem(dt)
  719. }
  720. }
  721. private updateForCreateItem(dt: number) {
  722. if (!this.canCreateItem) {
  723. return
  724. }
  725. if (this.life == 0) {
  726. // 生
  727. let _itemData = this.itemDataList[this.baseIndex]
  728. if (this.createIndex >= this.instantiateCount || _itemData == null) {
  729. this.setCreateItems(false)
  730. return
  731. }
  732. this.addItemNode(this.baseIndex, _itemData)
  733. this.createIndex += 1
  734. this.baseIndex += 1
  735. }
  736. this.life += dt
  737. if (this.life >= this.ctime) {
  738. this.life = 0
  739. }
  740. }
  741. setScrollContentSize() {
  742. if (this.adapterItem) {
  743. switch (this.scrollDir) {
  744. case ScrollDirEnum.VERTICAL:
  745. this.scrollView.content.setContentSize(this.scrollView.node.width, this.adapterContentLength)
  746. break
  747. case ScrollDirEnum.HORIZONTAL:
  748. this.scrollView.content.setContentSize(this.adapterContentLength, this.scrollView.node.height)
  749. break
  750. case ScrollDirEnum.GRID:
  751. this.scrollView.content.setContentSize(this.scrollView.node.width, this.itemDistanceY * Math.ceil(this.itemDataList.length / this.hangCount) + this.padingY + this.tagLang + this.padingY2)
  752. break
  753. }
  754. } else {
  755. switch (this.scrollDir) {
  756. case ScrollDirEnum.VERTICAL:
  757. this.scrollView.content.setContentSize(this.scrollView.node.width, this.itemDistanceY * this.itemDataList.length + this.padingY + this.tagLang + this.padingY2 - this.spacingY)
  758. break
  759. case ScrollDirEnum.HORIZONTAL:
  760. this.scrollView.content.setContentSize(this.itemDistanceX * this.itemDataList.length + this.padingX + this.tagLang - this.spacingX, this.scrollView.node.height)
  761. break
  762. case ScrollDirEnum.GRID:
  763. this.scrollView.content.setContentSize(this.scrollView.node.width, this.itemDistanceY * Math.ceil(this.itemDataList.length / this.hangCount) + this.padingY + this.tagLang + this.padingY2 - this.spacingY)
  764. break
  765. }
  766. }
  767. this.scrollMaxOffsetX = -this.scrollView.getMaxScrollOffset().x
  768. this.scrollMaxOffsetY = this.scrollView.getMaxScrollOffset().y
  769. // console.log("---重新设置了滚动区域", this.scrollView.content.height)
  770. }
  771. /**
  772. * 弹出详情标签,将会重设后续item的坐标。
  773. * @param index item标签
  774. * @param lang 坐标偏移量
  775. */
  776. setTag(index: number, lang: number) {
  777. this.tagLang = lang
  778. switch (this.scrollDir) {
  779. case ScrollDirEnum.VERTICAL:
  780. case ScrollDirEnum.HORIZONTAL:
  781. this.tagIndex = index
  782. break
  783. case ScrollDirEnum.GRID:
  784. this.tagIndex = Math.floor(index / this.hangCount)
  785. break
  786. }
  787. this.setScrollContentSize()
  788. this.itemList.forEach((element) => {
  789. this.setPosX(element.index, element.node)
  790. this.setPosY(element.index, element.node)
  791. });
  792. }
  793. /**清除详情标签,恢复item默认坐标 */
  794. clearTag() {
  795. this.setTag(-999, 0)
  796. // 修正index和坐标
  797. let scrollHeight = this.scrollView.node.height
  798. this.itemList.forEach(ele => {
  799. // 判断是否超出边界
  800. if (this.getPositionInView(ele.node).y > scrollHeight * (1 - this.node.anchorY) + this.itemDistanceY / 2) {
  801. if (ele.index + this.instantiateCount < this.itemDataList.length) {
  802. ele.index += this.instantiateCount
  803. }
  804. this.setPosY(ele.index, ele.node)
  805. this.setItemData(ele.node, this.itemDataList[ele.index], ele.index)
  806. } else if (this.getPositionInView(ele.node).y < -(scrollHeight * this.node.anchorY + this.itemDistanceY / 2)) {
  807. if (ele.index - this.instantiateCount >= 0) {
  808. ele.index -= this.instantiateCount
  809. }
  810. this.setPosY(ele.index, ele.node)
  811. this.setItemData(ele.node, this.itemDataList[ele.index], ele.index)
  812. }
  813. })
  814. this.setScrollContentSize()
  815. }
  816. /**删除某个元素 */
  817. del(index: number) {
  818. if (this.itemDataList.length < index) { return }
  819. this.itemDataList.splice(index, 1)
  820. // // 判断下是否需要删除节点
  821. if (this.itemList.length > this.itemDataList.length) {
  822. this.itemList.pop().node.destroy()
  823. }
  824. this.setScrollContentSize()
  825. for (let index = 0; index < this.itemList.length; index++) {
  826. let element = this.itemList[index]
  827. if (this.itemDataList[element.index] == null) {
  828. element.index -= this.instantiateCount
  829. break
  830. }
  831. }
  832. this.itemList.forEach((element) => {
  833. this.setPosX(element.index, element.node)
  834. this.setPosY(element.index, element.node)
  835. this.setItemData(element.node, this.itemDataList[element.index], element.index)
  836. });
  837. }
  838. /**在末尾添加一个元素 */
  839. add(data: any) {
  840. this.itemDataList.push(data)
  841. // 判断是否需要增加item
  842. if (this.itemList.length >= this.instantiateCount) {
  843. // 不需要
  844. // 判断下是否需要把最前面的放到最后面:1
  845. let needUpdate = false
  846. for (let index = 0; index < this.itemList.length; index++) {
  847. if (this.itemList[index].index == this.itemDataList.length - 2) {
  848. needUpdate = true
  849. break
  850. }
  851. }
  852. if (needUpdate) {
  853. let minIndex = 1000 //这个是最小值
  854. let minUpdateIndex = 0// 这个是最小值的标签
  855. for (let i = 0; i < this.itemList.length; i++) {
  856. if (this.itemList[i].index < minIndex) {
  857. minIndex = this.itemList[i].index
  858. minUpdateIndex = i
  859. }
  860. }
  861. // 判断下是否需要把最前面的放到最后面:2
  862. let _nodePos = 0
  863. let _offset = 0
  864. let needUpdate2 = false
  865. switch (this.scrollDir) {
  866. case ScrollDirEnum.HORIZONTAL:
  867. _nodePos = this.itemList[minUpdateIndex].node.x
  868. _offset = this.scrollView.getScrollOffset().x
  869. needUpdate2 = _nodePos + _offset > this.itemWidth / 2
  870. break
  871. case ScrollDirEnum.VERTICAL:
  872. case ScrollDirEnum.GRID:
  873. _nodePos = this.itemList[minUpdateIndex].node.y
  874. _offset = this.scrollView.getScrollOffset().y
  875. needUpdate2 = _nodePos + _offset > this.itemHeight / 2
  876. break
  877. }
  878. if (needUpdate2) {
  879. this.itemList[minUpdateIndex].index += this.instantiateCount
  880. this.setItemData(this.itemList[minUpdateIndex].node, data, this.itemList[minUpdateIndex].index)
  881. this.setPosX(this.itemList[minUpdateIndex].index, this.itemList[minUpdateIndex].node)
  882. this.setPosY(this.itemList[minUpdateIndex].index, this.itemList[minUpdateIndex].node)
  883. }
  884. }
  885. } else {
  886. // 需要
  887. let index = this.itemDataList.length - 1
  888. this.addItemNode(index, data)
  889. }
  890. this.setScrollContentSize()
  891. }
  892. addItemNode(index: number, data: any, isRefresh: boolean = false) {
  893. let _node = this.initItemNode()
  894. // add放在前面.先激活onLoad方法,再走setData
  895. this.scrollView.content.addChild(_node)
  896. this.setItemData(_node, data, index)
  897. this.itemList.push({ index: index, node: _node })
  898. this.setPosX(index, _node)
  899. this.setPosY(index, _node)
  900. if (isRefresh || this.showAnim == 0) {
  901. _node.scale = this.itemScale
  902. } else if (this.showAnim == 1) {
  903. _node.scale = 0
  904. cc.tween(_node).
  905. to(this.animSpeed, { scale: this.itemScale + 0.1 }).
  906. to(this.animSpeed, { scale: this.itemScale }).
  907. start()
  908. } else if (this.showAnim == 2) {
  909. _node.scale = this.itemScale
  910. let delayTime = index % this.hangCount / 20 + 0.1
  911. _node.x -= _node.width / 2
  912. _node.scaleX = 0
  913. cc.tween(_node).delay(delayTime).to(this.animSpeed, { x: _node.x + _node.width / 2, scaleX: this.itemScale }).start()
  914. } else if (this.showAnim == 3) {
  915. //@TODO
  916. _node.scale = this.itemScale
  917. let delayTime = index % this.hangCount / 20 + 0.1
  918. _node.y += _node.height / 2
  919. _node.scaleY = 0
  920. cc.tween(_node).delay(delayTime).to(this.animSpeed, { y: _node.y - _node.height / 2, scaleY: this.itemScale }).start()
  921. } else if (this.showAnim == 4) {
  922. _node.scale = 0
  923. cc.tween(_node).
  924. to(this.animSpeed, { scale: this.itemScale }).
  925. start()
  926. } else {
  927. _node.scale = this.itemScale
  928. }
  929. }
  930. /**
  931. * @param val 立刻滚动到目标标签,目标标签将在顶部|左侧出现
  932. * @param type 滚到哪里的类型(1.居中 2.顶部|左侧 3.底部|右侧)
  933. */
  934. scrollToIndexNow(val: number, type: number = 1, ...args: any[]) {
  935. if (this.adapterItem && this.adapterList[val] == null) {
  936. console.warn("未展示过,无法移动")
  937. return
  938. }
  939. if (val >= this.itemDataList.length) {
  940. val = this.itemDataList.length - 1
  941. }
  942. if (val < 0) {
  943. this.refreshItems(this.itemDataList)
  944. return
  945. }
  946. this.scrollView.stopAutoScroll()
  947. this.baseIndex = val
  948. let scrollPosIndex = this.baseIndex
  949. if (this.itemDataList.length - this.baseIndex < this.instantiateCount) {
  950. this.baseIndex -= this.instantiateCount - (this.itemDataList.length - this.baseIndex)
  951. } else {
  952. if (type == 1) {
  953. this.baseIndex -= Math.ceil(this.instantiateCount / 2)
  954. } else if (type == 3) {
  955. this.baseIndex -= (this.lieCount - 1) * this.hangCount
  956. }
  957. }
  958. if (this.scrollDir == ScrollDirEnum.GRID) {
  959. this.baseIndex -= this.baseIndex % this.hangCount
  960. }
  961. this.baseIndex = Math.max(this.baseIndex, 0)
  962. let _itemWidth = this.adapterItem ? this.adapterList[val].width : this.itemWidth
  963. let _itemHeight = this.adapterItem ? this.adapterList[val].height : this.itemHeight
  964. // 从第几个标签开始显示
  965. switch (this.scrollDir) {
  966. case ScrollDirEnum.HORIZONTAL:
  967. let _x = -(this.setPosX(scrollPosIndex) - (_itemWidth * this.itemScale + this.spacingX) / 2)
  968. // 判断是否超过边界
  969. if (_x < this.scrollMaxOffsetX) {
  970. scrollPosIndex = this.itemDataList.length - this.instantiateCount
  971. this.scrollView.content.x = this.scrollMaxOffsetX
  972. } else {
  973. if (type == 1) {
  974. this.scrollView.content.x = Math.min(-(this.setPosX(scrollPosIndex) - this.scrollView.node.width / 2), 0)
  975. if (-(this.setPosX(scrollPosIndex) - this.scrollView.node.width / 2) >= 0) {
  976. this.baseIndex = 0
  977. }
  978. } else if (type == 2) {
  979. this.scrollView.content.x = -(this.setPosX(scrollPosIndex) - (_itemWidth * this.itemScale + this.spacingX) / 2)
  980. } else {
  981. scrollPosIndex++
  982. this.scrollView.content.x = Math.min(-(this.setPosX(scrollPosIndex) - this.scrollView.node.width - _itemWidth / 2 - this.spacingY), 0)
  983. if (-(this.setPosX(scrollPosIndex) - this.scrollView.node.width - _itemWidth / 2 - this.spacingY) >= 0) {
  984. this.baseIndex = 0
  985. }
  986. }
  987. }
  988. break
  989. case ScrollDirEnum.VERTICAL:
  990. case ScrollDirEnum.GRID:
  991. let _y = (Math.abs(this.setPosY(scrollPosIndex)) - (_itemHeight * this.itemScale + this.spacingY) / 2)
  992. if (_y > this.scrollMaxOffsetY) {
  993. scrollPosIndex = this.itemDataList.length - this.instantiateCount
  994. this.scrollView.content.y = this.scrollMaxOffsetY
  995. } else {
  996. if (type == 1) {
  997. this.scrollView.content.y = Math.max(Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height / 2, 0)
  998. if (Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height / 2 <= 0) {
  999. this.baseIndex = 0
  1000. }
  1001. } else if (type == 2) {
  1002. this.scrollView.content.y = Math.abs(this.setPosY(scrollPosIndex)) - (_itemHeight * this.itemScale + this.spacingY) / 2
  1003. } else {
  1004. scrollPosIndex = this.scrollDir == ScrollDirEnum.VERTICAL ? (scrollPosIndex + 1) : scrollPosIndex + this.hangCount
  1005. this.scrollView.content.y = Math.max(Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height - _itemHeight / 2 - this.spacingY, 0)
  1006. if (Math.abs(this.setPosY(scrollPosIndex)) - this.scrollView.node.height - _itemHeight / 2 - this.spacingY <= 0) {
  1007. this.baseIndex = 0
  1008. }
  1009. }
  1010. }
  1011. break
  1012. }
  1013. if (args.length > 0) {
  1014. this.extraParams = args
  1015. }
  1016. this.fixItemNodes()
  1017. // 如果content有子项目,则重制目标点位置
  1018. if (this.itemList.length > 0) {
  1019. for (let index = 0; index < this.itemList.length; index++) {
  1020. // let i = this.baseIndex + index
  1021. let elemnet = this.itemList[index]
  1022. elemnet.index = this.baseIndex + index
  1023. this.setPosX(elemnet.index, elemnet.node)
  1024. this.setPosY(elemnet.index, elemnet.node)
  1025. if (gameMethod.isEmpty(this.itemDataList[elemnet.index])) {
  1026. continue
  1027. }
  1028. this.setItemData(elemnet.node, this.itemDataList[elemnet.index], elemnet.index)
  1029. }
  1030. }
  1031. }
  1032. /**
  1033. * 尝试滚动到滚动视图中心
  1034. * @param index 标签
  1035. * @param type 滚到哪里的类型(1.居中 2.顶部|左侧 3.底部|右侧)
  1036. * @param time
  1037. */
  1038. scrollToIndex(index: number, type: number = 1, time: number = 1, offsetY: number = 0) {
  1039. if (this.itemDataList[index] == null) {
  1040. console.error("不存在此标签")
  1041. return
  1042. }
  1043. switch (this.scrollDir) {
  1044. case ScrollDirEnum.HORIZONTAL:
  1045. if (type == 1) {
  1046. this.scrollView.scrollToOffset(cc.v2(this.setPosX(index) - this.scrollView.node.width / 2, this.scrollView.getScrollOffset().y), time)
  1047. } else if (type == 2) {
  1048. this.scrollView.scrollToOffset(cc.v2(this.setPosX(index) - this.itemDistanceX / 2, this.scrollView.getScrollOffset().y), time)
  1049. } else {
  1050. index++
  1051. this.scrollView.scrollToOffset(cc.v2(this.setPosX(index) - this.scrollView.node.width, this.scrollView.getScrollOffset().y - this.itemWidth / 2 - this.spacingX), time)
  1052. }
  1053. break
  1054. case ScrollDirEnum.VERTICAL:
  1055. case ScrollDirEnum.GRID:
  1056. if (type == 1) {
  1057. this.scrollView.scrollToOffset(cc.v2(this.scrollView.getScrollOffset().x, Math.abs(this.setPosY(index)) - this.scrollView.node.height / 2 - offsetY), time)
  1058. } else if (type == 2) {
  1059. this.scrollView.scrollToOffset(cc.v2(this.scrollView.getScrollOffset().x, Math.abs(this.setPosY(index)) - this.itemDistanceY / 2 - offsetY), time)
  1060. } else {
  1061. index = this.scrollDir == ScrollDirEnum.VERTICAL ? (index + 1) : index + this.hangCount
  1062. this.scrollView.scrollToOffset(cc.v2(this.scrollView.getScrollOffset().x, Math.abs(this.setPosY(index)) - this.scrollView.node.height - this.itemHeight / 2 - this.spacingY - offsetY), time)
  1063. }
  1064. break
  1065. }
  1066. // this.scheduleOnce(() => {
  1067. // this.refreshItem(index)
  1068. // }, time)
  1069. }
  1070. getItem(index: number): cc.Node | null {
  1071. for (const key in this.itemList) {
  1072. if (this.itemList[key].index == index) {
  1073. return this.itemList[key].node
  1074. }
  1075. }
  1076. return
  1077. }
  1078. checkScrollState() {
  1079. if (this.scrollIndex > this.itemDataList.length) {
  1080. this.scrollView.stopAutoScroll()
  1081. this.scrollView.scrollToOffset(cc.v2(0, this.scrollMaxOffsetY / 2))
  1082. }
  1083. }
  1084. // 使用对象池时,在切换界面时必须使用这个方法,将所有对象放到池中
  1085. clear() {
  1086. // @TODO PoolManager
  1087. if (this.scrollView && this.scrollView.content) {
  1088. this.scrollView.content.removeAllChildren()
  1089. }
  1090. // if (this.useNodePool) {
  1091. // this.itemList.forEach(element => {
  1092. // if (this.poolType == PoolEnum.ITEM_BAG) {
  1093. // PoolManager.putItemBag(element.node)
  1094. // } else if (this.poolType == PoolEnum.ITEM_BASE) {
  1095. // PoolManager.putItemBase(element.node)
  1096. // }
  1097. // });
  1098. // } else {
  1099. // if (this.scrollView && this.scrollView.content) {
  1100. // this.scrollView.content.removeAllChildren()
  1101. // }
  1102. // }
  1103. this.itemDataList = []
  1104. this.itemList = []
  1105. }
  1106. }