优化调整辅助线等

This commit is contained in:
24kycj
2025-12-10 01:40:03 +08:00
parent 378593047d
commit 026d564c92
8 changed files with 1591 additions and 965 deletions
+78 -20
View File
@@ -2305,6 +2305,11 @@ function initCanvasEvents() {
let idx = getIndex(event.target);
if (idx <= 0) return;
let objData = objs[idx - 1];
// 安全检查:确保 objData 存在且有 type 属性
if (!objData || typeof objData.type === 'undefined') {
console.warn('objData is undefined or missing type at index', idx - 1);
return;
}
let target = event.target;
if ([3, 4, 10].includes(objData.type)) {
@@ -2327,10 +2332,10 @@ function initCanvasEvents() {
// 8. 【完美修复版】渲染后绘制:外部蒙版 + 中心圆孔 (修复三角形Bug)
// ==================================================
currentCanvas.on('after:render', function () {
const ctx = currentCanvas.getContext();
let bgObj = (currentCanvas === canvas1) ? background_image1 : background_image2;
if (bgObj) {
const ctx = currentCanvas.getContext();
const width = currentCanvas.width;
const height = currentCanvas.height;
@@ -2378,6 +2383,32 @@ function initCanvasEvents() {
ctx.fill();
ctx.restore();
}
// 在遮罩之后重新绘制辅助线,确保辅助线在最上层
const objects = currentCanvas.getObjects();
objects.forEach(obj => {
if (obj.isGuideLine) {
ctx.save();
// 应用对象的变换矩阵
const m = obj.calcTransformMatrix();
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
// 设置透明度
ctx.globalAlpha = obj.opacity || 1;
// 应用阴影
if (obj.shadow) {
ctx.shadowColor = obj.shadow.color;
ctx.shadowBlur = obj.shadow.blur;
ctx.shadowOffsetX = obj.shadow.offsetX;
ctx.shadowOffsetY = obj.shadow.offsetY;
}
// 使用 Fabric 对象的内置渲染方法
obj._render(ctx);
ctx.restore();
}
});
});
});
}
@@ -2469,30 +2500,54 @@ function initRulers() {
// --- 2. 在画布中创建辅助线 ---
function createFabricGuide(type, domX, domY) {
// 计算画布内的逻辑坐标 (减去 margin 偏移,除以缩放)
let boxOffset = canvasBox.offset();
let wrapperOffset = canvasWrapper.offset();
// 计算相对于 canvasBox 的像素位置
let relX = domX - (boxOffset.left - wrapperOffset.left);
let relY = domY - (boxOffset.top - wrapperOffset.top);
// domX 和 domY 是相对于 canvas-wrapper 的坐标
// 需要转换为相对于 canvas_box 的坐标,然后再转换为 Fabric 画布坐标
// 获取 canvas_box 相对于 canvas-wrapper 的位置
let boxPosition = canvasBox.position();
let boxMarginLeft = parseFloat(canvasBox.css('margin-left')) || 0;
let boxMarginTop = parseFloat(canvasBox.css('margin-top')) || 0;
// 计算相对于 canvas_box 的坐标(减去 box 在 wrapper 中的位置和 margin
let relX = domX - boxPosition.left - boxMarginLeft;
let relY = domY - boxPosition.top - boxMarginTop;
// 转为 Fabric 坐标 (除以 zoom)
let canvasX = relX / zoom;
let canvasY = relY / zoom;
// 构造线对象
// 获取画布的实际尺寸和可见区域尺寸
let canvasWidth = canvas.width || 10000;
let canvasHeight = canvas.height || 10000;
// 获取可见区域的尺寸(考虑缩放)
let visibleWidth = canvasWrapper.width() || canvasWidth;
let visibleHeight = canvasWrapper.height() || canvasHeight;
// 使用足够大的固定值,确保辅助线能够覆盖整个画布和可见区域
// 使用 100000 确保无论画布多大、如何缩放,都能覆盖整个可见区域
let lineExtent = 100000;
// 构造线对象,使用足够大的范围确保覆盖整个画布
// 线的起点和终点要足够远,确保覆盖整个可见区域
let linePoints = [];
if (type === 'horizontal') {
linePoints = [-5000, canvasY, 5000, canvasY];
// 水平线:从很远的左边到很远的右边
linePoints = [-lineExtent, canvasY, lineExtent, canvasY];
} else {
linePoints = [canvasX, -5000, canvasX, 5000];
// 垂直线:从很远的上面到很远的下面
linePoints = [canvasX, -lineExtent, canvasX, lineExtent];
}
let guideLine = new fabric.Line(linePoints, {
stroke: '#00FFFF',
stroke: '#0066FF',
strokeWidth: 1 / zoom,
strokeDashArray: [5, 5],
opacity: 0.9,
shadow: {
color: '#0066FF',
blur: 6,
offsetX: 0,
offsetY: 0,
affectStroke: true
},
selectable: true,
evented: true,
hasControls: false,
@@ -2501,6 +2556,9 @@ function initRulers() {
lockScalingX: true,
lockScalingY: true,
hoverCursor: type === 'horizontal' ? 'ns-resize' : 'ew-resize',
perPixelTargetFind: false, // 使用边界框检测而不是像素检测
targetFindTolerance: 10, // 增加点击容差,扩大可点击区域(像素)
padding: 10, // 增加选择边界的内边距
isGuideLine: true,
ignoreSave: true
});
@@ -2508,9 +2566,9 @@ function initRulers() {
// 限制移动方向
guideLine.on('moving', function (e) {
if (type === 'horizontal') {
this.left = -5000;
this.left = -lineExtent;
} else {
this.top = -5000;
this.top = -lineExtent;
}
});
@@ -2558,14 +2616,14 @@ function initRulers() {
function drawHorizontal() {
ctxH.clearRect(0, 0, currentCanvasWidth, rulerHeight);
ctxH.strokeStyle = '#999';
ctxH.strokeStyle = '#666';
ctxH.lineWidth = 1;
ctxH.beginPath();
ctxH.moveTo(0, 19);
ctxH.lineTo(currentCanvasWidth, 19);
ctxH.stroke();
ctxH.fillStyle = '#CCC';
ctxH.fillStyle = '#888';
ctxH.font = '9px Arial';
ctxH.textAlign = 'left';
ctxH.textBaseline = 'top';
@@ -2626,14 +2684,14 @@ function initRulers() {
function drawVertical() {
ctxV.clearRect(0, 0, rulerWidth, currentCanvasHeight);
ctxV.strokeStyle = '#999';
ctxV.strokeStyle = '#666';
ctxV.lineWidth = 1;
ctxV.beginPath();
ctxV.moveTo(19, 0);
ctxV.lineTo(19, currentCanvasHeight);
ctxV.stroke();
ctxV.fillStyle = '#CCC';
ctxV.fillStyle = '#888';
ctxV.font = '9px Arial';
ctxV.textAlign = 'center';
ctxV.textBaseline = 'middle';
+103 -5
View File
@@ -14,7 +14,8 @@ function display_func(img1, img2, img3) {
$("#component_type").text(language_str("bg"));//"背景"
canvas1.discardActiveObject().renderAll();
canvas2.discardActiveObject().renderAll();
let _objs1 = canvas1.getObjects();
// 过滤掉辅助线
let _objs1 = canvas1.getObjects().filter(obj => !obj.isGuideLine);
let g1 = [];
let g3 = [];
let g5 = [];
@@ -24,9 +25,16 @@ function display_func(img1, img2, img3) {
let black_top = 0;//黑色顶裁剪
let all_left = 0;//预览图左裁剪
let all_top = 0;//预览图顶裁剪
let list1 = [..._objs1]
// 过滤掉辅助线
let list1 = _objs1.filter(obj => !obj.isGuideLine);
for (let item of list1) {
let idx = getIndex(item, canvas1);
// 安全检查:确保 objs1[idx - 1] 存在且有 type 属性
if (idx != 0 && (!objs1[idx - 1] || typeof objs1[idx - 1].type === 'undefined')) {
continue;
}
//console.log("------");
//console.log(item.getCoords());
//console.log(getCoordsMinX(background_image.getCoords()));
@@ -149,7 +157,8 @@ function display_func(img1, img2, img3) {
top: black_top,
left: black_left
});
let _objs2 = canvas2.getObjects();
// 过滤掉辅助线
let _objs2 = canvas2.getObjects().filter(obj => !obj.isGuideLine);
let g2 = [];
let g4 = [];
let g6 = [];
@@ -160,7 +169,14 @@ function display_func(img1, img2, img3) {
all_top = 0;
all_left = 0;
for (let item of _objs2) {
let idx = getIndex(item, canvas2);
// 安全检查:确保 objs2[idx - 1] 存在且有 type 属性
if (idx != 0 && (!objs2[idx - 1] || typeof objs2[idx - 1].type === 'undefined')) {
continue;
}
if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > all_left) {
all_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords()));
}
@@ -362,7 +378,9 @@ function output(callback = null, _save = save) {
let black_top = 0;//黑色顶裁剪
let all_left = 0;//预览图左裁剪
let all_top = 0;//预览图顶裁剪
for (let item of _objs1) {
// 过滤掉辅助线
let filteredObjs1 = _objs1.filter(obj => !obj.isGuideLine);
for (let item of filteredObjs1) {
let idx = getIndex(item, canvas1);
if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > all_left) {
@@ -372,6 +390,12 @@ function output(callback = null, _save = save) {
all_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords()));
}
g3.push(fabric.util.object.clone(item));
// 安全检查:确保 objs1[idx - 1] 存在且有 type 属性
if (idx != 0 && (!objs1[idx - 1] || typeof objs1[idx - 1].type === 'undefined')) {
continue;
}
if (idx != 0 && (objs1[idx - 1].type == 10 || objs1[idx - 1].type == 4)) {
continue;//计数器 或 合成文字
}
@@ -485,8 +509,16 @@ function output(callback = null, _save = save) {
black_top = 0;//黑色顶裁剪
all_left = 0;//黑色左裁剪
all_top = 0;//黑色顶裁剪
for (let item of _objs2) {
// 过滤掉辅助线
let filteredObjs2 = _objs2.filter(obj => !obj.isGuideLine);
for (let item of filteredObjs2) {
let idx = getIndex(item, canvas2);
// 安全检查:确保 objs2[idx - 1] 存在且有 type 属性
if (idx != 0 && (!objs2[idx - 1] || typeof objs2[idx - 1].type === 'undefined')) {
continue;
}
if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > all_left) {
all_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords()));
}
@@ -635,8 +667,19 @@ function output(callback = null, _save = save) {
op.frontBlackPic = '';
op.backBlackPic = '';
for (let item of _objs1) {
// 跳过辅助线
if (item.isGuideLine) {
continue;
}
let idx = getIndex(item, canvas1);
if (idx == 0) continue;//background-image
// 安全检查:确保 objs1[idx - 1] 存在且有 type 属性
if (!objs1[idx - 1] || typeof objs1[idx - 1].type === 'undefined') {
continue;
}
let type = objs1[idx - 1].type;
if (type == 2) {
//out_pic
@@ -750,8 +793,19 @@ function output(callback = null, _save = save) {
}
//obj2
for (let item of _objs2) {
// 跳过辅助线
if (item.isGuideLine) {
continue;
}
let idx = getIndex(item, canvas2);
if (idx == 0) continue;//background-image
// 安全检查:确保 objs2[idx - 1] 存在且有 type 属性
if (!objs2[idx - 1] || typeof objs2[idx - 1].type === 'undefined') {
continue;
}
let type = objs2[idx - 1].type;
if (type == 2) {
//out_pic
@@ -941,9 +995,31 @@ function cleanupSrcFields(jsonObjects, objsArray) {
}
function saveAs(op1, callback) {
// 临时移除辅助线,保存后再恢复
let guideLines1 = [];
let guideLines2 = [];
canvas1.getObjects().forEach((obj, index) => {
if (obj.isGuideLine) {
guideLines1.push(obj);
canvas1.remove(obj);
}
});
canvas2.getObjects().forEach((obj, index) => {
if (obj.isGuideLine) {
guideLines2.push(obj);
canvas2.remove(obj);
}
});
let j = canvas1.toJSON(["selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline"]);
let b = canvas2.toJSON(["selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline"]);
// 恢复辅助线
guideLines1.forEach(obj => canvas1.add(obj));
guideLines2.forEach(obj => canvas2.add(obj));
canvas1.renderAll();
canvas2.renderAll();
// 清理不需要的 src 字段
cleanupSrcFields(j.objects, objs1);
cleanupSrcFields(b.objects, objs2);
@@ -975,9 +1051,31 @@ function saveAs(op1, callback) {
}
function save(op1, callback) {
// 临时移除辅助线,保存后再恢复
let guideLines1 = [];
let guideLines2 = [];
canvas1.getObjects().forEach((obj, index) => {
if (obj.isGuideLine) {
guideLines1.push(obj);
canvas1.remove(obj);
}
});
canvas2.getObjects().forEach((obj, index) => {
if (obj.isGuideLine) {
guideLines2.push(obj);
canvas2.remove(obj);
}
});
let j = canvas1.toJSON(["selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline"]);
let b = canvas2.toJSON(["selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline"]);
// 恢复辅助线
guideLines1.forEach(obj => canvas1.add(obj));
guideLines2.forEach(obj => canvas2.add(obj));
canvas1.renderAll();
canvas2.renderAll();
// 清理不需要的 src 字段
cleanupSrcFields(j.objects, objs1);
cleanupSrcFields(b.objects, objs2);