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

如何在2D Canvas中仅绘制图像的非透明瓦片区域?

解决Canvas瓦片地图只渲染非透明瓦片的问题

核心思路

先检测每个裁剪出的瓦片是否包含非透明像素,只保留有效瓦片,避免渲染全透明的无用瓦片,最终仅保留你需要的4个有效图像,以此提升渲染性能。

具体实现方法

1. 编写瓦片透明度检测函数

创建一个辅助函数,用来判断单个瓦片是否包含非透明内容:

async function isTileNonTransparent(tileBmp) {
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = tileBmp.width;
    tempCanvas.height = tileBmp.height;
    const tempCtx = tempCanvas.getContext('2d');
    
    tempCtx.drawImage(tileBmp, 0, 0);
    const imageData = tempCtx.getImageData(0, 0, tileBmp.width, tileBmp.height);
    const data = imageData.data;
    
    // 遍历像素数据,检查是否有alpha值大于0的像素
    for (let i = 3; i < data.length; i += 4) {
        if (data[i] > 0) {
            return true;
        }
    }
    return false;
}

2. 修改原代码,只保留有效瓦片

在生成每个瓦片后,先调用检测函数,只有非透明的瓦片才加入数组:

(async () => {
    const img = new Image();
    img.src = "./img/obstacles.png";
    await img.decode();

    let tiles = [];
    let tileWidth = 32;
    let tileHeight = 32;

    for (let i = 0; i < 100; i++) {
        let x = i % 10;
        let y = Math.floor(i / 10);

        let bmp = await createImageBitmap(img, x * tileWidth, y * tileHeight, tileWidth, tileHeight);
        
        // 检测瓦片是否非透明,是则加入数组
        if (await isTileNonTransparent(bmp)) {
            tiles.push({
                bmp,
                x,
                y
            });
        } else {
            // 释放透明瓦片的内存
            bmp.close();
        }
    }

    console.log(tiles); // 现在只会输出4个有效瓦片

    const canvas = document.querySelector("canvas");
    canvas.width = 320;
    canvas.height = 320;

    const ctx = canvas.getContext("2d");

    function draw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        tiles.forEach((tile) => {
            ctx.drawImage(tile.bmp, tile.x * tileWidth, tile.y * tileHeight);
        });
    }
    draw();

})();

性能优化(可选)

上面的方法每个瓦片都要创建临时canvas,如果想进一步提升效率,可以一次性获取整个大图的像素数据,批量检测所有瓦片:

(async () => {
    const img = new Image();
    img.src = "./img/obstacles.png";
    await img.decode();

    let tiles = [];
    let tileWidth = 32;
    let tileHeight = 32;
    const totalWidth = img.width;
    const totalHeight = img.height;

    // 创建临时canvas获取整个大图的像素数据
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = totalWidth;
    tempCanvas.height = totalHeight;
    const tempCtx = tempCanvas.getContext('2d');
    tempCtx.drawImage(img, 0, 0);
    const fullImageData = tempCtx.getImageData(0, 0, totalWidth, totalHeight);
    const data = fullImageData.data;

    for (let i = 0; i < 100; i++) {
        let tileX = i % 10;
        let tileY = Math.floor(i / 10);
        let startX = tileX * tileWidth;
        let startY = tileY * tileHeight;
        let isNonTransparent = false;

        // 遍历当前瓦片区域的所有像素
        for (let y = startY; y < startY + tileHeight; y++) {
            for (let x = startX; x < startX + tileWidth; x++) {
                const index = (y * totalWidth + x) * 4;
                if (data[index + 3] > 0) {
                    isNonTransparent = true;
                    // 找到非透明像素就跳出所有循环
                    y = startY + tileHeight;
                    x = startX + tileWidth;
                }
            }
        }

        if (isNonTransparent) {
            let bmp = await createImageBitmap(img, startX, startY, tileWidth, tileHeight);
            tiles.push({
                bmp,
                x: tileX,
                y: tileY
            });
        }
    }

    console.log(tiles);

    const canvas = document.querySelector("canvas");
    canvas.width = 320;
    canvas.height = 320;

    const ctx = canvas.getContext("2d");

    function draw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        tiles.forEach((tile) => {
            ctx.drawImage(tile.bmp, tile.x * tileWidth, tile.y * tileHeight);
        });
    }
    draw();

})();

这种方法只创建一次临时canvas,减少了重复创建DOM元素的开销,对于大量瓦片的场景更高效。

内容的提问来源于stack exchange,提问作者anathrax

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 00:12:37