如何在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
相关产品推荐
相关产品推荐

