如何让Fabric.js中的对齐参考线始终显示在所有对象之上
Fabric.js 对齐参考线置顶显示问题解决
你的问题核心是Fabric.js中选中的对象会在主画布的渲染流程中被优先绘制在普通对象之上,直接添加到主画布的参考线会被选中对象遮挡。解决这个问题最直接的方式是利用Fabric内置的overlayCanvas——它是一个独立于主画布的顶层画布,所有绘制在上面的内容都会显示在主画布所有对象(包括选中对象)的上方。
修改步骤:
- 清空覆盖层画布:每次绘制参考线前,先清空overlayCanvas的内容,避免残留旧的参考线。
- 直接在overlayCanvas绘制参考线:不再创建
fabric.Line对象添加到主画布,改用原生Canvas API在overlayCanvas的上下文中绘制线条和标记点。
修改后的代码片段:
const drawGuides = (obj) => { const targets = canvas.getObjects().filter((o) => o.type !== "line" && o !== obj); const ctx = canvas.overlayCanvas.getContext('2d'); // 清空覆盖层画布 ctx.clearRect(0, 0, canvas.width, canvas.height); const objLeft = obj.left; const objRight = obj.left + Math.round(obj.getScaledWidth()); const objCenterX = (objLeft + objRight) / 2.0; const objTop = obj.top; const objBottom = obj.top + Math.round(obj.getScaledHeight()); const objCenterY = (objTop + objBottom) / 2.0; // 定义绘制样式 ctx.strokeStyle = 'blue'; ctx.lineWidth = 1; const markSize = 5; sides.forEach(side => { let value; let pointArray = []; switch (side) { case "top": value = objTop; pointArray = [objLeft, objRight, objCenterX]; break; case "bottom": value = objBottom; pointArray = [objLeft, objRight, objCenterX]; break; case "centerY": value = objCenterY; pointArray = [objLeft, objRight, objCenterX]; break; case "left": value = objLeft; pointArray = [objTop, objBottom, objCenterY]; break; case "right": value = objRight; pointArray = [objTop, objBottom, objCenterY]; break; case "centerX": value = objCenterX; pointArray = [objTop, objBottom, objCenterY]; break; } for (const target of targets) { const targetLeft = target.left; const targetRight = target.left + Math.round(target.getScaledWidth()); const targetCenterX = (targetLeft + targetRight) / 2.0; const targetTop = target.top; const targetBottom = target.top + Math.round(target.getScaledHeight()); const targetCenterY = (targetTop + targetBottom) / 2.0; switch (side) { case "top": case "bottom": case "centerY": if (inRange(value, targetTop) || inRange(value, targetBottom) || inRange(value, targetCenterY)) { pointArray.push(targetLeft); pointArray.push(targetRight); pointArray.push(targetCenterX); } break; case "left": case "right": case "centerX": if (inRange(value, targetLeft) || inRange(value, targetRight) || inRange(value, targetCenterX)) { pointArray.push(targetBottom); pointArray.push(targetTop); pointArray.push(targetCenterY); } break; } } if(pointArray.length <= 3 ) return; const sortedPointArray = pointArray.sort((a, b) => a - b); // 绘制参考线 switch (side) { case "top": case "bottom": case "centerY": // 绘制水平线 ctx.beginPath(); ctx.moveTo(sortedPointArray[0], value); ctx.lineTo(sortedPointArray[sortedPointArray.length - 1], value); ctx.stroke(); // 绘制交叉标记 sortedPointArray.forEach(point => { ctx.beginPath(); ctx.moveTo(point - markSize, value - markSize); ctx.lineTo(point + markSize, value + markSize); ctx.stroke(); ctx.beginPath(); ctx.moveTo(point - markSize, value + markSize); ctx.lineTo(point + markSize, value - markSize); ctx.stroke(); }); break; case "left": case "right": case "centerX": // 绘制垂直线 ctx.beginPath(); ctx.moveTo(value, sortedPointArray[0]); ctx.lineTo(value, sortedPointArray[sortedPointArray.length - 1]); ctx.stroke(); // 绘制交叉标记 sortedPointArray.forEach(point => { ctx.beginPath(); ctx.moveTo(value - markSize, point - markSize); ctx.lineTo(value + markSize, point + markSize); ctx.stroke(); ctx.beginPath(); ctx.moveTo(value - markSize, point + markSize); ctx.lineTo(value + markSize, point - markSize); ctx.stroke(); }); break; } }); }
关键说明:
- 移除了原来创建
fabric.Line并添加到主画布的逻辑,改用overlayCanvas的上下文直接绘制,确保内容始终在顶层。 - 每次绘制前清空
overlayCanvas,避免残留之前的参考线。 - 不需要再维护
obj.guides和obj.guidePoints,因为参考线不再是Fabric对象,而是直接绘制在覆盖层上。
内容的提问来源于stack exchange,提问作者Youth Dream
相关产品推荐
相关产品推荐

