// design2核心功能模块 // 注意:此代码在layui.use回调函数内部执行 // 全局变量已在主入口文件中定义,这里只需要初始化 $('#canvas1').attr('height', document.body.clientHeight - 100) $('#canvas2').attr('height', document.body.clientHeight - 100) $('#object_attribute').css('height', '470px') $('#obj_list').css('height', document.body.clientHeight - 520) $('.obj_list').css('height', $('#obj_list').height() - 32) $('.main').show() $('#canvas1').attr('width', $('#canvas-div').width()) $('#canvas2').attr('width', $('#canvas-div').width()) // 初始化Canvas对象 // 优化性能:在创建 Fabric Canvas 之前,先设置 canvas context 的 willReadFrequently try { const canvas1El = document.getElementById('canvas1'); const canvas2El = document.getElementById('canvas2'); if (canvas1El) { canvas1El.getContext('2d', { willReadFrequently: true }); } if (canvas2El) { canvas2El.getContext('2d', { willReadFrequently: true }); } } catch (e) { // 如果浏览器不支持 willReadFrequently,忽略错误 } canvas1 = new fabric.Canvas('canvas1') canvas2 = new fabric.Canvas('canvas2') fabric.Object.prototype.objectCaching = false ctx1 = canvas1.getSelectionContext() ctx2 = canvas2.getSelectionContext() is_bgi_add = false canvas1.zoomToPoint(new fabric.Point(canvas1.width / 2, canvas1.height / 2), $('#canvas-div').width() / 1200 > 1 ? 1 : $('#canvas-div').width() / 1200) //console.log(canvas1.width / 2, canvas1.height / 2); //console.log($("#canvas-div").width() / 1200 > 1 ? 1 : $("#canvas-div").width() / 1200); canvas2.zoomToPoint(new fabric.Point(canvas2.width / 2, canvas2.height / 2), $('#canvas-div').width() / 1200 > 1 ? 1 : $('#canvas-div').width() / 1200) zoom = $('#canvas-div').width() / 1200 > 1 ? 1 : $('#canvas-div').width() / 1200 // 初始化标尺 function initRulers() { // dpi已在主入口文件中定义为全局变量 const mmToPx = dpi / 25.4; // 1mm = 11.811px (300 DPI) const rulerHeight = 20; const rulerWidth = 20; // 获取实际画布的像素尺寸(px) // design2.html 的实际画布尺寸是 1012px x 648px const actualCanvasWidth = canvas1.width || 1012; // 实际画布宽度(px) const actualCanvasHeight = canvas1.height || 648; // 实际画布高度(px) // 获取显示区域的尺寸(包括 margin) const canvasDiv = $('#canvas-div'); const canvasWrapper = $('.canvas-wrapper'); // 使用 canvas-wrapper 的宽度,如果没有则使用 canvas_div 的宽度 let canvasWidth = canvasWrapper.width(); let canvasHeight = canvasWrapper.height(); if (!canvasWidth || canvasWidth === 0) { const canvas1Width = $('#canvas1').width() || canvasDiv.width() - 20; canvasWidth = canvas1Width + 20; // 加上 margin-left: 20px } if (!canvasHeight || canvasHeight === 0) { const canvas1Height = $('#canvas1').height() || canvasDiv.height() - 20; canvasHeight = canvas1Height + 20; // 加上 margin-top: 20px } // 设置标尺尺寸(使用显示区域的尺寸) $('#ruler-h').attr('width', canvasWidth).attr('height', rulerHeight); $('#ruler-v').attr('width', rulerWidth).attr('height', canvasHeight); $('.ruler-horizontal').css('width', canvasWidth + 'px'); $('.ruler-vertical').css('height', canvasHeight + 'px'); // 绘制水平标尺 function drawHorizontalRuler() { const ctx = document.getElementById('ruler-h').getContext('2d', { willReadFrequently: true }); ctx.clearRect(0, 0, canvasWidth, rulerHeight); // 背景 ctx.fillStyle = '#2C2C2C'; ctx.fillRect(0, 0, canvasWidth, rulerHeight); // 边框 ctx.strokeStyle = '#1E1E1E'; ctx.lineWidth = 1; ctx.strokeRect(0, 0, canvasWidth, rulerHeight); ctx.beginPath(); ctx.moveTo(0, rulerHeight - 1); ctx.lineTo(canvasWidth, rulerHeight - 1); ctx.stroke(); // 刻度线 ctx.strokeStyle = '#888888'; ctx.fillStyle = '#CCCCCC'; ctx.font = '10px Arial'; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; // 计算:实际画布尺寸(px)对应的物理尺寸(mm),然后根据显示区域的缩放比例计算 // 实际画布物理宽度 = actualCanvasWidth / dpi * 25.4 (mm) // 显示区域宽度 = actualCanvasWidth * zoom (px) // 所以:1mm 在显示区域中 = (actualCanvasWidth * zoom) / (actualCanvasWidth / dpi * 25.4) = zoom * dpi / 25.4 const pxPerMm = zoom * mmToPx; // 在显示区域中,1mm对应的像素数 const marginOffset = 20; // canvas 的 margin-left: 20px // 标尺0点应该在background_image的左上角位置 // background_image在画布坐标系中的left值,需要转换到标尺坐标系 // 画布从标尺的marginOffset位置开始,所以:标尺0点 = marginOffset + background_image.left (如果存在) let originX = marginOffset; if (typeof background_image !== 'undefined' && background_image) { originX = marginOffset + background_image.left; } const startX = 0; const endX = canvasWidth; // 绘制主要刻度(每10mm) // 从0点向左绘制负值刻度 for (let mm = -10; originX + mm * pxPerMm >= startX; mm -= 10) { const x = originX + mm * pxPerMm; if (x < startX) break; ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, rulerHeight); ctx.stroke(); // 数字(负值) ctx.fillText(mm + 'mm', x + 2, 2); } // 从0点向右绘制正值刻度 for (let mm = 0; originX + mm * pxPerMm <= endX; mm += 10) { const x = originX + mm * pxPerMm; if (x > endX) break; ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, rulerHeight); ctx.stroke(); // 数字 ctx.fillText(mm + 'mm', x + 2, 2); } // 绘制次要刻度(每1mm) // 从0点向左绘制负值刻度 for (let mm = -1; originX + mm * pxPerMm >= startX; mm -= 1) { const x = originX + mm * pxPerMm; if (x < startX) break; if (mm % 10 === 0) continue; // 跳过主要刻度 ctx.beginPath(); ctx.moveTo(x, rulerHeight - 8); ctx.lineTo(x, rulerHeight); ctx.stroke(); } // 从0点向右绘制正值刻度 for (let mm = 0; originX + mm * pxPerMm <= endX; mm += 1) { const x = originX + mm * pxPerMm; if (x > endX) break; if (mm % 10 === 0) continue; // 跳过主要刻度 ctx.beginPath(); ctx.moveTo(x, rulerHeight - 8); ctx.lineTo(x, rulerHeight); ctx.stroke(); } } // 绘制垂直标尺 function drawVerticalRuler() { const ctx = document.getElementById('ruler-v').getContext('2d', { willReadFrequently: true }); ctx.clearRect(0, 0, rulerWidth, canvasHeight); // 背景 ctx.fillStyle = '#2C2C2C'; ctx.fillRect(0, 0, rulerWidth, canvasHeight); // 边框 ctx.strokeStyle = '#1E1E1E'; ctx.lineWidth = 1; ctx.strokeRect(0, 0, rulerWidth, canvasHeight); ctx.beginPath(); ctx.moveTo(rulerWidth - 1, 0); ctx.lineTo(rulerWidth - 1, canvasHeight); ctx.stroke(); // 刻度线 ctx.strokeStyle = '#888888'; ctx.fillStyle = '#CCCCCC'; ctx.font = '10px Arial'; ctx.textAlign = 'left'; ctx.textBaseline = 'top'; // 计算:实际画布尺寸(px)对应的物理尺寸(mm),然后根据显示区域的缩放比例计算 const pxPerMm = zoom * mmToPx; // 在显示区域中,1mm对应的像素数 const marginOffset = 20; // canvas 的 margin-top: 20px (但还要加上ruler的高度) const rulerHeightOffset = 20; // ruler 的高度 // 标尺0点应该在background_image的左上角位置 // background_image在画布坐标系中的top值,需要转换到标尺坐标系 // 画布从标尺的marginOffset + rulerHeightOffset位置开始,所以:标尺0点 = marginOffset + rulerHeightOffset + background_image.top (如果存在) let originY = marginOffset + rulerHeightOffset; if (typeof background_image !== 'undefined' && background_image) { originY = marginOffset + rulerHeightOffset + background_image.top; } const startY = 0; const endY = canvasHeight; // 绘制主要刻度(每10mm) // 从0点向上绘制负值刻度 for (let mm = -10; originY + mm * pxPerMm >= startY; mm -= 10) { const y = originY + mm * pxPerMm; if (y < startY) break; ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(rulerWidth, y); ctx.stroke(); // 数字(旋转90度,负值) ctx.save(); ctx.translate(2, y + 8); ctx.rotate(-Math.PI / 2); ctx.fillText(mm + 'mm', 0, 0); ctx.restore(); } // 从0点向下绘制正值刻度 for (let mm = 0; originY + mm * pxPerMm <= endY; mm += 10) { const y = originY + mm * pxPerMm; if (y > endY) break; ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(rulerWidth, y); ctx.stroke(); // 数字(旋转90度) ctx.save(); ctx.translate(2, y + 8); ctx.rotate(-Math.PI / 2); ctx.fillText(mm + 'mm', 0, 0); ctx.restore(); } // 绘制次要刻度(每1mm) // 从0点向上绘制负值刻度 for (let mm = -1; originY + mm * pxPerMm >= startY; mm -= 1) { const y = originY + mm * pxPerMm; if (y < startY) break; if (mm % 10 === 0) continue; // 跳过主要刻度 ctx.beginPath(); ctx.moveTo(rulerWidth - 8, y); ctx.lineTo(rulerWidth, y); ctx.stroke(); } // 从0点向下绘制正值刻度 for (let mm = 0; originY + mm * pxPerMm <= endY; mm += 1) { const y = originY + mm * pxPerMm; if (y > endY) break; if (mm % 10 === 0) continue; // 跳过主要刻度 ctx.beginPath(); ctx.moveTo(rulerWidth - 8, y); ctx.lineTo(rulerWidth, y); ctx.stroke(); } } // 初始绘制 drawHorizontalRuler(); drawVerticalRuler(); // 监听窗口大小变化 $(window).on('resize', function () { setTimeout(function () { const canvasWrapper = $('.canvas-wrapper'); let newWidth = canvasWrapper.width(); let newHeight = canvasWrapper.height(); if (!newWidth || newWidth === 0) { const canvas1Width = $('#canvas1').width() || $('#canvas-div').width() - 20; newWidth = canvas1Width + 20; // 加上 margin-left: 20px } if (!newHeight || newHeight === 0) { const canvas1Height = $('#canvas1').height() || $('#canvas-div').height() - 20; newHeight = canvas1Height + 20; // 加上 margin-top: 20px } if (newWidth !== canvasWidth || newHeight !== canvasHeight) { canvasWidth = newWidth; canvasHeight = newHeight; $('#ruler-h').attr('width', newWidth); $('#ruler-v').attr('height', newHeight); $('.ruler-horizontal').css('width', newWidth + 'px'); $('.ruler-vertical').css('height', newHeight + 'px'); drawHorizontalRuler(); drawVerticalRuler(); } }, 100); }); // 监听画布缩放变化 canvas1.on('after:render', function () { drawHorizontalRuler(); drawVerticalRuler(); }); canvas2.on('after:render', function () { drawHorizontalRuler(); drawVerticalRuler(); }); } initRulers(); $('#source_front').width(zoom * $('#source_front').width()) $('#source_back').width(zoom * $('#source_back').width()) $('.canvas-container:eq(1)').hide() canvas = canvas1 fabric.Object.prototype.set({ borderColor: '#9013FE', cornerColor: '#9013FE', //激活状态角落图标的填充颜色 cornerStrokeColor: '#9013FE', //激活状态角落图标的边框颜色 borderOpacityWhenMoving: 1, borderScaleFactor: 1, cornerSize: 6, cornerStyle: 'circle', //rect,circle centeredScaling: false, //角落放大缩小是否是以图形中心为放大原点 centeredRotation: true, //旋转按钮旋转是否是左上角为圆心旋转 transparentCorners: false, //激活状态角落的图标是否透明 rotatingPointOffset: 20, //旋转距旋转体的距离 padding: 0 }) canvas1.preserveObjectStacking = true canvas2.preserveObjectStacking = true // ========================================== // 对象选择和属性更新函数 // ========================================== function updateControls() { if (!canvas.getActiveObject()) return; $("#text_r").val(canvas.getActiveObject().get("angle").toFixed(0)); $("#text_x").val((canvas.getActiveObject().get("left") - background_image.left).toFixed(0)); $("#text_y").val((canvas.getActiveObject().get("top") - background_image.top).toFixed(0)); $("#text_h").val((canvas.getActiveObject().get("scaleY") * canvas.getActiveObject().get("height")).toFixed(0)); $("#text_w").val((canvas.getActiveObject().get("scaleX") * canvas.getActiveObject().get("width")).toFixed(0)); if (canvas.getActiveObject().get("selectable")) { $("#lock_img").attr("src", "public/images/unlock20.png")//已经上锁,需要解锁 } else { $("#lock_img").attr("src", "public/images/lock20.png")//没上锁,可以上锁 } } function selectObject(target, index) { updateControls(); $("#base_control").show(); } function selectPic(target, index) { $("#pic_control").show(); $("#set_bg").show(); let activeObj = canvas.getActiveObject(); if (typeof pic_op !== 'undefined' && pic_op.setValue) { pic_op.setValue(100 - (activeObj.get("opacity") * 100)); } else { $("#pic_op").next().children(".layui-slider-bar").css("width", (1 - activeObj.get("opacity")) * 100 + "%"); $("#pic_op").next().children(".layui-slider-handle").css("left", (1 - activeObj.get("opacity")) * 100 + "%"); } $("#out_pic_control").hide(); $("#text_control").hide(); $("#circle_text_control").hide(); $("#out_text_control").hide(); $("#rect_control").hide(); $("#circle_control").hide(); $("#line_control").hide(); $("#counter_control").hide(); $("#qrcode_Controll").hide(); $("#barcode_controll").hide(); } function selectOutPic(target, index) { $("#out_pic_control").show(); if (typeof out_pic_op !== 'undefined' && out_pic_op.setValue) { out_pic_op.setValue(100 - (canvas.getActiveObject().get("opacity") * 100)); } else { $("#out_pic_op").next().children(".layui-slider-bar").css("width", (1 - canvas.getActiveObject().get("opacity")) * 100 + "%"); $("#out_pic_op").next().children(".layui-slider-handle").css("left", (1 - canvas.getActiveObject().get("opacity")) * 100 + "%"); } let idx = getIndex(canvas.getActiveObject(), canvas); $("#out_pic_field_name").val(objs[idx - 1].name); $("#pic_control").hide(); $("#set_bg").hide(); $("#text_control").hide(); $("#circle_text_control").hide(); $("#out_text_control").hide(); $("#rect_control").hide(); $("#circle_control").hide(); $("#line_control").hide(); $("#counter_control").hide(); $("#qrcode_Controll").hide(); $("#barcode_controll").hide(); } function selectText(target, index) { $("#text_control").show(); $("#text_color").val(canvas.getActiveObject().get("fill")); $("#text_back_color").val(canvas.getActiveObject().get("textBackgroundColor") == "" ? '#ffffff' : canvas.getActiveObject().get("textBackgroundColor")); let val = parseFloat(canvas.getActiveObject().get("fontSize")) * 72 / dpi if (val % 0.5 !== 0) val = Math.round(val * 2) / 2 $("#text_size").val(val); $("#text_text").val(canvas.getActiveObject().get("text")); $("#text_font_family").val(canvas.getActiveObject().get("fontFamily")); if (canvas.getActiveObject().get("underline") == true) { $("#text_underline").addClass("ui-radio-acitve") } else { $("#text_underline").removeClass("ui-radio-acitve") } if (canvas.getActiveObject().get("fontWeight") == "bold") { $("#text_bold").addClass("ui-radio-acitve") } else { $("#text_bold").removeClass("ui-radio-acitve") } if (canvas.getActiveObject().get("fontStyle") == "italic") { $("#text_italics").addClass("ui-radio-acitve") } else { $("#text_italics").removeClass("ui-radio-acitve") } if (canvas.getActiveObject().get("linethrough") == true) { $("#text_linethrough").addClass("ui-radio-acitve") } else { $("#text_linethrough").removeClass("ui-radio-acitve") } $("#circle_text_control").hide(); $("#pic_control").hide(); $("#set_bg").hide(); $("#out_pic_control").hide(); $("#out_text_control").hide(); $("#rect_control").hide(); $("#circle_control").hide(); $("#line_control").hide(); $("#counter_control").hide(); $("#qrcode_Controll").hide(); $("#barcode_controll").hide(); } function selectCircleText(target, index) { let idx = getIndex(canvas.getActiveObject(), canvas); $("#circle_text_control").show(); $("#circle_text_color").val(canvas.getActiveObject().get("fill")); $("#circle_text_back_color").val(canvas.getActiveObject().get("textBackgroundColor") == "" ? '#ffffff' : canvas.getActiveObject().get("textBackgroundColor")); if (typeof circle_text_size !== 'undefined' && circle_text_size.setValue) { circle_text_size_updating = true; // 设置标志位 circle_text_size.setValue(canvas.getActiveObject().get("fontSize")); circle_text_size_updating = false; // 清除标志位 } else { $("#circle_text_size").next().children(".layui-slider-bar").css("width", ((canvas.getActiveObject().get("fontSize") - 10) / (120 - 10)) * 100 + "%"); $("#circle_text_size").next().children(".layui-slider-handle").css("left", ((canvas.getActiveObject().get("fontSize") - 10) / (120 - 10)) * 100 + "%"); } if (typeof circle_text_diameter !== 'undefined' && circle_text_diameter.setValue) { circle_text_diameter.setValue(canvas.getActiveObject().get("diameter")); } else { $("#circle_text_diameter").next().children(".layui-slider-bar").css("width", ((canvas.getActiveObject().get("diameter") - 50) / (1100 - 50)) * 100 + "%"); $("#circle_text_diameter").next().children(".layui-slider-handle").css("left", ((canvas.getActiveObject().get("diameter") - 50) / (1100 - 50)) * 100 + "%"); } $("#circle_text_center").prop("checked", objs[idx - 1].circleCenter ? 'checked' : ''); $("#circle_text_text").val(canvas.getActiveObject().get("text")); $("#circle_text_font_family").val(canvas.getActiveObject().get("fontFamily")); if (canvas.getActiveObject().get("underline") == true) { $("#circle_text_underline").addClass("ui-radio-acitve") } else { $("#circle_text_underline").removeClass("ui-radio-acitve") } if (canvas.getActiveObject().get("fontWeight") == "bold") { $("#circle_text_bold").addClass("ui-radio-acitve") } else { $("#circle_text_bold").removeClass("ui-radio-acitve") } if (canvas.getActiveObject().get("fontStyle") == "italic") { $("#circle_text_italics").addClass("ui-radio-acitve") } else { $("#circle_text_italics").removeClass("ui-radio-acitve") } if (canvas.getActiveObject().get("linethrough") == true) { $("#circle_text_linethrough").addClass("ui-radio-acitve") } else { $("#circle_text_linethrough").removeClass("ui-radio-acitve") } $("#pic_control").hide(); $("#set_bg").hide(); $("#rotate").hide(); $("#out_pic_control").hide(); $("#text_control").hide(); $("#out_text_control").hide(); $("#rect_control").hide(); $("#circle_control").hide(); $("#line_control").hide(); $("#counter_control").hide(); $("#qrcode_Controll").hide(); $("#barcode_controll").hide(); } function selectOutText(target, index) { const align = canvas.getActiveObject().get('textAlign') if (align === 'left') { $('#out_left_alignment').parent().addClass('tool1') } else if (align === 'right') { $('#out_right_alignment').parent().addClass('tool1') } else { $('#out_center_alignment').parent().addClass('tool1') } $("#out_text_control").show(); $("#out_text_color").val(canvas.getActiveObject().get("fill")); $("#out_text_text").val(canvas.getActiveObject().get("text")); let val = parseFloat(canvas.getActiveObject().get("fontSize")) * 72 / dpi if (val % 0.5 !== 0) val = Math.round(val * 2) / 2 $("#out_text_size").val(val); $("#out_text_back_color").val(canvas.getActiveObject().get("textBackgroundColor") == "" ? '#ffffff' : canvas.getActiveObject().get("textBackgroundColor")); let idx = getIndex(canvas.getActiveObject(), canvas); $("#out_text_field_name").val(objs[idx - 1].name); $("#out_text_font_family").val(canvas.getActiveObject().get("fontFamily")); $("#exceed").prop("checked", objs[idx - 1].exceed ? 'checked' : ''); $("#autoWrap").prop("checked", objs[idx - 1].autoWrap ? 'checked' : ''); $("#fixSize").prop("checked", objs[idx - 1].fixSize ? 'checked' : ''); if (canvas.getActiveObject().get("underline") == true) { $("#out_text_underline").addClass("ui-radio-acitve") } else { $("#out_text_underline").removeClass("ui-radio-acitve") } if (canvas.getActiveObject().get("fontWeight") == "bold") { $("#out_text_bold").addClass("ui-radio-acitve") } else { $("#out_text_bold").removeClass("ui-radio-acitve") } if (canvas.getActiveObject().get("fontStyle") == "italic") { $("#out_text_italics").addClass("ui-radio-acitve") } else { $("#out_text_italics").removeClass("ui-radio-acitve") } if (canvas.getActiveObject().get("linethrough") == true) { $("#out_text_linethrough").addClass("ui-radio-acitve") } else { $("#out_text_linethrough").removeClass("ui-radio-acitve") } $("#pic_control").hide(); $("#set_bg").hide(); $("#out_pic_control").hide(); $("#text_control").hide(); $("#circle_text_control").hide(); $("#rect_control").hide(); $("#circle_control").hide(); $("#line_control").hide(); $("#counter_control").hide(); $("#qrcode_Controll").hide(); $("#barcode_controll").hide(); } function selectRect(target, index) { $("#rect_control").show(); $("#rect_color").val(canvas.getActiveObject().get("fill") == "" ? "#ffffff" : canvas.getActiveObject().get("fill")); $("#rect_stroke").val(canvas.getActiveObject().get("stroke") == "" ? "#ffffff" : canvas.getActiveObject().get("stroke")); $("#rect_stroke_width").val(canvas.getActiveObject().get("strokeWidth")); $("#rect_radius").val(canvas.getActiveObject().get("rx")); if (typeof rect_op !== 'undefined' && rect_op.setValue) { rect_op.setValue(100 - (canvas.getActiveObject().get("opacity") * 100)); } else { $("#rect_op").next().children(".layui-slider-bar").css("width", (1 - canvas.getActiveObject().get("opacity")) * 100 + "%"); $("#rect_op").next().children(".layui-slider-handle").css("left", (1 - canvas.getActiveObject().get("opacity")) * 100 + "%"); } $("#pic_control").hide(); $("#set_bg").hide(); $("#out_pic_control").hide(); $("#text_control").hide(); $("#circle_text_control").hide(); $("#out_text_control").hide(); $("#circle_control").hide(); $("#line_control").hide(); $("#counter_control").hide(); $("#qrcode_Controll").hide(); $("#barcode_controll").hide(); } function selectCircle(target, index) { $("#circle_control").show(); $("#circle_color").val(canvas.getActiveObject().get("fill") == "" ? "#ffffff" : canvas.getActiveObject().get("fill")); $("#circle_stroke").val(canvas.getActiveObject().get("stroke") == "" ? "#ffffff" : canvas.getActiveObject().get("stroke")); $("#circle_stroke_width").val(canvas.getActiveObject().get("strokeWidth")); if (typeof circle_op !== 'undefined' && circle_op.setValue) { circle_op.setValue(100 - (canvas.getActiveObject().get("opacity") * 100)); } else { $("#circle_op").next().children(".layui-slider-bar").css("width", (1 - canvas.getActiveObject().get("opacity")) * 100 + "%"); $("#circle_op").next().children(".layui-slider-handle").css("left", (1 - canvas.getActiveObject().get("opacity")) * 100 + "%"); } $("#pic_control").hide(); $("#set_bg").hide(); $("#out_pic_control").hide(); $("#text_control").hide(); $("#circle_text_control").hide(); $("#out_text_control").hide(); $("#rect_control").hide(); $("#line_control").hide(); $("#counter_control").hide(); $("#qrcode_Controll").hide(); $("#barcode_controll").hide(); } function selectLine(target, index) { $("#line_control").show(); let idx = getIndex(canvas.getActiveObject(), canvas); $("#line_color").val(canvas.getActiveObject().get("stroke")); $("#line_dist").val(canvas.getActiveObject().get("strokeDashArray")[0]); $("#text_h").val(canvas.getActiveObject().get("strokeWidth")) $("#pic_control").hide(); $("#set_bg").hide(); $("#out_pic_control").hide(); $("#text_control").hide(); $("#circle_text_control").hide(); $("#out_text_control").hide(); $("#rect_control").hide(); $("#circle_control").hide(); $("#counter_control").hide(); $("#qrcode_Controll").hide(); $("#barcode_controll").hide(); } function selectQrCode(target, index) { $("#qrcode_Controll").show(); let idx = getIndex(canvas.getActiveObject(), canvas); $("#qrcode_field_name").val(objs[idx - 1].name); $("#qrcode_color").val(objs[idx - 1].color); $("#qrcode_val").val(objs[idx - 1].val); $("#line_control").hide(); $("#pic_control").hide(); $("#set_bg").hide(); $("#out_pic_control").hide(); $("#text_control").hide(); $("#circle_text_control").hide(); $("#out_text_control").hide(); $("#rect_control").hide(); $("#circle_control").hide(); $("#counter_control").hide(); $("#barcode_controll").hide(); } function selectBarCode(target, index) { $("#barcode_controll").show(); let idx = getIndex(canvas.getActiveObject(), canvas); $("#barcode_field_name").val(objs[idx - 1].name); $("#barcode_font_family").val(objs[idx - 1].font); let val = parseFloat(objs[idx - 1].fontSize) * 72 / dpi if (val % 0.5 !== 0) val = Math.round(val * 2) / 2 $("#barcode_size").val(val); $("#barcode_val").val(objs[idx - 1].val); $("#barcode_show").prop("checked", objs[idx - 1].show ? 'checked' : ''); $("#line_control").hide(); $("#pic_control").hide(); $("#set_bg").hide(); $("#out_pic_control").hide(); $("#text_control").hide(); $("#circle_text_control").hide(); $("#out_text_control").hide(); $("#rect_control").hide(); $("#circle_control").hide(); $("#counter_control").hide(); $("#qrcode_Controll").hide(); } function selectCounter(target, index) { $("#counter_control").show(); let idx = getIndex(canvas.getActiveObject(), canvas); $("#counter_start").val(objs[idx - 1].start); $("#counter_add").val(objs[idx - 1].add); $("#counter_color").val(canvas.getActiveObject().get("fill")); $("#count_font_family").val(canvas.getActiveObject().get("fontFamily")); let val = parseFloat(canvas.getActiveObject().get("fontSize")) * 72 / dpi if (val % 0.5 !== 0) val = Math.round(val * 2) / 2 $("#count_size").val(val); $("#line_control").hide(); $("#pic_control").hide(); $("#set_bg").hide(); $("#out_pic_control").hide(); $("#text_control").hide(); $("#circle_text_control").hide(); $("#out_text_control").hide(); $("#rect_control").hide(); $("#circle_control").hide(); $("#qrcode_Controll").hide(); $("#barcode_controll").hide(); } function handleObjectSelected(e) { // 确定当前活动的 canvas(从事件参数或全局变量) let currentCanvas = (e && e.target && (e.target.canvas === canvas1 || e.target.canvas === canvas2)) ? (e.target.canvas === canvas1 ? canvas1 : canvas2) : canvas; // 如果事件来自 canvas,更新全局 canvas 变量 if (currentCanvas === canvas1 || currentCanvas === canvas2) { canvas = currentCanvas; objs = (currentCanvas === canvas1) ? objs1 : objs2; } let select_obj = canvas.getActiveObject(); // 1. 未选中任何对象 if (!select_obj) { $("#base_control, #line_control, #pic_control, #out_pic_control, #text_control, #circle_text_control, #out_text_control, #rect_control, #circle_control, #counter_control, #qrcode_Controll, #barcode_controll").hide(); $("#component_type").text(language_str("bg")); selectObj(-1, true); // 传 true,只更新UI return; } // 2. 获取索引 let i = getIndex(select_obj, canvas); if (i != 0) { // --- 选中了普通对象 --- --i; // 转换为 objs 数组下标 if (objs[i]) { switch (objs[i].type) { case 1: // 图片 $("#component_type").text(language_str("img")); selectObject(select_obj, i); selectPic(select_obj, i); break; case 2: // 合成图片 $("#component_type").text(language_str("simg")); selectObject(select_obj, i); selectOutPic(select_obj, i); break; case 3: // 文本 $("#component_type").text(language_str("text")); selectObject(select_obj, i); selectText(select_obj, i); break; case 4: // 合成文本 $("#component_type").text(language_str("stext")); selectObject(select_obj, i); selectOutText(select_obj, i); break; case 5: // 矩形 $("#component_type").text(language_str("rect")); selectObject(select_obj, i); selectRect(select_obj, i); break; case 6: // 圆形 $("#component_type").text(language_str("circ")); selectObject(select_obj, i); selectCircle(select_obj, i); break; case 7: // 直线 $("#component_type").text(language_str("line")); selectObject(select_obj, i); selectLine(select_obj, i); break; case 8: // 二维码 $("#component_type").text(language_str("qrc")); selectObject(select_obj, i); selectQrCode(select_obj, i); break; case 9: // 条形码 $("#component_type").text(language_str("barc")); selectObject(select_obj, i); selectBarCode(select_obj, i); break; case 10: // 计数器 $("#component_type").text(language_str("counter")); selectObject(select_obj, i); selectCounter(select_obj, i); break; case 11: // 圆形文本 $("#component_type").text(language_str("ctext")); selectObject(select_obj, i); selectCircleText(select_obj, i); break; } } // 【关键】这里传入 true! // 意思是:因为我已经是在画布上选中它了,selectObj 函数只负责把左边的条目变灰,不要再回头去 setActiveObject 了 selectObj(i, true); } else { // --- 选中了背景 --- $("#base_control, #line_control, #pic_control, #out_pic_control, #text_control, #circle_text_control, #out_text_control, #rect_control, #circle_control, #counter_control, #qrcode_Controll, #barcode_controll").hide(); selectObj(-1, true); $("#component_type").text(language_str("bg")); } } function handleObjectMoving(e) { let obj = e.target; let idx = getIndex(obj, canvas); // 圆形文本特殊处理 if (idx > 0 && idx <= objs.length && objs[idx - 1] && objs[idx - 1].type === 11 && $("#circle_text_center").prop("checked")) { let pointer = { x: obj.left, y: obj.top }; let distance = getDisdance(pointer.x, pointer.y, canvas.width / 2, canvas.height / 2); let angleDeg = getDeg(pointer); let flipped = pointer.y > canvas.height / 2 ? true : false; if (typeof circle_text_diameter !== 'undefined' && circle_text_diameter.setValue) { circle_text_diameter.setValue(distance * 2 - 50); } obj.set("diameter", distance * 2); obj.set("angle", angleDeg); obj.set("flipped", flipped); } updateControls(); } function initCanvasEvents() { [canvas1, canvas2].forEach(currentCanvas => { // 1. 鼠标移动 currentCanvas.on("mouse:move", (e) => { let offsetX = 0, offsetY = 0; if (currentCanvas === canvas2 && typeof background_image !== 'undefined') { offsetX = background_image.left; offsetY = background_image.top; } if (e.pointer) { $("#posText").text("X: " + (e.pointer.x - offsetX).toFixed(0) + " Y: " + (e.pointer.y - offsetY).toFixed(0)); } }); // 2. 鼠标移出 currentCanvas.on("mouse:out", () => { // 可以在这里隐藏标尺辅助线 }); // 3. 鼠标松开 (添加对象) currentCanvas.on("mouse:up", (e) => { if (addState === 0) return; const ptr = e.pointer; switch (addState) { case 1: addPic(ptr); break; case 2: addOutPic(ptr); break; case 3: addText(ptr); break; case 4: addOutText(ptr); break; case 5: addRect(ptr); break; case 6: addCircle(ptr); break; case 7: addLine(ptr); break; case 8: addQrCode(ptr); break; case 9: addBarCode(ptr); break; case 10: addCounter(ptr); break; case 11: addCircleText(ptr); break; } addState = 0; if (typeof background_image !== 'undefined') background_image.hoverCursor = "default"; currentCanvas.renderAll(); $("#addPic, #addOutPic, #addText, #addOutText, #addRect, #addCircle, #addLine, #addQrCode, #addBarCode, #addCounter, #addCircleText").css("background-color", ""); }); // 4. 选中处理 currentCanvas.on('mouse:down', (e) => { // 更新全局 canvas 变量 canvas = currentCanvas; objs = (currentCanvas === canvas1) ? objs1 : objs2; handleObjectSelected(e); }); // 添加 selection 事件以确保对象选中时正确显示属性面板 currentCanvas.on('selection:created', (e) => { canvas = currentCanvas; objs = (currentCanvas === canvas1) ? objs1 : objs2; handleObjectSelected(e); }); currentCanvas.on('selection:updated', (e) => { canvas = currentCanvas; objs = (currentCanvas === canvas1) ? objs1 : objs2; handleObjectSelected(e); }); currentCanvas.on('object:selected', (e) => { canvas = currentCanvas; objs = (currentCanvas === canvas1) ? objs1 : objs2; handleObjectSelected(e); }); // 5. 对象移动 currentCanvas.on("object:moving", (e) => { handleObjectMoving(e); }); // 6. 变换事件 currentCanvas.on("object:rotating", updateControls); currentCanvas.on("object:scaling", updateControls); currentCanvas.on("object:rotated", () => { recordState(); }); currentCanvas.on('text:editing:entered', () => { _textEdit = true; }); currentCanvas.on('text:editing:exited', () => { _textEdit = false; }); currentCanvas.on("text:changed", (e) => { $("#text_text").val(e.target.get("text")); }); // 7. 对象修改后 currentCanvas.on('object:modified', function (event) { if (currentCanvas.getActiveObjects().length > 1) return; let idx = getIndex(event.target, currentCanvas); if (idx <= 0) return; let objData = objs[idx - 1]; let target = event.target; if ([3, 4, 10].includes(objData.type)) { target.fontSize = (target.fontSize * target.scaleX).toFixed(0); target.scaleX = 1; target.scaleY = 1; target._clearCache(); if (objData.type == 3) $("#text_size").val(target.fontSize); if (objData.type == 4) $("#out_text_size").val(target.fontSize); if (objData.type == 10) $("#count_size").val(target.fontSize); currentCanvas.renderAll(); updateControls(); } else if (objData.type == 5) { target.width *= target.scaleX; target.height *= target.scaleY; target.scaleX = 1; target.scaleY = 1; updateControls(); } recordState(); }); }); } function updateList() { let str = ""; let i = 0; // 定义行样式:Flex布局,两端对齐,相对定位防止溢出 const rowStyle = "display:flex; justify-content:space-between; align-items:center; height:32px; padding:0 5px; cursor:pointer; border-bottom:1px solid #444; color:#ccc; font-size:12px; position:relative;"; // 定义左侧内容样式 const leftStyle = "display:flex; align-items:center; overflow:hidden; white-space:nowrap; max-width:180px;"; // 定义序号样式:靠右,有左边框 const indexStyle = "min-width:24px; text-align:center; padding-left:5px; color:#666; border-left:1px solid #555;"; for (let item of objs) { let displayIndex = i + 1; // 序号底层为1 // 图标逻辑 let iconName = "rect.svg"; switch (item.type) { case 1: iconName = "images.svg"; break; case 2: iconName = "images_add.svg"; break; case 3: iconName = "text.svg"; break; case 4: iconName = "text2.svg"; break; case 5: iconName = "rect.svg"; break; case 6: iconName = "round.svg"; break; case 7: iconName = "line.svg"; break; case 8: iconName = "qrcode.svg"; break; case 9: iconName = "barcode.svg"; break; case 10: iconName = "counter.svg"; break; case 11: iconName = "text3.svg"; break; } // 名称逻辑 let nameText = item.name ? item.name : (item.type === 3 ? "TEXT_" + displayIndex : (item.type === 1 ? "IMG_" + displayIndex : "OBJ_" + displayIndex)); // 组装 HTML str = `
${nameText}
${displayIndex}
` + str; i++; } $(".obj_list").html(str); // 重新绑定点击事件(防止 updateList 后点击失效) $(".obj_list > div").on("click", function () { let imgId = $(this).find("img").attr("id"); if (imgId) { let idx = parseInt(imgId.replace("obj", "")); selectObj(idx); } }); } function selectObj(objIndices, isFromCanvas = false) { let indicesArray = (Array.isArray(objIndices)) ? objIndices : ((typeof objIndices === 'number' && objIndices >= 0) ? [objIndices] : []); // 清除所有高亮 $('.obj_list > div').css("background-color", "transparent"); // 遍历列表查找并高亮 $('.obj_list > div').each(function () { let $img = $(this).find("img"); if ($img.length === 0) return; let idIndex = parseInt($img.attr("id").replace("obj", "")); if (indicesArray.includes(idIndex)) { $(this).css("background-color", "#6F7A84"); // 自动滚动到可视区域 if (isFromCanvas && indicesArray[0] === idIndex) { this.scrollIntoView({ behavior: "smooth", block: "nearest" }); } } }); // 如果是点击列表,触发画布选中 if (!isFromCanvas && indicesArray.length === 1) { let target = canvas.getObjects()[indicesArray[0] + 1]; if (target) { canvas.setActiveObject(target); canvas.requestRenderAll(); handleObjectSelected(); } } } function recordState() { ++step.val; for (let i = step.val; i < recordObjs.length; i++) { recordObjs.pop(); recordJson.pop(); } recordObjs.push(JSON.stringify(objs)) j = canvas.toJSON(["selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline"]); j.objects[0].hoverCursor = "default"; recordJson.push(j); } function getIndex(target, _canvas = null) { let temp_objs; if (_canvas == null) { temp_objs = canvas.getObjects(); } else { temp_objs = _canvas.getObjects(); } let i = 0; for (let res of temp_objs) { if (target == res) { return i; } i++; } return 0; } function getType(target) { let temp_objs = canvas.getObjects(); let i = 0; for (let res of temp_objs) { if (target == res) { return objs[i].type; } i++; } } function checkName(name) { //检查字段名是否重复了 for (let item of objs1) { if (item.name == name) { return false; } } for (let item of objs2) { if (item.name == name) { return false; } } return true; } function resName(pre_name) { //返回的是不重复的字段名 let num = (objs1.length + objs2.length) - 1; do { num++; } while (!checkName(pre_name + num));//重复了就再加1 return pre_name + num; } function getDisdance(x1, y1, x2, y2) { var dx = Math.abs(x2 - x1); // 计算x轴上的距离差,并取绝对值 var dy = Math.abs(y2 - y1); // 计算y轴上的距离差,并取绝对值 var distance = Math.sqrt(dx * dx + dy * dy); // 应用勾股定理计算距离 return distance; } function getDeg(pointer) { // 计算点击位置与画布中心的坐标差 var centerX = canvas.width / 2; var centerY = canvas.height / 2; var mouseX = pointer.x; var mouseY = pointer.y; var dx = mouseX - centerX; var dy = mouseY - centerY; // 计算旋转角度(弧度) var angleRad = Math.atan2(dy, dx); // 将角度转换为度数 var angleDeg = angleRad * (180 / Math.PI); if (angleDeg < 0) { angleDeg += 360; } angleDeg += 90 return angleDeg } function getCoordsMinX(acoords) { x = acoords[0].x; for (let item of acoords) { if (item.x < x) { x = item.x } } return x; } function getCoordsMaxX(acoords) { x = acoords[0].x; for (let item of acoords) { if (item.x > x) { x = item.x } } return x; } function getCoordsMinY(acoords) { y = acoords[0].y; for (let item of acoords) { if (item.y < y) { y = item.y } } return y; } function getCoordsMaxY(acoords) { y = acoords[0].y; for (let item of acoords) { if (item.y > y) { y = item.y } } return y; } function addBackground() { // 类型改变 fabric.Image.fromURL('./public/images/bg_front_2.png', function (image) { image.left = ($('#canvas1').width() - image.width * image.get('scaleX')) / 2 image.top = ($('#canvas1').height() - image.height * image.get('scaleY')) / 2 image.selectable = false image.hoverable = false image.hoverCursor = 'default' image.setSrc(image.toDataURL(), function (image) { background_image1 = image background_image = background_image1 canvas1.add(image) canvas1.renderAll() recordObjs1.push(JSON.stringify(objs1)) recordJson1.push(canvas1.toJSON(['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline'])) }) // 类型改变 fabric.Image.fromURL('./public/images/bg_back_2.png', function (image) { image.left = ($('#canvas2').width() - image.width) / 2 image.top = ($('#canvas2').height() - image.height) / 2 image.selectable = false image.hoverable = false image.hoverCursor = 'default' image.setSrc(image.toDataURL(), function (image) { background_image2 = image canvas2.add(image) canvas2.renderAll() recordObjs2.push(JSON.stringify(objs2)) recordJson2.push(canvas2.toJSON(['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline'])) let file = GetFile().get('file') if (file && file != 'empty') { open(file) } }) }) is_bgi_add = true }) } function addPic(pointer) { pre_add_image.left = pointer.x pre_add_image.top = pointer.y canvas.add(pre_add_image) objs.push({ type: 1, black: false }) canvas.renderAll() updateList() recordState() } function addOutPic(pointer) { fabric.Image.fromURL('./public/images/outpic_extra.png', function (image) { let width = canvas.getWidth() let height = canvas.getHeight() image.left = pointer.x image.top = pointer.y image.opacity = 1 image.setSrc(image.toDataURL(), function (image) { canvas.add(image) objs.push({ type: 2, name: resName('OUT_PIC_'), black: false }) canvas.renderAll() updateList() recordState() }) }) } function addText(pointer) { let text = new fabric.IText('TEXT', { left: pointer.x, top: pointer.y, fill: '#000000', textBackgroundColor: '', fontStyle: 'normal', fontWeight: 'normal', underline: false, linethrough: false, fontFamily: 'Arial' }) text.setControlVisible('ml', false) text.setControlVisible('mt', false) text.setControlVisible('mr', false) text.setControlVisible('mb', false) canvas.add(text) objs.push({ type: 3, black: false }) canvas.renderAll() updateList() recordState() } function addCircleText(pointer) { let distance = getDisdance(pointer.x, pointer.y, canvas.width / 2, canvas.height / 2) let angleDeg = getDeg(pointer) let text = new fabric.CurvedText('Circle TEXT', { left: pointer.x, top: pointer.y, fill: "#000000", textBackgroundColor: "", fontStyle: "normal", fontWeight: "normal", diameter: distance * 2, fontSize: 30, underline: false, linethrough: false, lockScalingX: true, lockScalingY: true, fontFamily: 'Arial', originX: 'center', originY: 'center', angle: angleDeg, flipped: pointer.y > canvas.height / 2 ? true : false }); text.setControlVisible("ml", false); text.setControlVisible("mt", false); text.setControlVisible("mr", false); text.setControlVisible("mb", false); canvas.add(text); objs.push({ type: 11, black: false, circleCenter: 0 }); canvas.renderAll(); updateList(); recordState(); } function addOutText(pointer) { let text = new fabric.Text('MERGE TEXT', { left: pointer.x, top: pointer.y, fill: '#000000', fontStyle: 'normal', fontWeight: 'normal', textBackgroundColor: '', underline: false, linethrough: false, fontFamily: 'Arial', textAlign: 'center' }) text.setControlVisible('ml', false) text.setControlVisible('mt', false) text.setControlVisible('mr', false) text.setControlVisible('mb', false) canvas.add(text) objs.push({ type: 4, name: resName('OUT_TEXT_'), exceed: false, autoWrap: false, fixSize: false, black: false, align: 'center' }) canvas.renderAll() updateList() recordState() } function addRect(pointer) { let rect = new fabric.Rect({ left: pointer.x, top: pointer.y, fill: '', stroke: "#ff0000", strokeWidth: 3, width: 100, height: 100 }) canvas.add(rect) objs.push({ type: 5, black: false }) canvas.renderAll() updateList() recordState() } function addCircle(pointer) { let circle = new fabric.Circle({ left: pointer.x, top: pointer.y, fill: '', stroke: '#ff0000', strokeWidth: 3, radius: 100, strokeUniform: true }) canvas.add(circle) objs.push({ type: 6, black: false }) canvas.renderAll() updateList() recordState() } function addLine(pointer) { let line = new fabric.Line([50, 100, 200, 100], { left: pointer.x, top: pointer.y, stroke: '#ff0000', strokeWidth: 3, strokeDashArray: [0, 0], }) line.setControlVisible('tl', false) line.setControlVisible('tr', false) line.setControlVisible('br', false) line.setControlVisible('bl', false) line.setControlVisible('mt', false) line.setControlVisible('mb', false) canvas.add(line) objs.push({ type: 7, black: false }) canvas.renderAll() updateList() recordState() } function addQrCode(pointer) { let qr = jrQrcode.getQrBase64('https://www.cardsoon.com', { padding: 0 }) fabric.Image.fromURL(qr, function (image) { image.left = pointer.x image.top = pointer.y canvas.add(image) objs.push({ type: 8, name: resName('QR_CODE_'), color: '#000000', val: 'http://www.cardsoon.com', black: false }) canvas.renderAll() updateList() recordState() }) } function addBarCode(pointer) { JsBarcode('#barcode', '123456789', { margin: 0, lineColor: '#000000', fontSize: 18, font: 'Arial' }) let barcode = document.getElementById('barcode') let bar = barcode.toDataURL('image/png') fabric.Image.fromURL(bar, function (image) { image.left = pointer.x image.top = pointer.y canvas.add(image) objs.push({ type: 9, name: resName('BAR_CODE_'), val: '123456789', color: '#000000', fontSize: 18, font: 'Arial', show: false, black: false }) canvas.renderAll() updateList() recordState() }) } function addCounter(pointer) { let text = new fabric.Text('0', { left: pointer.x, top: pointer.y, fill: '#000000' }) canvas.add(text) objs.push({ type: 10, name: resName('COUNTER_'), start: 0, add: 1, black: false }) canvas.renderAll() updateList() recordState() } var front_list = [] ipcRenderer.on('font-list', (event, data) => { const newFontList = [] data.map((item) => { if (item.indexOf('"') === 0) { newFontList.push(item.replace(/^"|"$/g, '')) } else { newFontList.push(item) } }) front_list = newFontList for (let item of front_list) { $('#text_font_family').append("') $("#circle_text_font_family").append(""); $('#out_text_font_family').append("') $('#barcode_font_family').append("') $('#count_font_family').append("') } }) ipcRenderer.on('sys-lan', (event, data) => { let lang = localStorage.getItem('lang') if (lang) { s_lan = lang langua_ge(lang) $('#language_select').val(lang) return } switch (data) { case 'zh-CN': s_lan = 'zh' langua_ge('zh') $('#language_select').val('zh') localStorage.setItem('lang', 'zh') break case 'zh': s_lan = 'zh' langua_ge('zh') $('#language_select').val('zh') localStorage.setItem('lang', 'zh') break case 'zh-TW': s_lan = 'ozh' langua_ge('ozh') $('#language_select').val('ozh') localStorage.setItem('lang', 'ozh') break case 'en': s_lan = 'en' langua_ge('en') $('#language_select').val('en') localStorage.setItem('lang', 'en') break case 'en-AU': s_lan = 'en' langua_ge('en') $('#language_select').val('en') localStorage.setItem('lang', 'en') break case 'en-CA': s_lan = 'en' langua_ge('en') $('#language_select').val('en') localStorage.setItem('lang', 'en') break case 'en-GB': s_lan = 'en' langua_ge('en') $('#language_select').val('en') localStorage.setItem('lang', 'en') break case 'en-NZ': s_lan = 'en' langua_ge('en') $('#language_select').val('en') localStorage.setItem('lang', 'en') break case 'en-US': s_lan = 'en' langua_ge('en') $('#language_select').val('en') localStorage.setItem('lang', 'en') break case 'en-ZA': s_lan = 'en' langua_ge('en') $('#language_select').val('en') localStorage.setItem('lang', 'en') break } }) ipcRenderer.on('close', (event, message) => { layer.confirm( '' + language_str('whetherSave') + '', { //是否保存当前文件? btn: [language_str('save'), language_str('noSave'), language_str('cancel')], //['保存', '不保存', '取消'] btn3: function (index, layero) { } }, function (index, layero) { output(() => ipcRenderer.send('run-close')) }, function (index) { ipcRenderer.send('run-close') } ) }) addBackground() //添加背景 白色卡片 initAligningGuidelines(canvas1) initAligningGuidelines(canvas2) // 初始化 Canvas 事件监听 initCanvasEvents(); // 初始化所有 Slider var circle_text_size_updating = false; // 标志位,防止循环更新 var circle_text_size = slider.render({ elem: '#circle_text_size', min: 10, max: 120, value: 50, change: function (value) { if (circle_text_size_updating) return; // 如果正在更新slider,跳过change事件 if (canvas.getActiveObject()) { canvas.getActiveObject().set('fontSize', value); canvas.renderAll(); updateControls(); recordState(); } } }); var circle_text_diameter = slider.render({ elem: '#circle_text_diameter', min: 50, max: 1100, value: 500, change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set('diameter', value); canvas.renderAll(); updateControls(); recordState(); } } }); var out_pic_op = slider.render({ elem: '#out_pic_op', min: 0, max: 100, value: 0, change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set('opacity', 1 - value / 100); canvas.renderAll(); updateControls(); recordState(); } } }); var pic_op = slider.render({ elem: '#pic_op', min: 0, max: 100, value: 0, change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set('opacity', 1 - value / 100); canvas.renderAll(); updateControls(); recordState(); } } }); var rect_op = slider.render({ elem: '#rect_op', min: 0, max: 100, value: 0, change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set('opacity', 1 - value / 100); canvas.renderAll(); updateControls(); recordState(); } } }); var circle_op = slider.render({ elem: '#circle_op', min: 0, max: 100, value: 0, change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set('opacity', 1 - value / 100); canvas.renderAll(); updateControls(); recordState(); } } }); //canvas.setZoom(0.5) //recordObjs1.push(JSON.stringify(objs1)); //recordJson1.push(canvas1.toJSON(["selectable","hoverable","hoverCursor","text","fontStyle","fontWeight","underline"])); //recordObjs2.push(JSON.stringify(objs2)); //recordJson2.push(canvas2.toJSON(["selectable","hoverable","hoverCursor","text","fontStyle","fontWeight","underline"])); canvas.renderAll() //重新渲染画布 var zoomSlider = slider.render({ elem: '#zoomSlide', //绑定元素 min: 25, max: 500, value: 100, change(val) { canvas.zoomToPoint(new fabric.Point(canvas.width / 2, canvas.height / 2), val / 100) zoom = val / 100 //$(".canvas-container").css("transform","scale(" + val / 100 + ")") } }) window.rotate = function (id) { let angle = $(`#${id}`).attr('angle') - 0 angle += 90 $(`#${id}`).css('transform', `rotate(${angle}deg)`) $(`#${id}`).attr('angle', angle) } $('#display').on('click', function () { // 类型改变 fabric.Image.fromURL('./public/images/front_bg' + bg_version + '_2.png', function (i1) { i1.left = background_image.left i1.top = background_image.top let img1 = i1 fabric.Image.fromURL('./public/images/back_bg_2.png', function (i2) { i2.left = background_image.left i2.top = background_image.top let img2 = i2 fabric.Image.fromURL('./public/images/op_2.png', function (i3) { i2.left = background_image.left i2.top = background_image.top let img3 = i3 display_func(img1, img2, img3) }) }) }) }) // 导出文件 async function savePdf(buffer) { let fs = require('fs'); // 弹出保存对话框 const { canceled, filePath } = await dialog.showSaveDialog({ title: '保存文件', defaultPath: path.join(exePath, 'output.pdf'), // 默认路径 filters: [{ name: 'PDF 文件', extensions: ['pdf'] }] // 文件过滤器 }); if (canceled) { console.log('用户取消了保存操作'); return; } // 将Buffer写入PNG文件到用户选择的路径 fs.writeFile(filePath, buffer, (err) => { if (err) { console.error('写入文件失败:', err); } else { layer.msg(language_str("saveSucc") + filePath);//'保存成功至' console.log('PNG文件已保存至:', filePath); } }); } function display_func(img1, img2, img3) { $('#base_control').hide() $('#line_control').hide() $('#pic_control').hide() $("#set_bg").hide(); $('#out_pic_control').hide() $('#text_control').hide() $('#circle_text_control').hide() $('#out_text_control').hide() $('#rect_control').hide() $('#circle_control').hide() $('#counter_control').hide() $('#qrcode_Controll').hide() $('#barcode_controll').hide() $('#component_type').text(language_str('bg')) //"背景" canvas1.discardActiveObject().renderAll() canvas2.discardActiveObject().renderAll() let _objs1 = canvas1.getObjects() let g1 = [] let g3 = [] let g5 = [] let left = 0 //左裁剪 let top = 0 //顶裁剪 let black_left = 0 //黑色左裁剪 let black_top = 0 //黑色顶裁剪 let all_left = 0 //预览图左裁剪 let all_top = 0 //预览图顶裁剪 for (let item of _objs1) { let idx = getIndex(item, canvas1) //console.log("------"); //console.log(item.getCoords()); //console.log(getCoordsMinX(background_image.getCoords())); //console.log(getCoordsMinX(item.getCoords())); //console.log(getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords())); //console.log("------"); if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > all_left) { all_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > all_top) { all_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g3.push(fabric.util.object.clone(item)) if (idx != 0 && (objs1[idx - 1].type == 10 || objs1[idx - 1].type == 4)) { continue //计数器 或 合成文字 } if (idx != 0 && (objs1[idx - 1].type == 8 || objs1[idx - 1].type == 9)) { continue //二维码 条形码 } if (idx != 0 && objs1[idx - 1].type == 2) { continue //合成图片 } if ( idx != 0 && ((objs1[idx - 1].type == 3 && (item.get('fill') == '#000000' || (item.get('fill') == '#ffffff' && false))) || ( objs1[idx - 1].type == 11 && (item.get("fill") == "#000000" || (item.get("fill") == "#ffffff" && false)) //圆形文字 && 黑色 ) || //文字 && 黑色 (objs1[idx - 1].type == 5 && //矩形 (((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('stroke') == '') || //黑填充 透明边框 (item.get('fill') == '' && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //透明填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //黑填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('strokeWidth') == 0))) || //黑填充 0边框 (objs1[idx - 1].type == 6 && //圆形 (((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('stroke') == '') || //黑填充 透明边框 (item.get('fill') == '' && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //透明填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //黑填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('strokeWidth') == 0))) || //黑填充 0边框 (objs1[idx - 1].type == 7 && //直线 (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || objs1[idx - 1].black == true) //勾选并入黑色图层 // ( // objs1[idx-1].type == 1//固定图片 // && // objs1[idx-1].black == true //勾选并入黑色图层 // ) ) { if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > black_left) { black_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > black_top) { black_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g5.push(fabric.util.object.clone(item)) continue //黑色文字 黑色矩形 黑色线条 黑色圆形 } else if (idx == 0) { g5.push(fabric.util.object.clone(item)) } if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > left) { left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > top) { top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g1.push(fabric.util.object.clone(item)) } let url1 = new fabric.Group(g1).toDataURL({ height: 648, width: 1012, top: top / zoom, left: left / zoom }) // g3.push(img1) let url3 = new fabric.Group(g3).toDataURL({ height: 648, width: 1012, top: all_top / zoom, left: all_left / zoom }) let url5 = new fabric.Group(g5).toDataURL({ height: 648, width: 1012, top: black_top / zoom, left: black_left / zoom }) let _objs2 = canvas2.getObjects() let g2 = [] let g4 = [] let g6 = [] left = 0 top = 0 black_top = 0 black_left = 0 all_top = 0 all_left = 0 for (let item of _objs2) { let idx = getIndex(item, canvas2) if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > all_left) { all_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > all_top) { all_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g4.push(fabric.util.object.clone(item)) if (idx != 0 && (objs2[idx - 1].type == 10 || objs2[idx - 1].type == 4)) { continue //黑色字或计数器 或 合成文字 } if (idx != 0 && (objs2[idx - 1].type == 8 || objs2[idx - 1].type == 9)) { continue //二维码 条形码 } if (idx != 0 && objs2[idx - 1].type == 2) { continue //合成图片 } if (idx != 0 && objs2[idx - 1].type == 10) { continue //合成图片 } if ( idx != 0 && ((objs2[idx - 1].type == 3 && (item.get('fill') == '#000000' || (item.get('fill') == '#ffffff' && false))) || ( objs2[idx - 1].type == 11 && (item.get("fill") == "#000000" || (item.get("fill") == "#ffffff" && false)) //圆形文字 && 黑色 ) || //文字 && 黑色 (objs2[idx - 1].type == 5 && //矩形 (((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('stroke') == '') || //黑填充 透明边框 (item.get('fill') == '' && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //透明填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //黑填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('strokeWidth') == 0))) || //黑填充 0边框 (objs2[idx - 1].type == 6 && //圆形 (((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('stroke') == '') || //黑填充 透明边框 (item.get('fill') == '' && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //透明填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //黑填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('strokeWidth') == 0))) || //黑填充 0边框 (objs2[idx - 1].type == 7 && //直线 (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || objs2[idx - 1].black == true) //勾选并入黑色图层 // ( // objs2[idx-1].type == 1//固定图片 // && // objs2[idx-1].black == true //勾选并入黑色图层 // ) ) { if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > black_left) { black_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > black_top) { black_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g6.push(fabric.util.object.clone(item)) continue //黑色文字 黑色矩形 黑色线条 黑色圆形 } else if (idx == 0) { g6.push(fabric.util.object.clone(item)) } if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > left) { left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > top) { top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g2.push(fabric.util.object.clone(item)) } let url2 = new fabric.Group(g2).toDataURL({ height: 648, width: 1012, top: top / zoom, left: left / zoom }) let white_url1 = canvas1.getObjects()[0].toDataURL({ height: 648, width: 1012 }) // g4.push(img2) let url4 = new fabric.Group(g4).toDataURL({ height: 648, width: 1012, top: all_top / zoom, left: all_left / zoom }) let url6 = new fabric.Group(g6).toDataURL({ height: 648, width: 1012, top: black_top / zoom, left: black_left / zoom }) let white_url2 = canvas2.getObjects()[0].toDataURL({ height: 648, width: 1012 }) //console.log(canvas.getObjects()) let w = document.body.clientWidth * 0.7 + 'px' let h = (document.body.clientWidth * 0.7 - 40 - 0.06 * document.body.clientWidth * 0.7) * 0.62548 + 150 + 'px' let h1 = (document.body.clientWidth * 0.7 - 40 - 0.06 * document.body.clientWidth * 0.7) * 0.62548 + 'px' if ((document.body.clientWidth * 0.7 - 40 - 0.06 * document.body.clientWidth * 0.7) * 0.62548 > document.body.clientHeight) { //高度超出,则按高度计算 h = document.body.clientHeight * 0.8 + 'px' h1 = document.body.clientHeight * 0.8 - 150 + 'px' w = ((document.body.clientHeight * 0.8 - 150) / 0.62548 + 40) / 0.94 } let fs = require('fs'); const Buffer = require('buffer').Buffer; const buffer3 = Buffer.from(url3.replace(/^data:image\/\w+;base64,/, ""), 'base64'); const printPath3 = path.join(exePath, 'print3.png'); fs.writeFileSync(printPath3, buffer3); const buffer4 = Buffer.from(url4.replace(/^data:image\/\w+;base64,/, ""), 'base64'); const printPath4 = path.join(exePath, 'print4.png'); fs.writeFileSync(printPath4, buffer4); // 创建 PDF 实例 const pdf = new jspdf.jsPDF({ orientation: 'landscape', unit: 'px', format: [1012, 648] }); let btns = [language_str('output')] if ((objs1.length != 0) & (objs2.length == 0)) { //只有正面 let img = new Image() img.src = printPath3 pdf.addImage(img, 'JPEG', 0, 0, 1012, 648) btns.push('打印正面') } else if ((objs1.length == 0) & (objs2.length != 0)) { //只有背面 let img = new Image() img.src = printPath4 pdf.addImage(img, 'JPEG', 0, 0, 1012, 648) btns.push('打印背面') } else { let img = new Image() img.src = printPath3 pdf.addImage(img, 'JPEG', 0, 0, 1012, 648) pdf.addPage() let img2 = new Image() img2.src = printPath4 pdf.addImage(img2, 'JPEG', 0, 0, 1012, 648) btns.push('打印正面') btns.push('打印背面') } var pdfBlob = pdf.output('blob') var pdfBuffer = pdf.output('arraybuffer') layer.open({ type: 1, area: [w, h], title: language_str('display'), //不显示标题栏"预览" shadeClose: true, //点击遮罩关闭 content: `
`, btn: btns, //'导出' btn1: function (index, layero) { savePdf(Buffer.from(pdfBuffer)) }, btn2: function () { if (btns[1] === '打印正面') { printJS({ printable: printPath3, type: 'image', style: 'img { width: 100%; height: auto; }' }) } else { printJS({ printable: printPath4, type: 'image', style: 'img { width: 100%; height: auto; }' }) } }, btn3: function () { //打印背面 printJS({ printable: printPath4, type: 'image', style: 'img { width: 100%; height: auto; }' }) } }) } function output(callback = null, _save = save) { // 类型改变 fabric.Image.fromURL('./public/images/op_2.png', function (i1) { i1.left = background_image.left i1.top = background_image.top let img1 = i1 fabric.Image.fromURL('./public/images/op_2.png', function (i2) { i2.left = background_image.left i2.top = background_image.top let img2 = i2 $('#base_control').hide() $('#line_control').hide() $('#pic_control').hide() $("#set_bg").hide(); $('#out_pic_control').hide() $('#text_control').hide() $("#circle_text_control").hide(); $('#out_text_control').hide() $('#rect_control').hide() $('#circle_control').hide() $('#counter_control').hide() $('#qrcode_Controll').hide() $('#barcode_controll').hide() $('#component_type').text(language_str('bg')) //"背景" canvas1.discardActiveObject().renderAll() canvas2.discardActiveObject().renderAll() let _objs1 = canvas1.getObjects() let g1 = [] let g3 = [] let g5 = [] //黑色图层 let left = 0 //左裁剪 let top = 0 //顶裁剪 let black_left = 0 //黑色左裁剪 let black_top = 0 //黑色顶裁剪 let all_left = 0 //预览图左裁剪 let all_top = 0 //预览图顶裁剪 for (let item of _objs1) { let idx = getIndex(item, canvas1) //console.log("------"); //console.log(item.getCoords()); //console.log(getCoordsMinX(background_image.getCoords())); //console.log(getCoordsMinX(item.getCoords())); //console.log(getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords())); //console.log("------"); if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > all_left) { all_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > all_top) { all_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g3.push(fabric.util.object.clone(item)) if (idx != 0 && (objs1[idx - 1].type == 10 || objs1[idx - 1].type == 4)) { continue //计数器 或 合成文字 } if (idx != 0 && (objs1[idx - 1].type == 8 || objs1[idx - 1].type == 9)) { continue //二维码 条形码 } if (idx != 0 && objs1[idx - 1].type == 2) { continue //合成图片 } if ( idx != 0 && ((objs1[idx - 1].type == 3 && item.get('fill') == '#000000') || ( objs1[idx - 1].type == 11 && item.get("fill") == "#000000" //圆形文字 && 黑色 ) || //文字 && 黑色 (objs1[idx - 1].type == 5 && //矩形 (((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('stroke') == '') || //黑填充 透明边框 (item.get('fill') == '' && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //透明填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //黑填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('strokeWidth') == 0))) || //黑填充 0边框 (objs1[idx - 1].type == 6 && //圆形 (((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('stroke') == '') || //黑填充 透明边框 (item.get('fill') == '' && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //透明填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //黑填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('strokeWidth') == 0))) || //黑填充 0边框 (objs1[idx - 1].type == 7 && //直线 (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || objs1[idx - 1].black == true) //勾选并入黑色图层 // || // ( // objs1[idx-1].type == 1//固定图片 // && // objs1[idx-1].black == true //勾选并入黑色图层 // ) ) { if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > black_left) { black_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > black_top) { black_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g5.push(fabric.util.object.clone(item)) continue //黑色文字 黑色矩形 黑色线条 黑色圆形 } else if (idx == 0) { g5.push(fabric.util.object.clone(item)) } if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > left) { left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > top) { top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g1.push(fabric.util.object.clone(item)) } let url1 = new fabric.Group(g1).toDataURL({ height: 648, width: 1012, top: top / zoom, left: left / zoom }) g3.push(img1) let url3 = new fabric.Group(g3).toDataURL({ height: 648, width: 1012, top: all_top / zoom, left: all_left / zoom }) let url5 = new fabric.Group(g5).toDataURL({ height: 648, width: 1012, top: black_top / zoom, left: black_left / zoom }) let _objs2 = canvas2.getObjects() let g2 = [] let g4 = [] let g6 = [] //黑色图层 left = 0 top = 0 black_left = 0 //黑色左裁剪 black_top = 0 //黑色顶裁剪 all_left = 0 //黑色左裁剪 all_top = 0 //黑色顶裁剪 for (let item of _objs2) { let idx = getIndex(item, canvas2) if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > all_left) { all_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > all_top) { all_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g4.push(fabric.util.object.clone(item)) if (idx != 0 && (objs2[idx - 1].type == 10 || objs2[idx - 1].type == 4)) { continue //计数器 或 合成文字 } if (idx != 0 && (objs2[idx - 1].type == 8 || objs2[idx - 1].type == 9)) { continue //二维码 条形码 } if (idx != 0 && objs2[idx - 1].type == 2) { continue //合成图片 } if ( idx != 0 && ((objs2[idx - 1].type == 3 && (item.get('fill') == '#000000' || (item.get('fill') == '#ffffff' && false))) || ( objs2[idx - 1].type == 11 && (item.get("fill") == "#000000" || (item.get("fill") == "#ffffff" && false)) //圆形文字 && 黑色 ) || //文字 && 黑色 (objs2[idx - 1].type == 5 && //矩形 (((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('stroke') == '') || //黑填充 透明边框 (item.get('fill') == '' && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //透明填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //黑填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('strokeWidth') == 0))) || //黑填充 0边框 (objs2[idx - 1].type == 6 && //圆形 (((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('stroke') == '') || //黑填充 透明边框 (item.get('fill') == '' && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //透明填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || //黑填充 黑边框 ((item.get('fill') == '#000000' || item.get('fill') == '#ffffff') && item.get('strokeWidth') == 0))) || //黑填充 0边框 (objs2[idx - 1].type == 7 && //直线 (item.get('stroke') == '#000000' || item.get('stroke') == '#ffffff')) || objs2[idx - 1].black == true) //勾选并入黑色图层 // ( // objs2[idx-1].type == 1//固定图片 // && // objs2[idx-1].black == true //勾选并入黑色图层 // ) ) { if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > black_left) { black_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > black_top) { black_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g6.push(fabric.util.object.clone(item)) continue //黑色文字 黑色矩形 黑色线条 黑色圆形 } else if (idx == 0) { g6.push(fabric.util.object.clone(item)) } if (getCoordsMinX(background_image.getCoords()) - getCoordsMinX(item.getCoords()) > left) { left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(background_image.getCoords())) } if (getCoordsMinY(background_image.getCoords()) - getCoordsMinY(item.getCoords()) > top) { top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(background_image.getCoords())) } g2.push(fabric.util.object.clone(item)) } let url2 = new fabric.Group(g2).toDataURL({ height: 648, width: 1012, top: top / zoom, left: left / zoom }) let white_url1 = canvas1.getObjects()[0].toDataURL({ height: 648, width: 1012 }) g4.push(img2) let url4 = new fabric.Group(g4).toDataURL({ height: 648, width: 1012, top: all_top / zoom, left: all_left / zoom }) let url6 = new fabric.Group(g6).toDataURL({ height: 648, width: 1012, top: black_top / zoom, left: black_left / zoom }) let white_url2 = canvas2.getObjects()[0].toDataURL({ height: 648, width: 1012 }) //console.log(canvas.getObjects()) let w = document.body.clientWidth * 0.7 + 'px' let h = (document.body.clientWidth * 0.7 - 40 - 0.06 * document.body.clientWidth * 0.7) * 0.62548 + 150 + 'px' let h1 = (document.body.clientWidth * 0.7 - 40 - 0.06 * document.body.clientWidth * 0.7) * 0.62548 + 'px' if ((document.body.clientWidth * 0.7 - 40 - 0.06 * document.body.clientWidth * 0.7) * 0.62548 > document.body.clientHeight) { //高度超出,则按高度计算 h = document.body.clientHeight * 0.8 + 'px' h1 = document.body.clientHeight * 0.8 - 150 + 'px' w = ((document.body.clientHeight * 0.8 - 150) / 0.62548 + 40) / 0.94 } let op = { frontColorPic: '', backColorPic: '', frontBlackPic: '', backBlackPic: '', frontData: [], backData: [], flag: 1, frontDisplayPic: url3, backDisplayPic: url4 } if ((objs1.length != 0) & (objs2.length == 0)) { op.flag = 2 } else if ((objs1.length == 0) & (objs2.length != 0)) { op.flag = 3 } op.frontColorPic = desEncrypt(url1) op.backColorPic = desEncrypt(url2) //op.frontBlPic = url5; //op.backBlPic = url6; op.frontBlackPic = desEncrypt(url5) op.backBlackPic = desEncrypt(url6) for (let item of _objs1) { let idx = getIndex(item, canvas1) if (idx == 0) continue //background-image let type = objs1[idx - 1].type if (type == 2) { //out_pic let xy = getAbsoluteXY(item) op.frontData.push({ type: 1, name: objs1[idx - 1].name, FontColor: objs1[idx - 1].black ? '#000000' : '@', transparency: item.get('opacity'), x: parseInt((xy[0] - background_image1.left).toFixed(0)), y: parseInt((xy[1] - background_image1.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)) }) } else if (type == 3) { //text /*2022-07-26 黑色并入黑色图层 if(item.get("fill") == "#000000"){ //纯黑 op.frontData.push({ "type" : 2, "Text" : item.get("text"), "FontFamily" : item.get("fontFamily"), "FontSize" : parseInt(item.get("fontSize")), "FontColor" : "#000000", "FontBackgroudColor" : item.get("textBackgroundColor"), "Bold" : item.get("fontWeight") == "bold", "Underline" : item.get("underline"), "Italic" : item.get("fontStyle") == "italic", "Strikeout" : item.get("linethrough"), "x" : parseInt((item.get("left") - background_image1.left).toFixed(0)), "y" : parseInt((item.get("top") - background_image1.top).toFixed(0)), "h" : parseInt((item.get("scaleY") * item.get("height")).toFixed(0)), "w" : parseInt((item.get("scaleX") * item.get("width")).toFixed(0)), "rotate" : parseInt(item.get("angle").toFixed(0)) }); } */ } else if (type == 11) { //circle_text op.frontData.push({ "type": 2, "Text": item.get("text"), "FontFamily": item.get("fontFamily"), "FontSize": parseInt(item.get("fontSize")), "FontColor": "#000000", "FontBackgroudColor": item.get("textBackgroundColor"), "Bold": item.get("fontWeight") == "bold", "Underline": item.get("underline"), "Italic": item.get("fontStyle") == "italic", "Strikeout": item.get("linethrough"), "x": parseInt((item.get("left") - background_image1.left).toFixed(0)), "y": parseInt((item.get("top") - background_image1.top).toFixed(0)), "h": parseInt((item.get("scaleY") * item.get("height")).toFixed(0)), "w": parseInt((item.get("scaleX") * item.get("width")).toFixed(0)), "rotate": parseInt(item.get("angle").toFixed(0)), "circleCenter": objs1[idx - 1].circleCenter || 0 }); } else if (type == 4) { //out_text let xy = getAbsoluteXY(item) op.frontData.push({ type: 3, name: objs1[idx - 1].name, FontFamily: item.get('fontFamily'), DefaultText: item.get('text'), FontBackgroudColor: item.get('textBackgroundColor'), FontSize: parseInt(item.get('fontSize')), FontColor: item.get('fill'), Exceed: objs1[idx - 1].exceed, AutoWrap: objs1[idx - 1].autoWrap, FixSize: objs1[idx - 1].fixSize, Bold: item.get('fontWeight') == 'bold', Underline: item.get('underline'), Italic: item.get('fontStyle') == 'italic', Strikeout: item.get('linethrough'), x: parseInt((xy[0] - background_image1.left).toFixed(0)), y: parseInt((xy[1] - background_image1.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)), align: objs1[idx - 1].align }) } else if (type == 8) { //二维码 let xy = getAbsoluteXY(item) op.frontData.push({ type: 4, name: objs1[idx - 1].name, FontColor: objs1[idx - 1].color, DefaultText: objs1[idx - 1].val, x: parseInt((xy[0] - background_image1.left).toFixed(0)), y: parseInt((xy[1] - background_image1.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)) }) } else if (type == 9) { //条形码 let xy = getAbsoluteXY(item) op.frontData.push({ type: 5, name: objs1[idx - 1].name, FontSize: parseInt(objs1[idx - 1].fontSize), DefaultText: objs1[idx - 1].val, HideText: objs1[idx - 1].show, FontFamily: objs1[idx - 1].font, FontColor: objs1[idx - 1].color, x: parseInt((xy[0] - background_image1.left).toFixed(0)), y: parseInt((xy[1] - background_image1.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)) }) } else if (type == 10) { let xy = getAbsoluteXY(item) op.frontData.push({ type: 6, name: objs1[idx - 1].name, FontFamily: item.get('fontFamily'), FontSize: parseInt(item.get('fontSize')), FontColor: item.get('fill'), start: objs1[idx - 1].start, add: parseInt(objs1[idx - 1].add), x: parseInt((xy[0] - background_image1.left).toFixed(0)), y: parseInt((xy[1] - background_image1.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)) }) } } //obj2 for (let item of _objs2) { let idx = getIndex(item, canvas2) if (idx == 0) continue //background-image let type = objs2[idx - 1].type if (type == 2) { //out_pic let xy = getAbsoluteXY(item) op.backData.push({ type: 1, name: objs2[idx - 1].name, FontColor: objs2[idx - 1].black ? '#000000' : '@', transparency: item.get('opacity'), x: parseInt((xy[0] - background_image2.left).toFixed(0)), y: parseInt((xy[1] - background_image2.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)) }) } else if (type == 3) { //text /*2022-07-26 黑色并入黑色图层 if(item.get("fill") == "#000000"){ //纯黑 op.backData.push({ "type" : 2, "Text" : item.get("text"), "FontFamily" : item.get("fontFamily"), "FontSize" : parseInt(item.get("fontSize")), "FontColor" : "#000000", "FontBackgroudColor" : item.get("textBackgroundColor"), "Bold" : item.get("fontWeight") == "bold", "Underline" : item.get("underline"), "Italic" : item.get("fontStyle") == "italic", "Strikeout" : item.get("linethrough"), "x" : parseInt((item.get("left") - background_image2.left).toFixed(0)), "y" : parseInt((item.get("top") - background_image2.top).toFixed(0)), "h" : parseInt((item.get("scaleY") * item.get("height")).toFixed(0)), "w" : parseInt((item.get("scaleX") * item.get("width")).toFixed(0)), "rotate" : parseInt(item.get("angle").toFixed(0)) }); } */ } else if (type == 11) { //circle_text op.backData.push({ "type": 2, "Text": item.get("text"), "FontFamily": item.get("fontFamily"), "FontSize": parseInt(item.get("fontSize")), "FontColor": "#000000", "FontBackgroudColor": item.get("textBackgroundColor"), "Bold": item.get("fontWeight") == "bold", "Underline": item.get("underline"), "Italic": item.get("fontStyle") == "italic", "Strikeout": item.get("linethrough"), "x": parseInt((item.get("left") - background_image2.left).toFixed(0)), "y": parseInt((item.get("top") - background_image2.top).toFixed(0)), "h": parseInt((item.get("scaleY") * item.get("height")).toFixed(0)), "w": parseInt((item.get("scaleX") * item.get("width")).toFixed(0)), "rotate": parseInt(item.get("angle").toFixed(0)), "circleCenter": objs2[idx - 1].circleCenter || 0 }); } else if (type == 4) { let xy = getAbsoluteXY(item) op.backData.push({ type: 3, name: objs2[idx - 1].name, FontFamily: item.get('fontFamily'), DefaultText: item.get('text'), FontSize: parseInt(item.get('fontSize')), FontBackgroudColor: item.get('textBackgroundColor'), FontColor: item.get('fill'), Exceed: objs2[idx - 1].exceed, AutoWrap: objs2[idx - 1].autoWrap, FixSize: objs2[idx - 1].fixSize, Bold: item.get('fontWeight') == 'bold', Underline: item.get('underline'), Italic: item.get('fontStyle') == 'italic', Strikeout: item.get('linethrough'), x: parseInt((xy[0] - background_image2.left).toFixed(0)), y: parseInt((xy[1] - background_image2.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)), align: objs2[idx - 1].align }) } else if (type == 8) { //二维码 let xy = getAbsoluteXY(item) op.backData.push({ type: 4, name: objs2[idx - 1].name, FontColor: objs2[idx - 1].color, DefaultText: objs2[idx - 1].val, x: parseInt((xy[0] - background_image2.left).toFixed(0)), y: parseInt((xy[1] - background_image2.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)) }) } else if (type == 9) { let xy = getAbsoluteXY(item) op.backData.push({ type: 5, name: objs2[idx - 1].name, DefaultText: objs2[idx - 1].val, HideText: objs1[idx - 1].show, FontSize: parseInt(objs2[idx - 1].fontSize), FontFamily: objs2[idx - 1].font, FontColor: objs2[idx - 1].color, x: parseInt((xy[0] - background_image2.left).toFixed(0)), y: parseInt((xy[1] - background_image2.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)) }) } else if (type == 10) { let xy = getAbsoluteXY(item) op.backData.push({ type: 6, name: objs2[idx - 1].name, FontFamily: item.get('fontFamily'), FontSize: parseInt(item.get('fontSize')), FontColor: item.get('fill'), start: parseInt(objs2[idx - 1].start), add: parseInt(objs2[idx - 1].add), x: parseInt((xy[0] - background_image2.left).toFixed(0)), y: parseInt((xy[1] - background_image2.top).toFixed(0)), h: parseInt((item.get('scaleY') * item.get('height')).toFixed(0)), w: parseInt((item.get('scaleX') * item.get('width')).toFixed(0)), rotate: parseInt(item.get('angle').toFixed(0)) }) } } _save(op, callback) //return false 开启该代码可禁止点击该按钮关闭 }) }) } $('#output').on('click', function () { output(null, saveAs) }) var _keyctrl = false, _keyc = false, _textEdit = false, _inputEdit = false $('input').focus(function () { _inputEdit = true }) $('input').blur(function () { _inputEdit = false }) $('textarea').focus(function () { _inputEdit = true }) $('textarea').blur(function () { _inputEdit = false }) $('body').keyup(function (event) { // 兼容三端的 modifier key 检测(Windows/Linux: Ctrl, Mac: Cmd/Meta) if (event.keyCode == 17 || event.keyCode == 91 || event.keyCode == 93) { // Ctrl 键或 Meta 键(Mac Cmd)释放 _keyctrl = false } if (event.keyCode == 67) { _keyc = false } }) $('body').keydown(function (event) { // 统一的快捷键处理(兼容 Windows/Linux: Ctrl, Mac: Cmd/Meta) const isModifierKey = event.ctrlKey || event.metaKey; // 兼容三端 // 快捷键处理(在输入框或文本区域中时不触发,除了删除相关的) if (!_inputEdit && !_textEdit) { // CTRL/CMD+N - 新建 if (isModifierKey && event.keyCode == 78) { event.preventDefault(); $('#new').click(); return; } // CTRL/CMD+O - 打开 if (isModifierKey && event.keyCode == 79) { event.preventDefault(); $('#open').click(); return; } // CTRL/CMD+S - 保存 if (isModifierKey && !event.shiftKey && event.keyCode == 83) { event.preventDefault(); $('#save').click(); return; } // CTRL/CMD+SHIFT+S - 保存副本 if (isModifierKey && event.shiftKey && event.keyCode == 83) { event.preventDefault(); output(null, saveAs); return; } // CTRL/CMD+Z - 还原 if (isModifierKey && !event.shiftKey && event.keyCode == 90) { event.preventDefault(); $('#previous').click(); return; } // CTRL/CMD+Y - 重做 if (isModifierKey && !event.shiftKey && event.keyCode == 89) { event.preventDefault(); $('#next').click(); return; } // CTRL/CMD+C - 复制 if (isModifierKey && event.keyCode == 67 && event.target.nodeName == 'BODY') { event.preventDefault(); copySelection(); return; } // CTRL/CMD+V - 粘贴 if (isModifierKey && event.keyCode == 86 && event.target.nodeName == 'BODY') { event.preventDefault(); pasteFromClipboard(); return; } // CTRL/CMD+SHIFT+DEL - 清空 if (isModifierKey && event.shiftKey && event.keyCode == 46) { event.preventDefault(); $('#clean').click(); return; } // CTRL/CMD+DEL - 删除 if (isModifierKey && !event.shiftKey && event.keyCode == 46) { event.preventDefault(); $('#delete').click(); return; } } // 兼容三端的 modifier key 检测(Windows/Linux: Ctrl, Mac: Cmd/Meta) if (event.keyCode == 17 || event.metaKey) { // Ctrl 键或 Meta 键(Mac Cmd) _keyctrl = true if (_keyc && _textEdit) { //同时按下且在编辑状态 setTimeout("clipboard.writeText( (clipboard.readText()+' ') );", 1) //clipboard.writeText((clipboard.readText()+' ')) } } if (event.keyCode == 67) { _keyc = true if (_keyctrl & _textEdit) { //同时按下且在编辑状态 setTimeout("clipboard.writeText( (clipboard.readText()+' ') );", 1) //clipboard.writeText((clipboard.readText()+' ')) } } if (event.keyCode == 37) { //left let v = -1 if (_keyctrl || event.ctrlKey || event.metaKey) { v = -5 } if (canvas.getActiveObject()) { let x = canvas.getActiveObject().get('left') x += v canvas.getActiveObject().set('left', x) canvas.renderAll() recordState() updateControls() } } if (event.keyCode == 38) { //up let v = -1 if (_keyctrl || event.ctrlKey || event.metaKey) { v = -5 } if (canvas.getActiveObject()) { let y = canvas.getActiveObject().get('top') y += v canvas.getActiveObject().set('top', y) canvas.renderAll() recordState() updateControls() } } if (event.keyCode == 39) { //right let v = 1 if (_keyctrl || event.ctrlKey || event.metaKey) { v = 5 } if (canvas.getActiveObject()) { let x = canvas.getActiveObject().get('left') x += v canvas.getActiveObject().set('left', x) canvas.renderAll() recordState() updateControls() } } if (event.keyCode == 40) { //down let v = +1 if (_keyctrl || event.ctrlKey || event.metaKey) { v = 5 } if (canvas.getActiveObject()) { let y = canvas.getActiveObject().get('top') y += v canvas.getActiveObject().set('top', y) canvas.renderAll() recordState() updateControls() } } // 普通删除键(DEL键,没有按CTRL/CMD) if (event.keyCode == 46 && !isModifierKey && !_inputEdit && !_textEdit) { let actObjs = canvas.getActiveObjects() let allObjs = canvas.getObjects() let flag = 0 for (let item of actObjs) { if (item != allObjs[0]) { let i = getIndex(item, canvas) //0是背景,所以从1开始,但objs不包含背景图片,故要-1 objs.splice(i - 1, 1) canvas.remove(item) flag = 1 } } updateList() canvas.discardActiveObject() if (flag == 1) { recordState() canvas.renderAll() handleObjectSelected() } } }) // 复制选中对象 function copySelection() { const activeObjects = canvas.getActiveObjects(); if (!activeObjects || activeObjects.length === 0) { layer.msg("未选中任何对象") return; } // 存储原始对象用于克隆 clipboardData.objects = activeObjects; } // 粘贴对象 function pasteFromClipboard() { if (!clipboardData.objects || clipboardData.objects.length === 0) { layer.msg("剪贴板为空") return; } // 克隆所有对象 fabric.util.enlivenObjects( clipboardData.objects.map(obj => obj.toObject()), clonedObjects => { // 设置新位置(添加偏移) clonedObjects.forEach((clone, index) => { let idx = getIndex(clipboardData.objects[index], canvas) clone.set({ left: clone.left + clipboardData.offset, top: clone.top + clipboardData.offset, evented: true }); // 添加到画布 canvas.add(clone); let tobj = { ...objs[idx - 1] } if (tobj.name) tobj.name = tobj.name + objs.length objs.push(tobj); canvas.renderAll(); updateList(); recordState(); }); // 选中粘贴的对象 canvas.discardActiveObject(); const sel = new fabric.ActiveSelection(clonedObjects, { canvas: canvas }); canvas.setActiveObject(sel); canvas.requestRenderAll(); selectObj(objs.length - 1); // 增加下次偏移 clipboardData.offset += 10; } ); }