废话不多说,直接上代码,原理看这边,看这边:https://blog.youkuaiyun.com/juedno/article/details/80767511
LoopScrow:
/**
* 无限滑动组件
* 注意点:
* 1:无限滑动遵循横排竖滑,竖排横滑规则 即uigrid.arrangement 与ScrollView的滑动方向一定是相反的, 即:不支持横排横滑,竖排竖滑(有时候只需要横排排一排,然后横滑,必须改成竖排,限制uigrid.maxPerLine为1即可)
* 2:不支持ScrollView.vertical 以及 不支持ScrollView.horizontal 同时为true 即不支持上下滑动的同时也可左右滑动 只支持一个方位
* 3:uigrid排序组件是我仿照unity的ngui的uigrid排序组件写的 没有加入适配该节点的size大小,所以需要额外在挂载uigrid的节点上添加layout组件resizeMode设置为container type设置为none
* 4:排序目前只支持TopLeft 即从左上角开始排序 (uigrid.pivot 会强制设置为Pivot.TopLeft)
*/
import { UIGrid, Pivot, Arrangement} from "./UIGrid";
const {ccclass, property} = cc._decorator;
@ccclass
export default class LoopScrow extends cc.Component
{
@property({displayName: "排序组件UIGrid", type: UIGrid})
public uiGrid: UIGrid | undefined = undefined; // 排序组件
@property({displayName: "滑动组件ScrollView", type: cc.ScrollView})
private scrowView: cc.ScrollView | undefined = undefined; // 滑动组件
@property({displayName: "遮罩mask", type: cc.Mask})
private mask: cc.Mask | undefined = undefined; // 遮罩
@property({displayName: "生成的origiItem", type: cc.Prefab})
private origiItem: cc.Prefab | undefined = undefined; // 原始item
@property
private itemCount: number = 0; // 需要初始化的item个数
private extents!: number; // 所有预制体所占长度或者是高度的一半 用在循环的时候计算item的坐标
private rows: number = 1; // 行数 (预制体所占的总行数)
private columns: number = 1; // 列数 (预制体所占的总列数)
private dataCount: number = 0; // 数据个数
private cellWidth!: number; // 宽度间隔
private cellHeight!: number; // 高度间隔
private maxPerLine!: number; // 每行或者每列限制个数
private maskSize!: cc.Size; // 遮罩组件的宽高
private rightUpLocalPos!: cc.Vec2; // 遮罩右上局部坐标
private leftDownLocalPos!: cc.Vec2; // 遮罩左下局部坐标
private centerLocalPos!: cc.Vec2; // 遮罩中心点局部坐标
private centerPos!: cc.Vec2; // 遮罩中心点世界坐标
private _items!: cc.Node[]; // 需要生成的item数据
public get items(): cc.Node[]{ return this._items; }
private cacheContentPos!: cc.Vec2; // 缓存的内容坐标(重复设置数据的时候需要还原拉动的位置)
/**
* itemIndex: 需要渲染的item下标
* dataIndex:需要渲染的数据下标
* item: 需要渲染的item节点
*/
private onRenderItem!: (itemIndex: number, dataIndex: number, item: cc.Node) => void; // 渲染回调
public onLoad()
{
this.initObj();
this.initMoveEvent();
this.initMaskCornerLocalPos();
this.initItems();
if (this.scrowView != null)
{
this.cacheContentPos = this.scrowView.content.position;
}
}
public start(): void
{
this.isUpdate = true;
}
private isUpdate: boolean = false;
public lateUpdate(): void
{
if (this.isUpdate == true)
{
this.isUpdate = false;
this.updateContentPos(this.cacheContentPos);
}
}
/**
* 初始化组件信息
*/
private initObj(): void
{
if (this.uiGrid == null || this.scrowView == null)
{
return;
}
this.cellHeight = this.uiGrid.cellHeight;
this.cellWidth = this.uiGrid.cellWidth;
this.maxPerLine = this.uiGrid.maxPerLine;
this.maskSize = this.scrowView.node.getContentSize();
if (this.scrowView.horizontal == true && this.scrowView.vertical == true)
{
console.error("无限滑动组件不支持上下左右一起滑动");
return;
}
if (this.scrowView.horizontal == true)
{
this.uiGrid.arrangement = Arrangement.Vertical; // 横排竖滑
this.rows = this.maxPerLine;
this.columns = this.itemCount / this.maxPerLine;
this.extents = this.columns * this.cellWidth * 0.5;
}else
{
this.uiGrid.arrangement = Arrangement.Horizontal; // 竖排横滑
this.columns = this.maxPerLine;
this.rows = this.itemCount / this.maxPerLine;
this.extents = this.rows * this.cellHeight * 0.5;
}
}
/**
* 把遮罩的边角左边转换成content内的局部坐标
* @param otherLocalPos
*/
private convertCornerPosToContentSpace(otherLocalPos: cc.Vec2): cc.Vec2
{
let contentPos: cc.Vec2 = cc.Vec2.ZERO;
if (this.mask == null || this.scrowView == null)
{
return contentPos;
}
const content: cc.Node = this.scrowView.content;
const worldPos: cc.Vec2 = this.mask.node.parent.convertToWorldSpaceAR(otherLocalPos);
contentPos = content.convertToNodeSpaceAR(worldPos);
return contentPos;
}
/**
* 初始化数据
* @param dataCount 数据个数
* @param onRenderItem 渲染回调
*/
public initData(dataCount: number, onRenderItem: (itemIndex: number, dataIndex: number, item: cc.Node) => void): void
{
if (onRenderItem == null)
{
console.warn("无限滑动组件渲染回调没有注册事件");
}
this.onRenderItem = onRenderItem;
this.dataCount = dataCount;
this.onInitDatas();
this.isUpdate = true;
}
/**
* 当初始化数据的时候触发事件(第一次刷新所有的item数据)
*/
private onInitDatas(): void
{
if (this.uiGrid == null || this.scrowView == null)
{
return;
}
if (this.cacheContentPos == null)
{
this.scrowView.scrollToOffset(cc.Vec2.ZERO);
this.updateCacheContentPos();
}else // 如果不是第一次设置数据 就需要还原滑动数据(位置要回归到最原始的状态)
{
this.scrowView.scrollToOffset(cc.Vec2.ZERO);
this.scrowView.content.position = this.cacheContentPos;
}
for (let index = 0; index < this.itemCount; index++)
{
const item: cc.Node = this._items[index];
const state: boolean = (index < this.dataCount);
item.active = state;
}
this.uiGrid.reposition();
for (let index = 0;