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

如何在HTML <img>标签上方用JS显示吸附网格并添加可吸附对象

解决地图上方网格不显示及巴士对象嵌入问题

我仔细看了你的测试代码,发现几个核心问题导致网格无法正常显示,同时帮你梳理下巴士对象的实现细节:

1. 错误引入JS库

你的代码里用的是fabric.Canvas,但头部引入的是kinetic.js——这两个是完全不同的图形库,Fabric.js的API无法在Kinetic环境下运行,这是网格不显示的首要原因。

你需要替换成Fabric.js的CDN链接,比如在<head>里添加:

<script src="https://cdn.jsdelivr.net/npm/fabric@5.3.0/dist/fabric.min.js"></script>

2. 网格绘制范围过小

你的循环只执行了600 / grid次(也就是24次),但你的canvas尺寸是1920x1080,这导致网格只在左上角一小块区域绘制,超出600的部分完全没有网格,看起来就像没显示一样。

应该根据canvas的实际宽高来计算循环次数:

// 基于canvas实际宽高计算网格数量
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;

for (var i = 0; i <= canvasWidth / grid; i++) {
  canvas.add(new fabric.Line([i * grid, 0, i * grid, canvasHeight], { stroke: '#ccc', selectable: false }));
}
for (var i = 0; i <= canvasHeight / grid; i++) {
  canvas.add(new fabric.Line([0, i * grid, canvasWidth, i * grid], { stroke: '#ccc', selectable: false }));
}

分开处理横竖线会更清晰,同时用<=确保最后一条线刚好覆盖canvas边缘。

3. 巴士图片的加载与优化

你的Fabric.js图片加载逻辑是对的,但可以补充一些细节,比如确保图片加载失败时有提示,同时保持吸附网格的功能正常:

const url = 'https://oege.ie.hva.nl/~delawai/assets/images/Photos/bus_geel.png';
fabric.Image.fromURL(url, (img) => {
  // 按比例缩放到指定尺寸(保持宽高比)
  const targetSize = 50;
  const scaleFactor = Math.min(targetSize / img.width, targetSize / img.height);
  img.scale(scaleFactor);
  // 初始位置对齐网格
  img.set({
    left: Math.round(50 / grid) * grid,
    top: Math.round(100 / grid) * grid
  });
  canvas.add(img);
}, {
  centeredRotation: true, // 保持旋转中心在对象中心
  selectable: true // 允许拖拽
});

完整修正后的代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bus Route Map</title>
<!-- 引入正确的Fabric.js库 -->
<script src="https://cdn.jsdelivr.net/npm/fabric@5.3.0/dist/fabric.min.js"></script>
<style>
.container { 
  position: relative; 
  width: 1920px; 
  height: 1080px;
  overflow: auto; /* 如果地图太大,允许滚动 */
}
canvas { 
  position: absolute; 
  z-index: 1; 
  top: 0; 
  left: 0; 
}
img { 
  position: absolute; 
  top: 0; 
  left: 0; 
  z-index: 0; /* 明确设置地图在底层 */
}
</style>
</head>
<body>
<div class="container">
<canvas id="c" width="1920" height="1080"></canvas>
<img height="1080" width="1920" src="https://oege.ie.hva.nl/~delawai/assets/images/Photos/map.jpg">
</div>
</body>
<script>
var canvas = new fabric.Canvas('c', { selection: false });
var grid = 25;

// 绘制铺满整个canvas的网格
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;

// 绘制竖线
for (var i = 0; i <= canvasWidth / grid; i++) {
  canvas.add(new fabric.Line([i * grid, 0, i * grid, canvasHeight], { 
    stroke: '#ccc', 
    selectable: false,
    evented: false // 避免网格干扰对象拖拽
  }));
}
// 绘制横线
for (var i = 0; i <= canvasHeight / grid; i++) {
  canvas.add(new fabric.Line([0, i * grid, canvasWidth, i * grid], { 
    stroke: '#ccc', 
    selectable: false,
    evented: false
  }));
}

// 添加巴士图片并对齐网格
const url = 'https://oege.ie.hva.nl/~delawai/assets/images/Photos/bus_geel.png';
fabric.Image.fromURL(url, (img) => {
  const targetSize = 50;
  const scaleFactor = Math.min(targetSize / img.width, targetSize / img.height);
  img.scale(scaleFactor);
  img.set({
    left: Math.round(50 / grid) * grid,
    top: Math.round(100 / grid) * grid
  });
  canvas.add(img);
}, {
  centeredRotation: true,
  selectable: true
});

// 保持对象吸附网格的功能
canvas.on('object:moving', function(options) {
  options.target.set({
    left: Math.round(options.target.left / grid) * grid,
    top: Math.round(options.target.top / grid) * grid
  });
});
</script>
</html>

额外提示

  • 如果你需要让巴士沿固定路线移动,而不是自由拖拽,可以预先定义路线的网格坐标点数组,然后用fabric.util.animate实现自动移动,比如:
// 示例:巴士沿预设网格点移动
const routePoints = [
  {x: 50, y:100},
  {x: 100, y:100},
  {x: 100, y:200},
  // 更多路线点...
];
let currentPointIndex = 0;

function moveBusToNextPoint() {
  const bus = canvas.getObjects().find(obj => obj.type === 'image');
  if (!bus || currentPointIndex >= routePoints.length) return;
  
  const targetPoint = routePoints[currentPointIndex];
  fabric.util.animate(bus, {
    left: Math.round(targetPoint.x / grid) * grid,
    top: Math.round(targetPoint.y / grid) * grid
  }, {
    duration: 1000, // 移动时间(毫秒)
    onChange: canvas.renderAll.bind(canvas),
    onComplete: () => {
      currentPointIndex++;
      moveBusToNextPoint(); // 自动移动到下一个点
    }
  });
}

// 启动自动移动(可以在巴士加载完成后调用)
// moveBusToNextPoint();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:54:58