Files
erqiwang_youle/client/js/gameabc-framework/ui/DynamicSpriteList.js
T

831 lines
26 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* ============================================================================
* DynamicSpriteList.js - 动态精灵列表工具
* ============================================================================
*
* 基于 gameabc 框架的精灵复制功能,提供动态列表创建、管理和交互的完整解决方案。
*
* 核心功能:
* - 动态创建/删除精灵列表
* - 自动滑动和边界回弹
* - 点击检测和事件分发
* - 裁剪区域管理
*
* 适用场景:
* - 小局结算列表
* - 大局结算列表
* - 听牌提示列表
* - 战绩列表
* - 排行榜
*
* 使用示例:
* ```javascript
* // 1. 创建列表实例
* var myList = new DynamicSpriteList({
* containerId: 100, // 父精灵ID(容器)
* clipArea: { x: 50, y: 100, width: 600, height: 400 },
* rowHeight: 80,
* templates: {
* background: { spriteId: 101 },
* title: { spriteId: 102, offset: { x: 20, y: 10 } },
* button: { spriteId: 103, offset: { x: 500, y: 20 } }
* }
* });
*
* // 2. 设置数据并渲染
* myList.setData([
* { title: '第1行', buttonText: '查看' },
* { title: '第2行', buttonText: '查看' }
* ]);
*
* // 3. 监听点击事件
* myList.onClick = function(type, index, data) {
* console.log('点击了', type, '第', index, '行');
* };
*
* // 4. 在事件处理函数中调用
* // mousedown: myList.handleMouseDown(spid, x, y);
* // mousemove: myList.handleMouseMove(spid, offsetY);
* // mouseup: myList.handleMouseUp(spid, x, y);
* // drawbegin: myList.handleDrawBegin(spid);
*
* // 5. 销毁
* myList.destroy();
* ```
*
* ============================================================================
* @guide 开发指南 - DynamicSpriteList 完整使用规范
* ============================================================================
*
* 【准备工作:在编辑器中预置精灵】
* 1. 在编辑器中放置一个容器精灵(作为 containerId)
* 2. 在容器精灵旁边放置各列模板精灵(通常隐藏):
* - 行背景模板(每行底色/分隔线)
* - 数据列模板(牌图、分数、数量等,每种一个)
* 3. 记录这些模板精灵的 ID,用于配置 templates
*
* 【初始化列表(听牌提示列表示例)】
* var tingList = new DynamicSpriteList({
* containerId: sprites.LIST_CONTAINER, // 容器精灵
* clipArea: {
* x: 0, y: 0, // 相对容器左上角
* width: 640, height: 480 // 可见区域尺寸
* },
* rowHeight: 80, // 每行高度(像素)
* templates: {
* // 每列都需要一个 spriteId(模板)
* rowBg: { spriteId: sprites.ROW_TEMPLATE },
* card: { spriteId: sprites.CARD_TEMPLATE, offset: { x: 20, y: 10 } },
* score: { spriteId: sprites.SCORE_TEMPLATE, offset: { x: 120, y: 10 } },
* jingBadge: { spriteId: sprites.JING_BADGE, offset: { x: 90, y: 5 } },
* remainBadge: { spriteId: sprites.REMAIN_BADGE, offset: { x: 200, y: 5 } },
* rowSeparator: { spriteId: sprites.ROW_SEPARATOR }
* },
* scrollSensitivity: 5, // 移动5px以上才触发滚动
* bounceSpeed: 1 // 回弹速度
* });
*
* 【设置数据(触发渲染)】
* // 数据数组中每个元素对应一行
* // setData 会清除旧行、重新创建新行
* tingList.setData(tingHints); // tingHints = [{ code, score, remain, isJing }, ...]
*
* 【监听点击(在 setData 之前或之后设置均可)】
* tingList.onClick = function(type, rowIndex, rowData) {
* // type: 模板键名,如 'card'、'score'
* // rowIndex: 第几行(0-based)
* // rowData: 该行对应的数据对象
* if (type === 'card') {
* self._onCardClick(rowData);
* }
* };
*
* 【接入 SpriteEventController 事件(必须接入,否则列表不响应交互)】
* var self = this;
* // 在 init 中注册容器精灵的鼠标事件
* SpriteEventController.registerMouseDown(sprites.LIST_CONTAINER, function(event) {
* tingList.handleMouseDown(event.spid, event.offsetX, event.offsetY);
* });
* SpriteEventController.registerMouseMove(sprites.LIST_CONTAINER, function(event) {
* tingList.handleMouseMove(event.spid, event.offsetY);
* });
* SpriteEventController.registerMouseUp(sprites.LIST_CONTAINER, function(event) {
* tingList.handleMouseUp(event.spid, event.offsetX, event.offsetY);
* });
* SpriteEventController.registerDrawBegin(sprites.LIST_CONTAINER, function(event) {
* tingList.handleDrawBegin(event.spid);
* });
*
* 【销毁(组件关闭/destroy时必须调用)】
* tingList.destroy(); // 清除所有复制精灵,解除事件绑定
*
* 【注意事项】
* - destroy() 必须在组件 onDestroy/hide 时调用,否则复制精灵会残留
* - 每次 setData 会完全重建行,不要频繁调用(建议数据变化时才调用)
* - templates 中的 spriteId 是编辑器中预置的精灵ID,不是复制出来的ID
* - 如果列表不需要滚动,设置 enableScroll: false 可提升性能
*
* @author JinXian Team
* @version 1.0.0
* @since 2025-12-13
*/
'use strict';
// ============================================================================
// DynamicSpriteList 类定义
// ============================================================================
/**
* 动态精灵列表构造函数
*
* @constructor
* @param {Object} config - 列表配置对象
* @param {number} config.containerId - 父精灵ID(容器精灵,所有复制精灵将附着在此精灵上)
* @param {Object} config.clipArea - 裁剪区域配置(可见区域)
* @param {number} config.clipArea.x - 裁剪区域左上角X坐标
* @param {number} config.clipArea.y - 裁剪区域左上角Y坐标
* @param {number} config.clipArea.width - 裁剪区域宽度
* @param {number} config.clipArea.height - 裁剪区域高度
* @param {number} config.rowHeight - 每行高度(像素)
* @param {Object} config.templates - 模板精灵配置
* @param {number} [config.scrollSensitivity=2] - 滑动灵敏度(移动多少像素触发滑动)
* @param {number} [config.bounceSpeed=1] - 回弹动画速度
* @param {boolean} [config.enableScroll=true] - 是否启用滚动
* @param {boolean} [config.enableClick=true] - 是否启用点击
*
* @example
* // 模板配置示例
* templates: {
* // 行背景 - 必须有
* background: {
* spriteId: 101, // 模板精灵ID
* offset: { x: 0, y: 0 } // 相对行起点的偏移(可选,默认0,0)
* },
* // 文字模板
* title: {
* spriteId: 102,
* offset: { x: 20, y: 15 },
* textProperty: 'title' // 对应数据中的属性名
* },
* // 按钮模板
* button: {
* spriteId: 103,
* offset: { x: 500, y: 20 },
* clickable: true // 标记为可点击
* }
* }
*/
function DynamicSpriteList(config) {
// ========================================================================
// 参数验证
// ========================================================================
if (!config) {
throw new Error('DynamicSpriteList: config is required');
}
if (typeof config.containerId !== 'number') {
throw new Error('DynamicSpriteList: containerId must be a number');
}
if (!config.clipArea) {
throw new Error('DynamicSpriteList: clipArea is required');
}
if (typeof config.rowHeight !== 'number' || config.rowHeight <= 0) {
throw new Error('DynamicSpriteList: rowHeight must be a positive number');
}
var hasTemplates = config.templates && typeof config.templates === 'object';
var hasRenderRow = typeof config.renderRow === 'function';
if (!hasTemplates && !hasRenderRow) {
throw new Error('DynamicSpriteList: templates or renderRow is required');
}
// ========================================================================
// 实例属性初始化
// ========================================================================
/**
* 容器精灵ID
* @type {number}
*/
this.containerId = config.containerId;
/**
* 裁剪区域配置
* @type {Object}
*/
this.clipArea = {
x: config.clipArea.x || 0,
y: config.clipArea.y || 0,
width: config.clipArea.width || 0,
height: config.clipArea.height || 0
};
/**
* 每行高度
* @type {number}
*/
this.rowHeight = config.rowHeight;
/**
* 模板精灵配置
* @type {Object}
*/
this.templates = config.templates || null;
this.renderRow = config.renderRow || null;
/**
* 滑动灵敏度
* @type {number}
*/
this.scrollSensitivity = config.scrollSensitivity || 2;
/**
* 回弹动画速度
* @type {number}
*/
this.bounceSpeed = config.bounceSpeed || 1;
/**
* 是否启用滚动
* @type {boolean}
*/
this.enableScroll = config.enableScroll !== false;
/**
* 是否启用点击
* @type {boolean}
*/
this.enableClick = config.enableClick !== false;
// ========================================================================
// 运行时状态
// ========================================================================
/**
* 当前数据
* @type {Array}
* @private
*/
this._data = [];
/**
* 是否正在滑动
* @type {boolean}
* @private
*/
this._isSliding = false;
/**
* 是否已渲染
* @type {boolean}
* @private
*/
this._isRendered = false;
/**
* 已创建的精灵记录 (用于删除)
* @type {Array}
* @private
*/
this._createdSprites = [];
this._tagIndex = {};
this._nextTag = 1;
// ========================================================================
// 事件回调
// ========================================================================
/**
* 点击事件回调
* @type {Function|null}
* @param {string} templateName - 点击的模板类型名称
* @param {number} rowIndex - 行索引(从0开始)
* @param {Object} rowData - 该行的数据对象
*
* @example
* list.onClick = function(templateName, rowIndex, rowData) {
* if (templateName === 'button') {
* console.log('点击了第', rowIndex, '行的按钮');
* console.log('数据:', rowData);
* }
* };
*/
this.onClick = null;
/**
* 滚动事件回调
* @type {Function|null}
* @param {number} scrollY - 当前滚动位置
* @param {number} maxScrollY - 最大滚动位置
*
* @example
* list.onScroll = function(scrollY, maxScrollY) {
* var progress = scrollY / maxScrollY;
* console.log('滚动进度:', progress);
* };
*/
this.onScroll = null;
}
// ============================================================================
// 公共方法
// ============================================================================
/**
* 设置数据并渲染列表
*
* @param {Array} data - 数据数组,每个元素对应一行
* @returns {DynamicSpriteList} 返回自身,支持链式调用
*
* @example
* list.setData([
* { title: '战绩1', score: '+100', time: '12:30' },
* { title: '战绩2', score: '-50', time: '12:25' },
* { title: '战绩3', score: '+200', time: '12:20' }
* ]);
*/
DynamicSpriteList.prototype.setData = function(data) {
if (!Array.isArray(data)) {
console.error('DynamicSpriteList.setData: data must be an array');
return this;
}
// 先清除旧内容
this.clear();
// 保存数据
this._data = data;
// 渲染列表
this._render();
return this;
};
/**
* 获取当前数据
*
* @returns {Array} 当前数据数组的副本
*/
DynamicSpriteList.prototype.getData = function() {
return this._data.slice();
};
/**
* 获取数据行数
*
* @returns {number} 数据行数
*/
DynamicSpriteList.prototype.getRowCount = function() {
return this._data.length;
};
/**
* 显示列表
*
* @returns {DynamicSpriteList} 返回自身,支持链式调用
*/
DynamicSpriteList.prototype.show = function() {
set_self(this.containerId, 37, 1, 0, 0);
return this;
};
/**
* 隐藏列表
*
* @returns {DynamicSpriteList} 返回自身,支持链式调用
*/
DynamicSpriteList.prototype.hide = function() {
set_self(this.containerId, 37, 0, 0, 0);
return this;
};
/**
* 清除所有动态创建的精灵
*
* @returns {DynamicSpriteList} 返回自身,支持链式调用
*/
DynamicSpriteList.prototype.clear = function() {
for (var i = 0; i < this._createdSprites.length; i++) {
ifast_dllpritefromspritecopy(this.containerId, this._createdSprites[i].tag);
}
this._createdSprites = [];
this._tagIndex = {};
this._nextTag = 1;
this._isRendered = false;
this._isSliding = false;
return this;
};
/**
* 销毁列表实例
* 释放所有资源,清除所有精灵
*/
DynamicSpriteList.prototype.destroy = function() {
this.clear();
this.hide();
this._data = [];
this.onClick = null;
this.onScroll = null;
};
/**
* 滚动到指定位置
*
* @param {number} y - 目标Y坐标
* @param {boolean} [animated=true] - 是否使用动画
* @returns {DynamicSpriteList} 返回自身,支持链式调用
*/
DynamicSpriteList.prototype.scrollTo = function(y, animated) {
var currentY = get_self(this.containerId, 19, 0, 0, 0);
var targetY = this.clipArea.y - y;
// 边界限制
var contentHeight = this._data.length * this.rowHeight;
var minY = this.clipArea.y + this.clipArea.height - contentHeight;
if (targetY < minY) {
targetY = minY;
}
if (targetY > this.clipArea.y) {
targetY = this.clipArea.y;
}
if (animated !== false) {
play_ani(1, this.containerId, 19, currentY, targetY, 0,
Math.abs(targetY - currentY), 0, 0, 0, this.bounceSpeed, 0, 0, 0);
} else {
set_self(this.containerId, 19, targetY, 0, 0, 0);
}
return this;
};
/**
* 滚动到顶部
*
* @param {boolean} [animated=true] - 是否使用动画
* @returns {DynamicSpriteList} 返回自身,支持链式调用
*/
DynamicSpriteList.prototype.scrollToTop = function(animated) {
return this.scrollTo(0, animated);
};
/**
* 滚动到底部
*
* @param {boolean} [animated=true] - 是否使用动画
* @returns {DynamicSpriteList} 返回自身,支持链式调用
*/
DynamicSpriteList.prototype.scrollToBottom = function(animated) {
var contentHeight = this._data.length * this.rowHeight;
var maxScroll = contentHeight - this.clipArea.height;
if (maxScroll < 0) {
maxScroll = 0;
}
return this.scrollTo(maxScroll, animated);
};
/**
* 滚动到指定行
*
* @param {number} rowIndex - 行索引(从0开始)
* @param {boolean} [animated=true] - 是否使用动画
* @returns {DynamicSpriteList} 返回自身,支持链式调用
*/
DynamicSpriteList.prototype.scrollToRow = function(rowIndex, animated) {
var y = rowIndex * this.rowHeight;
return this.scrollTo(y, animated);
};
// ============================================================================
// 事件处理方法 - 需要在对应的事件回调中调用
// ============================================================================
/**
* 处理鼠标/触摸按下事件
* 需要在 utlmousedown 回调中调用
*
* @param {number} spid - 按下的精灵ID
* @param {number} x - 按下位置X坐标
* @param {number} y - 按下位置Y坐标
* @returns {boolean} 如果事件被处理返回 true
*
* @example
* // 在 SpriteEventController 或 Game_Modify 中
* utlmousedown: function(gameid, spid, downx, downy) {
* if (myList.handleMouseDown(spid, downx, downy)) {
* return; // 事件已处理
* }
* // 其他处理...
* }
*/
DynamicSpriteList.prototype.handleMouseDown = function(spid, x, y) {
if (spid === this.containerId) {
this._isSliding = false;
return true;
}
return false;
};
/**
* 处理鼠标/触摸移动事件
* 需要在 utlmousemove 回调中调用
*
* @param {number} spid - 移动的精灵ID
* @param {number} offsetY - Y方向移动偏移量
* @returns {boolean} 如果事件被处理返回 true
*
* @example
* // 在 SpriteEventController 或 Game_Modify 中
* utlmousemove: function(gameid, spid, downx, downy, movex, movey, timelong, offmovex, offmovey) {
* if (myList.handleMouseMove(spid, offmovey)) {
* return; // 事件已处理
* }
* // 其他处理...
* }
*/
DynamicSpriteList.prototype.handleMouseMove = function(spid, offsetY) {
if (!this.enableScroll) {
return false;
}
if (spid === this.containerId) {
if (Math.abs(offsetY) > this.scrollSensitivity) {
// 增量移动容器
set_self(this.containerId, 19, offsetY, 1, 0, 0);
this._isSliding = true;
// 触发滚动回调
if (typeof this.onScroll === 'function') {
var currentY = get_self(this.containerId, 19, 0, 0, 0);
var scrollY = this.clipArea.y - currentY;
var contentHeight = this._data.length * this.rowHeight;
var maxScrollY = contentHeight - this.clipArea.height;
this.onScroll(scrollY, maxScrollY > 0 ? maxScrollY : 0);
}
}
return true;
}
return false;
};
/**
* 处理鼠标/触摸松开事件
* 需要在 mouseup 回调中调用
*
* @param {number} spidDown - 按下时的精灵ID
* @param {number} spidUp - 松开时的精灵ID
* @param {number} upX - 松开位置X坐标
* @param {number} upY - 松开位置Y坐标
* @returns {boolean} 如果事件被处理返回 true
*
* @example
* // 在 SpriteEventController 或 Game_Modify 中
* mouseup: function(gameid, spid_down, downx, downy, spid_up, upx, upy, timelong) {
* if (myList.handleMouseUp(spid_down, spid_up, upx, upy)) {
* return; // 事件已处理
* }
* // 其他处理...
* }
*/
DynamicSpriteList.prototype.handleMouseUp = function(spidDown, spidUp, upX, upY) {
if (spidDown !== this.containerId) {
return false;
}
// 处理边界回弹
this._handleBounce();
// 处理点击(非滑动状态)
if (!this._isSliding && this.enableClick && spidDown === spidUp) {
this._handleClick(upX, upY);
}
return true;
};
/**
* 处理绘制开始事件
* 需要在 utlgamemydrawbegin 回调中调用,用于设置裁剪区域
*
* @param {number} spid - 正在绘制的精灵ID
* @returns {boolean} 如果事件被处理返回 true
*
* @example
* // 在 SpriteEventController 或 Game_Modify 中
* utlgamemydrawbegin: function(gameid, spid, times, timelong) {
* if (myList.handleDrawBegin(spid)) {
* return; // 已设置裁剪区域
* }
* // 其他处理...
* }
*/
DynamicSpriteList.prototype.handleDrawBegin = function(spid) {
if (spid === this.containerId) {
set_clip(0, 0,
this.clipArea.x,
this.clipArea.y,
this.clipArea.width,
this.clipArea.height
);
return true;
}
return false;
};
// ============================================================================
// 私有方法
// ============================================================================
/**
* 内部创建精灵并登记到 _tagIndex / _createdSprites
* @param {string} name - 精灵名称(模板键名或自定义名)
* @param {number} templateSpriteId - 模板精灵 ID
* @param {number} x - X 坐标
* @param {number} y - Y 坐标(已叠加 rowBaseY)
* @param {number} rowIndex - 所在行索引
* @param {boolean} clickable - 是否可点击
* @returns {*} 创建出的精灵 ID
* @private
*/
DynamicSpriteList.prototype._addSpriteInternal = function (name, templateSpriteId, x, y, rowIndex, clickable) {
var tag = this._nextTag++;
var spriteId = ifast_addtospritefromspritecopy(this.containerId, templateSpriteId, x, y, tag);
var rec = { tag: tag, spriteId: spriteId, rowIndex: rowIndex, name: name, clickable: (clickable !== false) };
this._createdSprites.push(rec);
this._tagIndex[tag] = rec;
return spriteId;
};
/**
* 渲染列表
* @private
*/
DynamicSpriteList.prototype._render = function() {
set_self(this.containerId, 18, this.clipArea.x, 0, 0);
set_self(this.containerId, 19, this.clipArea.y, 0, 0);
var contentHeight = this._data.length * this.rowHeight;
set_self(this.containerId, 21, contentHeight, 0, 0);
if (this.templates) {
var tnames = Object.keys(this.templates);
if (tnames.length > 0) {
var w = get_self(this.templates[tnames[0]].spriteId, 20, 0, 0, 0);
set_self(this.containerId, 20, w, 0, 0);
}
} else {
// renderRow 模式:容器宽度取裁剪区宽,保证整行可视宽都在容器 hit 区(点击/拖动检测)内。
// 否则容器保持编辑器预置窄宽度,超出部分点击/拖动报的 spid≠containerId 而失效(对账 bak set_self(fSpid,20,bg宽))。
set_self(this.containerId, 20, this.clipArea.width, 0, 0);
}
for (var rowIndex = 0; rowIndex < this._data.length; rowIndex++) {
var rowData = this._data[rowIndex];
var rowY = rowIndex * this.rowHeight;
if (this.renderRow) {
this.renderRow(this._makeRowContext(rowIndex, rowData, rowY));
} else {
var names = Object.keys(this.templates);
for (var i = 0; i < names.length; i++) {
var name = names[i];
var t = this.templates[name];
var ox = (t.offset && t.offset.x) || 0;
var oy = (t.offset && t.offset.y) || 0;
var sid = this._addSpriteInternal(name, t.spriteId, ox, rowY + oy, rowIndex, t.clickable !== false);
if (t.textProperty && rowData[t.textProperty] !== undefined) {
set_self(sid, 7, rowData[t.textProperty], 0, 0);
}
if (t.frameProperty && rowData[t.frameProperty] !== undefined) {
set_self(sid, 43, rowData[t.frameProperty], 0, 0);
}
}
}
}
this._isRendered = true;
};
/**
* 创建行上下文对象(传给 renderRow 回调)
* @param {number} rowIndex - 行索引
* @param {Object} rowData - 行数据
* @param {number} rowBaseY - 行基准 Y 坐标
* @returns {Object} 行上下文
* @private
*/
DynamicSpriteList.prototype._makeRowContext = function (rowIndex, rowData, rowBaseY) {
var self = this;
return {
rowIndex: rowIndex,
rowData: rowData,
rowBaseY: rowBaseY,
containerId: this.containerId,
addSprite: function (name, templateSpriteId, x, y) {
return self._addSpriteInternal(name, templateSpriteId, x, rowBaseY + y, rowIndex, true);
}
};
};
/**
* 处理边界回弹
* @private
*/
DynamicSpriteList.prototype._handleBounce = function() {
var currentY = get_self(this.containerId, 19, 0, 0, 0);
var contentHeight = get_self(this.containerId, 21, 0, 0, 0);
var clipY = this.clipArea.y;
var clipH = this.clipArea.height;
var targetY = currentY;
var needBounce = false;
if (contentHeight <= clipH) {
// 内容不足一屏,回弹到顶部
if (currentY !== clipY) {
targetY = clipY;
needBounce = true;
}
} else {
if (currentY > clipY) {
// 超出顶部
targetY = clipY;
needBounce = true;
} else {
var minY = clipY + clipH - contentHeight;
if (currentY < minY) {
// 超出底部
targetY = minY;
needBounce = true;
}
}
}
if (needBounce) {
play_ani(1, this.containerId, 19, currentY, targetY, 0,
Math.abs(targetY - currentY), 0, 0, 0, this.bounceSpeed, 0, 0, 0);
}
};
/**
* 处理点击事件
* @param {number} x - 点击X坐标
* @param {number} y - 点击Y坐标
* @private
*/
DynamicSpriteList.prototype._handleClick = function(x, y) {
var clickedTag = ifast_check_add(this.containerId, x, y);
if (clickedTag === -99999999) { return; }
var rec = this._tagIndex[clickedTag];
if (!rec || rec.clickable === false) { return; }
if (typeof this.onClick === 'function' && rec.rowIndex < this._data.length) {
this.onClick(rec.name, rec.rowIndex, this._data[rec.rowIndex]);
}
};
// ============================================================================
// 静态工具方法
// ============================================================================
/**
* 创建简单列表的快捷方法
*
* @static
* @param {Object} options - 配置选项
* @param {number} options.containerId - 容器精灵ID
* @param {number} options.itemTemplateId - 列表项模板精灵ID
* @param {Object} options.clipArea - 裁剪区域
* @param {number} options.rowHeight - 行高
* @returns {DynamicSpriteList} 列表实例
*
* @example
* var simpleList = DynamicSpriteList.createSimple({
* containerId: 100,
* itemTemplateId: 101,
* clipArea: { x: 50, y: 100, width: 600, height: 400 },
* rowHeight: 60
* });
*/
DynamicSpriteList.createSimple = function(options) {
return new DynamicSpriteList({
containerId: options.containerId,
clipArea: options.clipArea,
rowHeight: options.rowHeight,
templates: {
item: {
spriteId: options.itemTemplateId,
offset: { x: 0, y: 0 }
}
}
});
};
// ============================================================================
// 导出
// ============================================================================
// 兼容多种模块系统
if (typeof module !== 'undefined' && module.exports) {
module.exports = DynamicSpriteList;
} else if (typeof window !== 'undefined') {
window.DynamicSpriteList = DynamicSpriteList;
}