二七王:前端 SpriteIndex,精灵键名扁平索引
布局配置按键名引用精灵,本模块把嵌套的精灵常量展平供查。 重复键与未知键一律抛错——静默覆盖会让界面指向别处的精灵且极难排查。 用例含真实常量的全量建索引校验。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
///////////////////////////////////////////////////////////////
|
||||
////////// EQW_SpriteIndex: 精灵键名 → ID 扁平索引 /////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
// 精灵常量以 UI View 为组织单位,View 下是一个个 group 容器:
|
||||
// EQW_Sprites.XxxView = { Layer: 图层, GroupName: { id: 群组, 精灵键: id, ... } }
|
||||
// 而布局配置里 attach.target 写的是【键名】(清单 §6.1:ID 回填时只改一处
|
||||
// 映射表),故需把这棵树展平成 { 键名: 精灵ID } 供求解器查用;
|
||||
// group 容器把精灵与群组 ID 绑在一起,顺带能给出 { 键名: 群组ID }。
|
||||
// 【显式失败】重复键、值非数字、结构不合法、查不到的键一律抛错——静默覆盖
|
||||
// 会让某个界面指向另一个界面的精灵,且极难排查(工程总则 §7)。
|
||||
var EQW_SpriteIndex = EQW_SpriteIndex || {
|
||||
|
||||
_index: null,
|
||||
_groupMap: null,
|
||||
|
||||
//遍历 树 → View → group → 精灵,对每个精灵回调 fn(key, spriteId, groupId)
|
||||
//【不变式】一个 View 恰好一个图层:View 必须有数字型的 Layer 键。
|
||||
//跨图层的界面应拆成两个 View(各带自己的 Layer)——一个 View 将来对应一个
|
||||
//BaseComponent 组件,把两个图层塞进一个 View 会让「哪个 group 属于哪个图层」
|
||||
//只能靠注释隐含、无法机械判定。
|
||||
_walk: function (spriteTree, fn) {
|
||||
for (var viewName in spriteTree) {
|
||||
if (!spriteTree.hasOwnProperty(viewName)) { continue; }
|
||||
var view = spriteTree[viewName];
|
||||
if (typeof view.Layer !== 'number') {
|
||||
throw new Error('[EQW_SpriteIndex] View ' + viewName +
|
||||
' 缺少数字型的 Layer(一个 View 恰好一个图层;跨图层请拆成两个 View)');
|
||||
}
|
||||
for (var groupName in view) {
|
||||
if (!view.hasOwnProperty(groupName)) { continue; }
|
||||
if (groupName === 'Layer') { continue; } //View 级图层声明
|
||||
var group = view[groupName];
|
||||
if (!group || typeof group !== 'object') {
|
||||
throw new Error('[EQW_SpriteIndex] ' + viewName + '.' + groupName +
|
||||
' 不是 group 容器(值应为对象)——图层声明请用保留键名 Layer');
|
||||
}
|
||||
if (typeof group.id !== 'number') {
|
||||
throw new Error('[EQW_SpriteIndex] group 容器 ' + viewName + '.' + groupName +
|
||||
' 缺少数字型的 id(群组 ID)');
|
||||
}
|
||||
for (var key in group) {
|
||||
if (!group.hasOwnProperty(key)) { continue; }
|
||||
if (key === 'id') { continue; } //群组 ID
|
||||
if (typeof group[key] !== 'number') {
|
||||
throw new Error('[EQW_SpriteIndex] 精灵 ' + key + ' 的值不是数字(' +
|
||||
viewName + '.' + groupName + ')');
|
||||
}
|
||||
fn(key, group[key], group.id, viewName, groupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//展平成 { 键名: 精灵ID }
|
||||
build: function (spriteTree) {
|
||||
var index = {};
|
||||
this._walk(spriteTree, function (key, spriteId, groupId, viewName, groupName) {
|
||||
if (index.hasOwnProperty(key)) {
|
||||
throw new Error('[EQW_SpriteIndex] 精灵键名重复: ' + key +
|
||||
'(' + viewName + '.' + groupName + ' 与更早的定义冲突)');
|
||||
}
|
||||
index[key] = spriteId;
|
||||
});
|
||||
return index;
|
||||
},
|
||||
|
||||
//展平成 { 键名: 群组ID },供「显隐走群组」时查精灵属于哪个群组
|
||||
buildGroupMap: function (spriteTree) {
|
||||
var map = {};
|
||||
this._walk(spriteTree, function (key, spriteId, groupId) {
|
||||
map[key] = groupId;
|
||||
});
|
||||
return map;
|
||||
},
|
||||
|
||||
//用给定的树(默认 EQW_Sprites)建立两张内部索引
|
||||
init: function (spriteTree) {
|
||||
var tree = spriteTree || EQW_Sprites;
|
||||
this._index = this.build(tree);
|
||||
this._groupMap = this.buildGroupMap(tree);
|
||||
return this._index;
|
||||
},
|
||||
|
||||
//按键名取精灵 ID;查不到抛错,绝不返回 undefined 让 SpriteManager 静默无效
|
||||
idOf: function (key) {
|
||||
if (!this._index) { throw new Error('[EQW_SpriteIndex] 尚未 init()'); }
|
||||
if (!this._index.hasOwnProperty(key)) {
|
||||
throw new Error('[EQW_SpriteIndex] 未知精灵键名: ' + key);
|
||||
}
|
||||
return this._index[key];
|
||||
},
|
||||
|
||||
//按键名取它所属的群组 ID
|
||||
groupIdOf: function (key) {
|
||||
if (!this._groupMap) { throw new Error('[EQW_SpriteIndex] 尚未 init()'); }
|
||||
if (!this._groupMap.hasOwnProperty(key)) {
|
||||
throw new Error('[EQW_SpriteIndex] 未知精灵键名: ' + key);
|
||||
}
|
||||
return this._groupMap[key];
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user