You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:12:04