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

游戏城市地图Canvas drawImage层级设置求助:结构重叠无法高亮

解决Canvas游戏地图建筑层级重叠与高亮问题

嘿,我懂你遇到的麻烦了!Canvas 和 DOM 元素不一样,它没有内置的 z-index 属性来控制层级——所有元素的层级完全由你调用 drawImage 的顺序决定:后画的元素会覆盖先画的。你数组里的 z 字段设计得很好,但得先利用它调整绘制顺序,再解决图片异步加载的问题,这样层级和高亮就能正常工作了。

下面给你一步步的解决方案:

1. 先按 z 值排序建筑数组

首先把你的建筑数组按照 z 字段从小到大排序,这样 z 值小的建筑先绘制,z 值大的后绘制,自然就会叠在上方。注意要把 z 转成数字(因为你数组里存的是字符串),避免排序出错:

Canvas_Image_Arr.sort((a, b) => parseInt(a.z) - parseInt(b.z));

2. 解决图片异步加载的顺序问题

你原来的代码里,图片是各自异步加载的,加载完就立刻绘制,这会导致加载快的图片不管 z 值大小都会先画,打乱层级。所以最好等所有图片都加载完成后,再按排序好的顺序统一绘制。可以用 Promise 来管理加载流程:

修改后的完整代码

var Canvas_Image_Arr = [
  {"name":"NAME1","Image":"../structure1.png","x":"439","y":"47","z":"3","w":"175","h":"179"},
  {"name":"NAME2","Image":"../structure2.png","x":"554","y":"175","z":"2","w":"175","h":"179"}
];

window.onload = function(){
  var canvas = document.getElementById("ctymap");
  var ctx = canvas.getContext("2d");
  ctx.canvas.width = 942;
  ctx.canvas.height = 430;
  canvas.style.background = "url(../background.jpg)";

  // 第一步:按z值排序数组,确保层级正确
  Canvas_Image_Arr.sort((a, b) => parseInt(a.z) - parseInt(b.z));

  // 把每个图片加载任务包装成Promise
  const imageLoadPromises = Canvas_Image_Arr.map(element => {
    return new Promise((resolve) => {
      const img = new Image();
      img.src = element.Image;
      img.onload = () => {
        // 加载完成后返回图片和对应的建筑数据
        resolve({ img, data: element });
      };
    });
  });

  // 等所有图片加载完成后再绘制
  Promise.all(imageLoadPromises).then(loadedImages => {
    // 封装绘制所有建筑的函数,方便重复调用
    const drawAllBuildings = () => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      loadedImages.forEach(({ img, data }) => {
        ctx.drawImage(
          img,
          parseInt(data.x),
          parseInt(data.y),
          parseInt(data.w),
          parseInt(data.h)
        );
      });
    };

    // 初始绘制所有建筑
    drawAllBuildings();

    // 第二步:添加鼠标高亮逻辑
    canvas.addEventListener('mousemove', (e) => {
      // 获取鼠标在Canvas内的相对坐标
      const rect = canvas.getBoundingClientRect();
      const mouseX = e.clientX - rect.left;
      const mouseY = e.clientY - rect.top;

      // 先重新绘制所有建筑,清空之前的高亮
      drawAllBuildings();

      // 遍历建筑,判断鼠标是否hover并绘制高亮
      loadedImages.forEach(({ data }) => {
        const x = parseInt(data.x);
        const y = parseInt(data.y);
        const w = parseInt(data.w);
        const h = parseInt(data.h);
        
        if (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h) {
          // 绘制半透明红色高亮边框,确保在建筑上层
          ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';
          ctx.lineWidth = 3;
          ctx.strokeRect(x, y, w, h);
        }
      });
    });
  });
};

关键细节说明

  • 坐标转数字:你数组里的 x、y、w、h 都是字符串,直接用会导致绘制错误,所以要转成整数。
  • Canvas 即时模式:Canvas 没有“元素”的概念,所有内容都是画在画布上的像素,所以高亮需要每次鼠标移动时清空画布、重绘所有建筑,再在目标建筑上叠加高亮效果——这样高亮会自动处于最上层。
  • 层级保障:排序后再统一绘制,确保 z 值大的建筑最后画,不会被 z 值小的覆盖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 10:08:35