Files

699 lines
17 KiB
JavaScript
Raw Permalink Normal View History

2026-05-24 10:21:26 +08:00
"use strict";
cc._RF.push(module, 'b2510UChlJMibpfMA8BwlhT', 'nx.fx.sv.expand');
// Scripts/nx/cmp/flex/nx.fx.sv.expand.js
"use strict";
/******************************************************************
* Copyright(C) 2019 - 2020 Nx Studio
*
* 动态扩充列表(上下向)
*
* 2018.05.18
******************************************************************/
cc.Class({
"extends": cc.Component,
properties: {
svcKey: {
"default": "",
displayName: "用途标签"
},
fabItem: {
"default": null,
type: cc.Prefab,
displayName: "表项模板(二选一)"
},
tmpItem: {
"default": null,
type: cc.Node,
displayName: "表项模板(二选一)"
},
bindSCV: {
"default": null,
type: cc.ScrollView,
displayName: "操作列表"
},
autoRow: {
"default": true,
displayName: "单行宽度自适应"
},
autoSpacePer: {
"default": cc.v2(1.1, 1.1),
displayName: "自适应间隙百分比"
},
cntInRow: {
"default": 1,
displayName: "单行个数(0为自动)"
},
itemHeight: {
"default": 0,
displayName: "单项高度(0为自动)"
},
jumpBottom: {
"default": true,
displayName: "重置是否到底"
},
activeOnce: {
"default": true,
displayName: "仅激活一次"
},
selectHookers: {
"default": [],
type: cc.Component.EventHandler,
displayName: "选择回调"
},
poolOpen: {
"default": false,
displayName: "开启缓存池"
},
poolName: {
"default": "",
displayName: "自定义标记"
},
poolCount: {
"default": 10,
displayName: "缓存池大小"
},
nodEmpty: {
"default": null,
type: cc.Node,
displayName: "空展示"
},
blankPage: {
"default": false,
displayName: "整页填空"
}
},
// 编辑器特性
editor: {
// 允许当前组件在编辑器模式下运行
executeInEditMode: false,
// requireComponent 参数用来指定当前组件的依赖组件
requireComponent: null,
// 当本组件添加到节点上后,禁止同类型(含子类)的组件再添加到同一个节点,防止逻辑发生冲突
disallowMultiple: true,
// menu 用来将当前组件添加到组件菜单中,方便用户查找
menu: "Nx/组件|无限滚表"
},
// 获取表项样本
getT: function getT() {
if (this.tmpItem) {
this.tmpItem.active = false;
return this.tmpItem;
}
if (this.fabItem) {
this.tmpItem = cc.instantiate(this.fabItem);
}
// 表项模板合法性判断
if (!this.tmpItem || !this.tmpItem.getComponent("nx.fx.sv.expand.item")) {
nx.error("[FxExpandSV]无效的列表表项模板!");
return null;
}
// 开启对象池
if (this.poolOpen && !this.pool) {
var key = this.fabItem ? this.fabItem.name : this.poolName;
if (nx.dt.strEmpty(key)) {
nx.warn("[SVC]开启对象池失败,无效的标记名!");
this.poolOpen = false;
} else {
this.pool = nx.pools.getPool(key, this.tmpItem, this.poolCount);
}
}
this.tmpItem.active = false;
return this.tmpItem;
},
// 加载
onLoad: function onLoad() {
// 操作列表
if (!this.bindSCV) {
this.bindSCV = this.node.getComponent(cc.ScrollView);
if (!this.bindSCV) {
nx.error("[FxExpandSV]无效的操作列表!");
return;
}
}
// 表项模板合法性判断
this.getT();
// 初始化占位节点
this.initPlaceholder();
// 滚动条件设定
this.dtSroll = 0;
if (this.tmpNode) {
this.ckScroll = this.tmpNode.height * 0.25;
}
},
// 显示
onEnable: function onEnable() {
// 监听列表事件
if (this.bindSCV) {
this.bindSCV.node.on("scrolling", this.onScrolling, this);
}
},
// 隐藏
onDisable: function onDisable() {
// 监听列表事件
if (this.bindSCV) {
this.bindSCV.node.off("scrolling", this.onScrolling, this);
}
},
// 清理
clean: function clean() {
// 清除聚焦
if (nx.dt.arrNEmpty(this.focusList)) {
this.cleanFocus();
}
this.focusList = [];
// 监听撤销
var content = this.bindSCV.content;
content.off("size-changed", this.onListSizeChanged, this);
// 来源解除
this.data = null;
// 当前激活
this.actIndexs = [];
// 列表清理
var chd = content.children;
for (var i in chd) {
var node = chd[i];
if (node.svItem) {
node.svItem.rebind(-1, null, this.svcKey);
}
}
// 节点回收
if (this.pool) {
for (var _i in chd) {
var _node2 = chd[_i];
if (_node2.svItem) {
this.pool.put(_node2.svItem.node);
}
}
}
content.removeAllChildren();
},
// 重建列表
rebuild: function rebuild(_data) {
if (!nx.dt.arrGood(_data)) {
nx.error("[FxExpandSV]无效的列表来源类型!");
return;
}
// 表项模板合法性判断
this.getT();
// 初始化占位节点
this.initPlaceholder();
// 滚动条件设定
this.dtSroll = 0;
this.ckScroll = this.tmpNode.height * 0.25;
this.clean();
this.data = [];
// 是否填空
if (this.blankPage) {
var row = Math.ceil(this.bindSCV.node.height / this.tmpNode.height);
var count = this.cntInRow * row;
if (_data.length < count) {
for (var i = _data.length; i < count; ++i) {
_data.push({});
}
}
}
// 批量追加
this.append(_data, true);
// 监听挂载
var content = this.bindSCV.content;
content.on("size-changed", this.onListSizeChanged, this);
// 空展示
if (this.nodEmpty) {
this.nodEmpty.active = nx.dt.arrEmpty(_data);
}
},
// 追加
append: function append(_data, _fixPos) {
var _this = this;
if (_fixPos === void 0) {
_fixPos = false;
}
if (!this.data) {
nx.error("[FxExpandSV]先重建后追加!");
return;
}
if (!nx.dt.arrGood(_data)) {
nx.error("[FxExpandSV]无效的列表来源类型!");
return;
}
// 逐个创建
_data.forEach(function (_dt) {
_this._appendSingle(_dt);
});
// 强刷布局
var content = this.bindSCV.content;
var cmp = content.getComponent(cc.Layout);
if (cmp) {
cmp.updateLayout();
}
// 位置修正
if (_fixPos) {
if (this.jumpBottom) {
this.bindSCV.scrollToBottom();
this.freshRange(_data.length - 1, false);
} else {
this.bindSCV.scrollToTop();
this.freshRange(0, true);
}
} else {
this.freshRange(this.actIndexs[0], true);
}
},
// 删除
remove: function remove(_indexes) {
var _this2 = this;
if (nx.dt.arrEmpty(_indexes)) {
return;
}
// 逐个销毁
_indexes.forEach(function (_idx) {
_this2._removeSingle(_idx);
});
// 数组清理
nx.dt.arrDelete(this.data, function (_t) {
return _t == undefined;
}, false);
// 节点序列重排
this._reorderList();
// 强刷布局
var content = this.bindSCV.content;
var cmp = content.getComponent(cc.Layout);
if (cmp) {
cmp.updateLayout();
}
// 刷新
this.freshRange(0, true);
},
// 是否聚焦
isFocus: function isFocus(_index) {
return nx.dt.arrMember(this.focusList, _index);
},
// 添加聚焦
addFocus: function addFocus(_index) {
if (!this.data) {
nx.warn("[FxExpandSV]無法添加聚焦,暫無數據!");
return;
}
// 越界
if (_index < 0 || _index >= this.data.length) {
return;
}
// 是否聚焦
if (this.isFocus(_index)) {
return;
}
this.focusList.push(_index);
var chd = this.bindSCV.content.children;
var cur = chd[_index];
if (cur && cur.svItem) {
cur.svItem.onFocus();
}
},
// 移除聚焦
removeFocus: function removeFocus(_index, _justArray) {
if (_justArray === void 0) {
_justArray = false;
}
if (!_justArray) {
var chd = this.bindSCV.content.children;
var item = chd[_index];
if (item && item.svItem) {
item.svItem.outFocus();
}
}
nx.dt.arrDelete(this.focusList, function (_t) {
return _t == _index;
});
},
// 清除聚焦
cleanFocus: function cleanFocus(_justArray) {
if (_justArray === void 0) {
_justArray = false;
}
if (!_justArray && nx.dt.arrNEmpty(this.focusList)) {
var chd = this.bindSCV.content.children;
for (var i = 0; i < this.focusList.length; ++i) {
var index = this.focusList[i];
var item = chd[index];
if (item && item.svItem) {
item.svItem.outFocus();
}
}
}
this.focusList = [];
},
// 初始化占位节点
initPlaceholder: function initPlaceholder() {
// 计算占位符尺寸
var tempN = this.getT();
var content = this.bindSCV.content;
if (tempN) {
// 单行自动
if (this.autoRow || !nx.dt.numPositive(this.cntInRow, false)) {
this.cntInRow = Math.floor(content.width / (tempN.width * this.autoSpacePer.x));
if (this.cntInRow <= 0) {
// nx.error( "无限列表宽度无效!" );
this.cntInRow = 1;
}
}
// 高度自适应
if (!nx.dt.numPositive(this.itemHeight, false)) {
this.itemHeight = tempN.height * this.autoSpacePer.y;
}
var width = Math.floor(content.width / this.cntInRow);
var height = this.itemHeight;
var ph = new cc.Node("tmp");
ph.width = width;
ph.height = height;
// 保存
this.tmpNode = ph;
}
},
// 刷新可视区域
freshRange: function freshRange(_from, _toDn) {
// console.log( "刷新!" );
var sy = this.bindSCV.getScrollOffset().y;
var ey = sy + this.bindSCV.node.height;
var children = this.bindSCV.content.children;
// 碰撞检测
var collision = function collision(_node) {
if (!_node || !_node.active) {
return false;
}
var y = Math.abs(_node.y);
var up = y - _node.height / 2;
var dn = y + _node.height / 2;
// 中间
if (up >= sy && dn <= ey) {
return true;
}
// 横跨
if (up <= sy && dn >= ey) {
return true;
}
// 上交||下交
if (up <= sy && dn >= sy || up <= ey && dn >= sy) {
return true;
}
return false;
};
// 扫描
var actives = [];
if (_toDn) {
// 向后扫描
var od = false;
for (var i = _from; i < children.length; ++i) {
var nd = children[i];
if (!nd || !nd.active) {
continue;
}
if (collision(nd)) {
od = true;
actives.push(i);
nd.opacity = 255;
} else {
nd.opacity = 0;
if (od) break;
}
}
} else {
// 向前扫描
var _od = false;
for (var _i2 = _from; _i2 >= 0; --_i2) {
var _nd = children[_i2];
if (!_nd || !_nd.active) {
continue;
}
if (collision(_nd)) {
_od = true;
actives.push(_i2);
_nd.opacity = 255;
} else {
if (_od) break;
_nd.opacity = 0;
}
}
}
// 排序
actives.sort(function (a, b) {
return a - b;
});
// 列表项激活
this.activeItems(actives);
},
// 列表项激活
activeItems: function activeItems(_indexs) {
// console.log( "列表项激活:" );
// console.log( "老:", this.actIndexs );
// console.log( "新:", _indexs );
if (nx.dt.arrEmpty(_indexs)) {
return;
}
// 统计新增和过期
var adds = [];
var outs = [];
for (var i in _indexs) {
if (!nx.dt.arrMember(this.actIndexs, _indexs[i])) {
adds.push(_indexs[i]);
}
}
for (var _i3 in this.actIndexs) {
if (!nx.dt.arrMember(_indexs, this.actIndexs[_i3])) {
outs.push(_indexs[_i3]);
}
}
// console.log( "增:", adds );
// console.log( "减:", outs );
// 更新当前激活列表
this.actIndexs = _indexs;
var open = nx.dt.arrNEmpty(this.selectHookers);
// 逐个新增
var children = this.bindSCV.content.children;
for (var _i4 in adds) {
var idx = adds[_i4];
var node = children[idx];
// 不需要新建
if (node.active && node.svItem && node.svItem.node.active) {
// 需要inShow
if (!this.activeOnce && !node.svShow) {
node.svShow = true;
node.svItem.inShow();
}
} else {
// 新建
if (!node.svItem) {
var tempN = this.getT();
var item = this.pool ? this.pool.get() : cc.instantiate(tempN);
item.name = "node";
item.active = true;
item.parent = node;
item.position = cc.Vec2.ZERO;
item.svc = this;
node.svItem = item.getComponent("nx.fx.sv.expand.item");
node.svItem.openTouch(open, this._onTouchItem.bind(this));
}
node.active = true;
node.svItem.node.active = true;
node.svItem.rebind(idx, this.data[idx], this.svcKey);
}
}
// 需要outShow
if (!this.activeOnce && outs.length > 0) {
for (var _i5 in adds) {
var _idx2 = adds[_i5];
var _node3 = children[_idx2];
if (!_node3 || !_node3.svItem || !_node3.svShow) {
continue;
}
_node3.svShow = false;
_node3.svItem.outShow();
}
}
},
// 列表滚动
onScrolling: function onScrolling() {
// 无效越界屏蔽
var y = this.bindSCV.getScrollOffset().y;
var max = this.bindSCV.getMaxScrollOffset().y;
if (y < 0 || y > max) {
return;
}
// 不需要更新
var dt = y - this.dtSroll;
if (Math.abs(dt) < this.ckScroll || this.actIndexs.length == 0) {
return;
}
// 刷新
var from = dt > 0 ? this.actIndexs[0] : this.actIndexs[this.actIndexs.length - 1];
var toDn = dt > 0 ? true : false;
this.freshRange(from, toDn);
this.dtSroll = y;
},
// 列表动态长度改变
onListSizeChanged: function onListSizeChanged() {
// console.log( "onListSizeChanged" );
if (this.actIndexs.length > 0) {
this.freshRange(this.actIndexs[0], true);
}
},
// 单个追加
_appendSingle: function _appendSingle(_dt) {
if (!_dt) {
nx.error("[FxExpandSV]追加失败,空数据!");
return;
}
// 追加数据
this.data.push(_dt);
// 列表项同步
var content = this.bindSCV.content;
var index = this.data.length - 1;
var node = content.children[index];
if (!node) {
// 创建节点
node = cc.instantiate(this.tmpNode);
node.parent = content;
}
// 这个时候svOrder绝对不会大于0,否则视为有BUG
if (nx.dt.numPositive(node.svOrder, false)) {
nx.error("[FxExpandSV]追加出错,缓存节点报错:%d", index);
}
node.active = true;
node.svShow = false;
node.svOrder = index;
},
// 单个销毁
_removeSingle: function _removeSingle(_index) {
// 越界
if (_index < 0 || _index >= this.data.length) {
nx.error("[FxExpandSV]单个销毁失败,无效位置:%d", _index);
return;
}
// 删除数据
delete this.data[_index];
// 聚焦移除
this.removeFocus(_index, true);
// 删除节点
var chd = this.bindSCV.content.children;
for (var i in chd) {
var node = chd[i];
if (node && node.svOrder == _index) {
node.destroy();
return;
}
}
// 失败
nx.error("[FxExpandSV]单个销毁失败,节点销毁失败:%d", _index);
},
// 节点序列重排
_reorderList: function _reorderList() {
var idx = 0;
var chd = this.bindSCV.content.children;
for (var i in chd) {
var node = chd[i];
node.svOrder = node.active ? idx++ : -1;
if (!node.active && node.svItem) {
if (node.svItem != -1) {
node.svItem.rebind(-1, null, this.svcKey);
}
node.svItem.node.active = false;
}
}
// 聚焦清理
this.cleanFocus(true);
},
// 节点点击
_onTouchItem: function _onTouchItem(_item) {
var _this3 = this;
// 无效
if (!_item || !nx.dt.numPositive(_item.index, true)) {
return;
}
// 有监听
if (nx.dt.arrNEmpty(this.selectHookers)) {
this.selectHookers.forEach(function (_hk) {
_hk.customEventData = _item;
});
nx.dt.trycatch(function () {
cc.Component.EventHandler.emitEvents(_this3.selectHookers);
});
}
},
// 数据/节点同步检测
checkData: function checkData() {
var len = 0;
var orders = [];
var chd = this.bindSCV.content.children;
for (var i in chd) {
var node = chd[i];
orders.push(node.svOrder);
if (node.active) {
++len;
}
}
nx.debug("[FxExpandSV]同步检测:");
nx.debug("[FxExpandSV]\t数据:%d", this.data.length);
nx.debug("[FxExpandSV]\t列表项: 总:%d 有效:%d", chd.length, len);
console.log(orders);
}
});
cc._RF.pop();