createImageBitmap仅在页面重载后执行导致游戏崩溃如何修复
问题成因
- 根本原因是图片加载为异步操作,现有代码没有等待图片加载完成就执行了切图逻辑:
- 首次加载时,给
Image对象设置src后,网络请求图片的过程不会阻塞代码执行,调用loadSprites时所有图片尚未加载完成,naturalWidth属性为0,第二层循环条件k < Spritesheets[xxx].naturalWidth / spriteW结果为假,循环完全不会执行,自然没有生成任何createImageBitmap对应的Promise。 - 重载页面时图片已被浏览器缓存,设置
src后图片瞬间加载完成,执行loadSprites时naturalWidth已经是正确数值,循环可以正常执行生成Promise。你观测到的“函数调用前就已填充完成”是浏览器控制台的延迟求值特性导致的:打印对象时控制台仅存储引用,手动展开对象时才会读取最新的属性值,因此产生了提前填充的错觉。
- 首次加载时,给
修复方案
修复核心逻辑为:先等待所有雪碧图加载完成,再执行切图操作,最后等待所有切图Promise解析完成后再启动游戏。
修复后参考代码:
const Spritesheets = { tileset_grass : new Image(), tileset : new Image(), player : new Image(), crawler : new Image(), creature : new Image(), items : new Image(), particles : new Image() }; const Images = { tileset_grass : { spriteW : 16, spriteH : 18 }, tileset : { spriteW : 16, spriteH : 16 }, player : { spriteW : 16, spriteH : 16 }, crawler : { spriteW : 16, spriteH : 16 }, creature : { spriteW : 16, spriteH : 16 }, items : { spriteW : 16, spriteH : 16 }, particles : { spriteW : 16, spriteH : 16 }, }; // 1. 封装所有雪碧图的加载Promise const spritesheetLoadPromises = Object.entries(Spritesheets).map(([key, img]) => { return new Promise((resolve, reject) => { img.onload = () => resolve(key); img.onerror = (err) => reject(new Error(`加载${key}失败: ${err}`)); img.src = `images/${key}.png`; // 统一设置src避免漏写 }); }); // 2. 等待所有雪碧图加载完成后再切图 async function loadAllResources() { try { await Promise.all(spritesheetLoadPromises); // 3. 执行切图逻辑,收集所有createImageBitmap的Promise const bitmapPromises = []; Object.entries(Images).forEach(([sheetKey, sheetConfig]) => { const img = Spritesheets[sheetKey]; const totalSprites = img.naturalWidth / sheetConfig.spriteW; for(let k = 0; k < totalSprites; k++) { const bitmapPromise = createImageBitmap( img, k * sheetConfig.spriteW, 0, sheetConfig.spriteW, sheetConfig.spriteH ); bitmapPromises.push(bitmapPromise); // 若需要直接存解析后的位图,可在promise resolve后赋值 sheetConfig[k] = bitmapPromise; } }); // 4. 等待所有切图完成 await Promise.all(bitmapPromises); // 5. 所有资源加载完成,在这里调用游戏初始化逻辑 console.log('所有资源加载完成', Images); // initGame(); } catch (err) { // 资源加载失败的兜底处理 console.error('资源加载失败:', err); } } // 启动资源加载流程 loadAllResources();
内容的提问来源于stack exchange,提问作者Red Wizard
相关产品推荐
相关产品推荐

