Files
creator-collection-view/list-3x/assets/lib/page-layout.ts

144 lines
6.3 KiB
TypeScript
Raw Normal View History

2025-12-07 20:00:23 +08:00
import { log, math, warn } from "cc";
import { YXCollectionView, YXIndexPath, YXLayout, YXLayoutAttributes } from "./yx-collection-view";
/**
* PageView
*/
export class PageLayout extends YXLayout {
/**
*
*/
private pagingEnabled: boolean = true
/**
*
*/
pagingAnimationDuration: number = 0.5
/**
*
* 注意: 当开启循环滚动时YXCollectionView `ignoreScrollEndedDuringAutoScroll = true`
* 注意: 开启循环滚动会生成较大范围的 `indexPath`使
*
* @example
* listComp.ignoreScrollEndedDuringAutoScroll = true
* listComp.numberOfItems = () => {
* return <data-length>
* }
* listComp.cellForItemAt = (indexPath, collectionView) => {
* let index = indexPath.row % <data-length> // 通过取余获取真实数据索引
* const cell = collectionView.dequeueReusableCell(`cell`)
* return cell
* }
*/
loop: boolean = false
/**
* (...)
* 穿
* 1 ~ 10默认: 5 ()
*/
scrollRangeMultiplier: number = 5
private get safeScrollRangeMultiplier(): number {
let value = Math.floor(this.scrollRangeMultiplier)
return Math.max(1, value)
}
prepare(collectionView: YXCollectionView): void {
collectionView.scrollView.horizontal = true
collectionView.scrollView.vertical = false
if (collectionView.scrollDirection === YXCollectionView.ScrollDirection.VERTICAL) {
warn(`PageLayout 仅支持水平方向排列`)
}
const numberOfSections = collectionView.getNumberOfSections()
if (numberOfSections > 1) { warn(`GridLayout 暂时不支持分区模式`) }
let contentSize = collectionView.scrollView.view.contentSize.clone()
let attrs = []
let itemSize = collectionView.scrollView.view.contentSize
let numberOfItems = collectionView.getNumberOfItems(0)
if (this.loop) {
numberOfItems = numberOfItems * 3 * this.safeScrollRangeMultiplier
if (collectionView.ignoreScrollEndedDuringAutoScroll == false) {
warn(`PageLayout: 开启循环滚动时建议将 YXCollectionView.ignoreScrollEndedDuringAutoScroll 设置为 true`)
}
}
for (let index = 0; index < numberOfItems; index++) {
let attr = YXLayoutAttributes.layoutAttributesForCell(new YXIndexPath(0, index))
attr.frame.x = itemSize.width * index
attr.frame.y = 0
attr.frame.width = itemSize.width
attr.frame.height = itemSize.height
attrs.push(attr)
contentSize.width = Math.max(contentSize.width, attr.frame.xMax)
}
this.attributes = attrs
this.contentSize = contentSize
}
initOffset(collectionView: YXCollectionView): void {
if (this.loop) {
let numberOfItems = collectionView.getNumberOfItems(0)
let offset = new math.Vec2()
offset.x = numberOfItems * this.safeScrollRangeMultiplier * collectionView.scrollView.view.width
offset.y = 0
collectionView.scrollView.scrollToOffset(offset, 0)
} else {
collectionView.scrollView.scrollToLeft()
}
}
targetOffset(collectionView: YXCollectionView, touchMoveVelocity: math.Vec3, startOffset: math.Vec2, originTargetOffset: math.Vec2, originScrollDuration: number): { offset: math.Vec2; time?: number; attenuated?: boolean; } | null {
if (this.pagingEnabled == false) {
return null
}
let offset = collectionView.scrollView.getScrollOffset()
offset.x = - offset.x
let threshold = 0.2
let idx = Math.round(offset.x / collectionView.scrollView.view.width)
let r = touchMoveVelocity.x / collectionView.scrollView.view.width
if (startOffset && Math.abs(r) >= threshold) {
idx = Math.round(startOffset.x / collectionView.scrollView.view.width) + (r > 0 ? -1 : 1)
}
offset.x = idx * collectionView.scrollView.view.width
return { offset: offset, time: this.pagingAnimationDuration }
}
layoutAttributesForElementsInRect(rect: math.Rect, collectionView: YXCollectionView): YXLayoutAttributes[] {
if (collectionView.scrollView.view.width <= 0 || this.attributes.length <= 0) {
return super.layoutAttributesForElementsInRect(rect, collectionView)
}
// 直接计算出当前元素位置,另外额外返回左右两边的元素
let result = []
let idx = Math.round(rect.x / collectionView.scrollView.view.width)
let previousIdx = idx - 1
let latterIdx = idx + 1
if (idx >= 0 && idx < this.attributes.length) {
result.push(this.attributes[idx])
}
if (previousIdx >= 0 && previousIdx < this.attributes.length && previousIdx != idx) {
result.push(this.attributes[previousIdx])
}
if (latterIdx >= 0 && latterIdx < this.attributes.length && latterIdx != idx) {
result.push(this.attributes[latterIdx])
}
return result
}
onScrollEnded(collectionView: YXCollectionView): void {
if (this.loop == false) {
return
}
let numberOfItems = collectionView.getNumberOfItems(0)
let offset = collectionView.scrollView.getScrollOffset()
offset.x = - offset.x
let idx = Math.round(offset.x / collectionView.scrollView.view.width) % numberOfItems
offset.x = collectionView.scrollView.view.width * (numberOfItems * this.safeScrollRangeMultiplier + idx)
collectionView.scrollView.scrollToOffset(offset)
// 直接设置滚动位置不会触发刷新,这里强制刷新一下
collectionView.markForUpdateVisibleData(true)
}
}