Canvas无法从Web Worker接收ImageBitmap的问题排查
问题:Web Worker渲染OffscreenCanvas后显示黑图
我在HTML5/TypeScript游戏项目中,尝试用Web Worker将2D世界的区块渲染到OffscreenCanvas上。由于手机等低端设备上渲染可能耗时数秒,必须异步执行才能避免应用失去响应。
将渲染逻辑与Worker代码分离后,不使用Worker时输出完全正常,但使用Worker后只显示黑图,而且玩家(绿色方块)还被渲染在图像后方,这和代码逻辑不符。检查发现Worker生成的ImageBitmap确实存在且内容正确,怀疑是跨源问题?
效果对比
- 未使用Web Worker:蓝色区块正常显示,红色边框标注区块位置,绿色玩家方块在区块上方
- 使用Web Worker:区块显示为黑色,绿色玩家方块出现在黑色区块后方
Worker初始化代码
const renderChunkAsync: Function<ChunkRenderData, Supplier<Optional<ImageBitmap>>> = (renderData) => { const worker: Worker = new Worker(new URL('./chunkDrawerWorker.ts', import.meta.url)) let drawingResult: Optional<ImageBitmap> = null worker.onmessage = (event: MessageEvent<ImageBitmap>) => { console.log('Worker done with ', event.data) drawingResult = event.data worker.terminate() } worker.postMessage(renderData) // const drawingResult = drawChunk(renderData) return () => drawingResult }
Worker脚本代码
const messageHandler: Consumer<MessageEvent<ChunkRenderData>> = ({ data }) => { const result = drawChunk(data) console.log('Worker done with ', result) self.postMessage(result, { transfer: [result] }) } self.onmessage = messageHandler
实际渲染代码
export const drawChunk: Function<ChunkRenderData, ImageBitmap> = ({ tileData, imageSize, tileSize }) => { const tilesPerLine = Math.round(imageSize / tileSize) const canvas = new OffscreenCanvas(imageSize, imageSize) const ctx = canvas.getContext('2d') const color = 'blue' ctx.fillStyle = color ctx.fillRect(0, 0, imageSize, imageSize) return canvas.transferToImageBitmap() }
渲染主逻辑代码
const renderer: Function<RenderOptions, Procedure> = ({ canvas, chunkController }) => () => { const chunkSize = config.tileSizePx * CHUNK_DEF.SIZE const { width, height } = canvas.getBoundingClientRect() canvas.width = width canvas.height = height const ctx = canvas.getContext('2d') ctx.clearRect(0, 0, width, height) const tileSize: number = config.tileSizePx const offsetScreen = calculateScreenOffset(width, height, playerPos) const chunkPlayer = chunkController.getChunkForPos(playerPos) const chunkPosScreen = offsetScreen(chunkPlayer.worldPosition) ctx.strokeStyle = 'red' ctx.strokeRect(chunkPosScreen.x, chunkPosScreen.y, chunkSize, chunkSize) const chunkImg = chunkPlayer.getCanvas() chunkImg && ctx.drawImage(chunkImg, chunkPosScreen.x, chunkPosScreen.y, chunkSize, chunkSize) const ps = offsetScreen(playerPos) ctx.fillStyle = 'green' ctx.fillRect(ps.x, ps.y, tileSize, tileSize) }
内容的提问来源于stack exchange,提问作者TwoThe
相关产品推荐
相关产品推荐

