"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();