import ListViewItem from "./ListViewItem"; const {ccclass, property} = cc._decorator; /** * 实现scrollview item循环滚动 ,目前只支持垂直 */ /** * count: length of listview * listViewData: data of listview */ type ListViewData = { count: number, listViewData: any } @ccclass export default class ListView extends cc.Component { // @property(cc.Label) // itemCountLabel: cc.Label = null; @property({ type: cc.Prefab, tooltip: "listview单元格,仅支持大小相等的单元格" }) prefabCell: cc.Prefab = null; // listview单元格,仅支持大小相等的单元格 @property({ type: cc.ScrollView, tooltip: "找到scrollview的控件" }) scrollView:cc.ScrollView = null; // 找到scrollview的控件 @property(cc.Integer) spacing: number = 0; // listview单元格间距 // @property(cc.String) // cellScriptName: string = ""; @property({ type: cc.Node, tooltip: "找到scrollview的content" }) content: cc.Node = null; // 找到scrollview的content, 搭配scrollview使用 @property({ type: cc.Integer, tooltip: "缓冲区总个数,即上下缓冲区分别bufferZoneCount / 2" }) bufferZoneCount: number = 4; // 缓冲区总个数,即上下缓冲区分别bufferZoneCount / 2 items: cc.Node[] = []; itemCount: number = 0; prefabCellHeight: number = 0; prefabCellWidth: number = 0; updateTimer: number = 0; updateInterval: number = 0.1; lastContentPosX: number = 0; lastContentPosY: number = 0; _bufferZone: number = 0; //when item is away from bufferZone, we relocate it _spawnCount: number = 0; // how many items we actually spawn _totalCount: number = 0; // how many items we need for the whole list _isUpdate: boolean = false; _itemData: any; // _listViewItem: ListViewItem = new ListViewItem(); onLoad () { this._bufferZone = this.scrollView.node.height; this.content.anchorX = 0.5; this.content.anchorY = 1; if (this.scrollView.horizontal) { this._bufferZone = this.scrollView.node.width; this.content.anchorX = 0; this.content.anchorY = 0.5; } this.prefabCellHeight = this.prefabCell.data.height; this.prefabCellWidth = this.prefabCell.data.width; this.scrollView.node.on('scrolling', this.scrolling.bind(this), this); this.scrollView.node.on('scroll-ended', this.scrollEnded.bind(this), this); // this.init({count: 20, listViewData: null}); } /** * 初始化ListView组件 * @param listViewData ListViewData */ public init (listViewData: ListViewData) { this.content.destroyAllChildren(); this.items = []; this.lastContentPosY = 0; this.lastContentPosX = 0; this.itemCount = listViewData.count; this._itemData = listViewData.listViewData; if (this.scrollView.vertical && this.scrollView.horizontal) { return; } this._totalCount = this.itemCount; this._spawnCount = Math.floor(this._bufferZone / (this.prefabCellHeight + this.spacing)) + this.bufferZoneCount; if (this.scrollView.horizontal) { this._spawnCount = Math.floor(this._bufferZone / (this.prefabCellWidth + this.spacing)) + this.bufferZoneCount; } if (this._spawnCount > this._totalCount) { this._spawnCount = this._totalCount; } if (this.scrollView.vertical) { this.scrollView.scrollToTop(); this.content.height = this._totalCount * (this.prefabCellHeight + this.spacing) + this.spacing; // get total content height } else if (this.scrollView.horizontal) { this.scrollView.scrollToLeft(); this.content.width = this._totalCount * (this.prefabCellWidth + this.spacing) + this.spacing; // get total content width } for (let i = 0; i < this._spawnCount; ++i) { // spawn items, we only need to do this once let item = cc.instantiate(this.prefabCell); this.content.addChild(item); item.getComponent(ListViewItem).initData(i, this._itemData); if (this.scrollView.vertical) { item.setPosition(0, -item.height * (0.5 + i) - this.spacing * (i + 1)); } else if (this.scrollView.horizontal) { item.setPosition(item.width * (i + 0.5) + this.spacing * (i + 1), 0); } this.items.push(item); } } /** * 获取item在scrollview中的位置 * @param item item位置 */ private getPositionInView (item) { // get item position in scrollview's node space let worldPos = item.parent.convertToWorldSpaceAR(item.position); let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos); return viewPos; } /** * 监听:滚动时更新 */ private scrolling(){ this._isUpdate = true; } /** * 监听:滚动停止时不执行逻辑 */ private scrollEnded() { this._isUpdate = false; } update (dt) { // this.itemCountLabel.string = (this.content.childrenCount).toString(); // scrolling excute or don't excute if (!this._isUpdate) return; this.updateTimer += dt; if (this.updateTimer < this.updateInterval) return; // we don't need to do the math every frame this.updateTimer = 0; let items = this.items; let buffer = this._bufferZone; if (this.scrollView.vertical) { let isDown = this.scrollView.content.y < this.lastContentPosY; // scrolling direction let offset = (this.prefabCellHeight + this.spacing) * items.length; for (let i = 0; i < items.length; ++i) { let viewPos = this.getPositionInView(items[i]); let item = items[i].getComponent(ListViewItem); if (isDown) { // if away from buffer zone and not reaching top of content if (viewPos.y < -buffer && items[i].y + offset < 0) { items[i].y = items[i].y + offset; items[i].getComponent(ListViewItem).initData(item.itemId -items.length, this._itemData); } } else { // if away from buffer zone and not reaching bottom of content if (viewPos.y > buffer / 2 + (this.prefabCellHeight + this.spacing) / 2 && items[i].y - offset > -this.content.height) { items[i].y = items[i].y - offset; items[i].getComponent(ListViewItem).initData(item.itemId + items.length, this._itemData); } } } // update lastContentPosY this.lastContentPosY = this.scrollView.content.y; } else if (this.scrollView.horizontal) { let isRight = this.scrollView.content.x > this.lastContentPosX; // scrolling direction let offset = (this.prefabCellWidth + this.spacing) * items.length; for (let i = 0; i < items.length; ++i) { let viewPos = this.getPositionInView(items[i]); if (isRight) { // if away from buffer zone and not reaching top of content if (viewPos.x > buffer / 2 + (this.prefabCellWidth + this.spacing) / 2 && items[i].x - offset > 0) { items[i].x = items[i].x - offset; } } else { // if away from buffer zone and not reaching bottom of content if (viewPos.x < -buffer / 2 - (this.prefabCellWidth + this.spacing) / 2 && items[i].x + offset < this.content.width) { items[i].x = items[i].x + offset; } } } // update lastContentPosX this.lastContentPosX = this.scrollView.content.x; } } };