// design1核心功能模块 // 注意:此代码在layui.use回调函数内部执行,不需要$(function())包装 // 全局变量已在主入口文件中定义,这里只需要初始化 $("#object_attribute").css("height", "470px"); $("#obj_list").css("height", document.body.clientHeight - 520); $(".obj_list").css("height", $("#obj_list").height() - 32); $(".main").show(); $(".canvas_box").css("height", document.body.clientHeight - 100); zoom = $(".canvas_bg_img").width() / 2787 $("#canvas1").attr("width", $(".canvas_box").width() / zoom); $("#canvas1").attr("height", $(".canvas_box").height() / zoom); $("#canvas2").attr("width", $(".canvas_box").width() / zoom); $("#canvas2").attr("height", $(".canvas_box").height() / zoom); // 优化性能:在创建 Fabric Canvas 之前,先设置 canvas context 的 willReadFrequently // 注意:这需要在 Fabric.js 创建 canvas 之前执行 try { const canvas1El = document.getElementById('canvas1'); const canvas2El = document.getElementById('canvas2'); if (canvas1El) { // 先获取 context 并设置 willReadFrequently,这样 Fabric.js 创建时会复用这个 context canvas1El.getContext('2d', { willReadFrequently: true }); } if (canvas2El) { canvas2El.getContext('2d', { willReadFrequently: true }); } } catch (e) { // 如果浏览器不支持 willReadFrequently,忽略错误 } // 初始化Canvas对象 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(0, 0), 1); canvas2.zoomToPoint(new fabric.Point(0, 0), 1); // 初始化标尺 initRulers(); $("#source_front").width(zoom * $("#source_front").width()); $("#source_back").width(zoom * $("#source_back").width()); $(".canvas-container:eq(0)").css('transform', 'scale(' + zoom + ')') $(".canvas-container:eq(1)").css('transform', 'scale(' + zoom + ')') $(".canvas-container:eq(0)").css('width', 0) $(".canvas-container:eq(1)").hide(); canvas = canvas1; fabric.Object.prototype.set({ borderColor: "#9013FE", cornerColor: "#9013FE", //激活状态角落图标的填充颜色 cornerStrokeColor: "#9013FE", //激活状态角落图标的边框颜色 borderOpacityWhenMoving: 1, borderScaleFactor: 6, cornerSize: 24, cornerStyle: "circle", //rect,circle centeredScaling: false, //角落放大缩小是否是以图形中心为放大原点 centeredRotation: true, //旋转按钮旋转是否是左上角为圆心旋转 transparentCorners: false, //激活状态角落的图标是否透明 rotatingPointOffset: 20, //旋转距旋转体的距离 padding: 0, }); canvas1.preserveObjectStacking = true; canvas2.preserveObjectStacking = true; 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.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();//重新渲染画布 // 立即启动事件初始化(必须在 slider 初始化之前) initCanvasEvents(); var zoomSlider = slider.render({ elem: '#zoomSlide', //绑定元素 min: 25, max: 500, value: 100, change(val) { console.log(val, zoomSlider) canvas.zoomToPoint( new fabric.Point(0, 0), val / 100 ); // zoom = val / 100; //$(".canvas-container").css("transform","scale(" + val / 100 + ")") } }); // =========================================================== // Slider 滑块初始化 // =========================================================== var circle_text_size = slider.render({ elem: '#circle_text_size', min: 10, max: 120, change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set("fontSize", value); canvas.renderAll(); } } }); var circle_text_diameter = slider.render({ elem: '#circle_text_diameter', min: 500, max: 4000, change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set("diameter", value); canvas.renderAll(); } } }); var out_pic_op = slider.render({ elem: '#out_pic_op', change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set('opacity', (100 - value) / 100); canvas.renderAll(); } } }); var pic_op = slider.render({ elem: '#pic_op', change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set("opacity", (100 - value) / 100); canvas.renderAll(); } } }); var rect_op = slider.render({ elem: '#rect_op', change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set("opacity", (100 - value) / 100); canvas.renderAll(); } } }); var circle_op = slider.render({ elem: '#circle_op', change: function (value) { if (canvas.getActiveObject()) { canvas.getActiveObject().set("opacity", (100 - value) / 100); canvas.renderAll(); } } }); 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 + '_1.png', function (i1) { i1.left = background_image.left; i1.top = background_image.top; let img1 = i1; fabric.Image.fromURL('./public/images/back_bg_1.png', function (i2) { i2.left = background_image.left; i2.top = background_image.top; let img2 = i2; fabric.Image.fromURL('./public/images/op_1.png', function (i3) { i2.left = background_image.left; i2.top = background_image.top; let img3 = i3; display_func(img1, img2, img3); }) }) }) }); // 导出文件 async function saveImageAsPNG(buffer) { let fs = require('fs'); // 弹出保存对话框 const { canceled, filePath } = await dialog.showSaveDialog({ title: '保存图片', defaultPath: path.join(exePath, 'output.png'), // 默认路径 filters: [{ name: 'PNG Images', extensions: ['png'] }] // 文件过滤器 }); 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(); $("#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;//预览图顶裁剪 let list1 = [..._objs1] for (let item of list1) { 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())); } g1.push(fabric.util.object.clone(item)); continue;//黑色文字 黑色矩形 黑色线条 黑色圆形 } else if (idx == 0) { g1.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: 2787, width: 2787, top: all_top, left: all_left }); g3.push(img1); let url3 = new fabric.Group(g3).toDataURL({ height: 2787, width: 2787, top: all_top, left: all_left }); let url5 = new fabric.Group(g5).toDataURL({ height: 2787, width: 2787, top: black_top, left: black_left }); 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())); } g2.push(fabric.util.object.clone(item)); continue;//黑色文字 黑色矩形 黑色线条 黑色圆形 } else if (idx == 0) { g2.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: 2787, width: 2787, top: top, left: left }); let white_url1 = canvas1.getObjects()[0].toDataURL({ height: 2787, width: 2787, }); g4.push(img2); let url4 = new fabric.Group(g4).toDataURL({ height: 2787, width: 2787, top: all_top, left: all_left }); let url6 = new fabric.Group(g6).toDataURL({ height: 2787, width: 2787, top: black_top, left: black_left }); 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 buffer = Buffer.from(url3.replace(/^data:image\/\w+;base64,/, ""), 'base64'); const printPath = path.join(exePath, 'print.png'); fs.writeFileSync(printPath, buffer); layer.open({ type: 1, area: [w, h], title: language_str("display"), //不显示标题栏"预览" shadeClose: true, //点击遮罩关闭 content: `
`, btn: [language_str("output"), "打印"],//'导出' btn1: function (index, layero) { saveImageAsPNG(buffer) printJS({ printable: printPath, type: 'image', style: 'img { width: 100%; height: auto; }' }) } }); } function output(callback = null, _save = save) { // 类型改变 fabric.Image.fromURL('./public/images/op_1.png', function (i1) { i1.left = background_image.left; i1.top = background_image.top; let img1 = i1; fabric.Image.fromURL('./public/images/op_1.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(); $("#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); 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 //勾选并入黑色图层 ) ) { 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())); } g1.push(fabric.util.object.clone(item)); continue;//黑色文字 黑色矩形 黑色线条 黑色圆形 } else if (idx == 0) { g1.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: 2787, width: 2787, top: all_top, left: all_left }); g3.push(img1); let url3 = new fabric.Group(g3).toDataURL({ height: 2787, width: 2787, top: all_top, left: all_left }); let url5 = new fabric.Group(g5).toDataURL({ height: 2787, width: 2787, top: black_top, left: black_left }); 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())); } g2.push(fabric.util.object.clone(item)); continue;//黑色文字 黑色矩形 黑色线条 黑色圆形 } else if (idx == 0) { g2.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: 2787, width: 2787, top: top, left: left }); let white_url1 = canvas1.getObjects()[0].toDataURL({ height: 2787, width: 2787, }); g4.push(img2); let url4 = new fabric.Group(g4).toDataURL({ height: 2787, width: 2787, top: all_top, left: all_left }); let url6 = new fabric.Group(g6).toDataURL({ height: 2787, width: 2787, top: black_top, left: black_left }); // let white_url2 = canvas2.getObjects()[0].toDataURL({ // height: 2787, // width: 2787, // }); //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: '' }; 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 = ''; //op.frontBlPic = url5; //op.backBlPic = url6; op.frontBlackPic = ''; op.backBlackPic = ''; 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) { } else if (type == 11) { 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 }); } 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) { } else if (type == 11) { 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": objs2[idx - 1].circleCenter }); } 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); }); function addBackground() { // 类型改变 fabric.Image.fromURL('./public/images/bg_front_1.png', function (image) { let width = $("#canvas1").width() 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) { image.scale(1, 1); background_image1 = image; background_image = background_image1; canvas.add(image); canvas.renderAll(); //pre_objs1.push(JSON.stringify(objs1)); //pre_json1.push(canvas1.toJSON(["selectable","hoverable","hoverCursor"])); recordObjs1.push(JSON.stringify(objs1)); recordJson1.push(canvas1.toJSON(["selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline"])); }); // 类型改变 fabric.Image.fromURL('./public/images/bg_back.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(); //pre_objs2.push(JSON.stringify(objs2)); //pre_json2.push(canvas2.toJSON(["selectable","hoverable","hoverCursor"])); recordObjs2.push(JSON.stringify(objs2)); recordJson2.push(canvas2.toJSON(["selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline"])); let file = GetFile().get('file') console.log(file) if (file && file != 'empty') { open(file) } }); }); is_bgi_add = 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(); //$("#text_x").val((target.left - background_image.left).toFixed(1)); //$("#text_y").val((target.top - background_image.top).toFixed(1)); //$("#text_w").val(parseFloat(target.get("scaleX") * target.get("width")).toFixed(1)); //$("#text_h").val(parseFloat(target.get("scaleY") * target.get("height")).toFixed(1)); //$("#text_r").val(target.get("angle").toFixed(0)); } 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(); //OpenDialog(); //function OpenDialog() //{ // dialog.showOpenDialog({ // title: "请选择图片", // buttonLabel: "确认", // filters: [ // { name: 'Image File Type', extensions: ['png', 'jpg'] }, // ] // }).then(result => { // if(result.canceled)return; // //console.log(result.filePaths) // fabric.Image.fromURL(result.filePaths[0], function(image) { // image.left = pointer.x; // image.top = pointer.y; // // canvas.add(image); // objs.push({type: 1}); // canvas.renderAll(); // updateList(); // recordState(); // } ); // }).catch(err => { // console.log(err) // }) //} } 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 + "%"); } let idx = getIndex(canvas.getActiveObject()); // $("#pic_black").prop("checked", objs[idx-1].black ? 'checked' : ''); $("#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 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.on("selected",function(){//test well // console.log(222); //}); image.setSrc(image.toDataURL(), function (image) { canvas.add(image); objs.push({ type: 2, name: resName("OUT_PIC_"), black: false }); canvas.renderAll(); updateList(); recordState(); }); }); } 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()); $("#out_pic_field_name").val(objs[idx - 1].name); // $("#out_pic_black").prop("checked", objs[idx-1].black ? 'checked' : ''); $("#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 addText(pointer) { let text = new fabric.Text('TEXT', {//源:文本 left: pointer.x, top: pointer.y, fill: "#000000", textBackgroundColor: "", fontStyle: "normal", fontWeight: "normal", fontSize: 100, 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 selectText(target, index) { //console.log(canvas.getActiveObject().get("fontFamily")); $("#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 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: 100, underline: false, linethrough: false, lockScalingX: true, lockScalingY: true, fontFamily: 'Arial', originX: 'center', // originY: 'center', angle: angleDeg, flipped: pointer.y > canvas.height / 2 ? true : false }); // distance = getDisdance(pointer.x+text.width/2, pointer.y, canvas.width/2,canvas.height/2) // text.set({diameter: distance*2}) 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 }); canvas.renderAll(); updateList(); recordState(); } function selectCircleText(target, index) { let idx = getIndex(canvas.getActiveObject()); $("#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.setValue(canvas.getActiveObject().get("fontSize") - 10); } else { $("#circle_text_size").next().children(".layui-slider-bar").css("width", ((canvas.getActiveObject().get("fontSize") - 10 - 10) / (120 - 10)) * 100 + "%"); $("#circle_text_size").next().children(".layui-slider-handle").css("left", ((canvas.getActiveObject().get("fontSize") - 10 - 10) / (120 - 10)) * 100 + "%"); } if (typeof circle_text_diameter !== 'undefined' && circle_text_diameter.setValue) { circle_text_diameter.setValue(canvas.getActiveObject().get("diameter") - 500); } else { $("#circle_text_diameter").next().children(".layui-slider-bar").css("width", ((canvas.getActiveObject().get("diameter") - 500 - 500) / (4000 - 500)) * 100 + "%"); $("#circle_text_diameter").next().children(".layui-slider-handle").css("left", ((canvas.getActiveObject().get("diameter") - 500 - 500) / (4000 - 500)) * 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 addOutText(pointer) { //MERGE_TEXT_1 let text = new fabric.Text("MERGE TEXT", {//源:合成文本 left: pointer.x, top: pointer.y, fill: "#000000", fontStyle: "normal", fontWeight: "normal", fontSize: 100, textBackgroundColor: "", underline: false, linethrough: false, fontFamily: 'Arial', textAlign: 'center' }); //text.on({ // 'scaling': function(e) { // var obj = this, // w = obj.width * obj.scaleX, // h = obj.height * obj.scaleY; // // obj.set({ // 'height' : h, // 'width' : w, // 'scaleX' : 1, // 'scaleY' : 1 // }); // } //}); 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 selectOutText(target, index) { //color //size 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()); $("#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 addRect(pointer) { let rect = new fabric.Rect({ left: pointer.x, top: pointer.y, fill: '', stroke: "#ff0000", strokeWidth: 10, width: 300, height: 300 }); canvas.add(rect); objs.push({ type: 5, black: false }); canvas.renderAll(); updateList(); recordState(); } function selectRect(target, index) { //color $("#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 + "%"); } let a = canvas.getActiveObject().getCoords(); console.log(getCoordsMinX(a)); $("#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 addCircle(pointer) { let circle = new fabric.Circle({ left: pointer.x, top: pointer.y, fill: '', stroke: '#ff0000', strokeWidth: 10, radius: 150, strokeUniform: true, }); canvas.add(circle); objs.push({ type: 6, black: false }); canvas.renderAll(); updateList(); recordState(); } function selectCircle(target, index) { //border-color //color $("#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 addLine(pointer) { let line = new fabric.Line([100, 200, 400, 200], { left: pointer.x, top: pointer.y, stroke: '#ff0000', strokeWidth: 10, 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 selectLine(target, index) { //color //weight $("#line_control").show(); let idx = getIndex(canvas.getActiveObject()); $("#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 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; image.scale(2.4, 2.4) canvas.add(image); objs.push({ type: 8, name: resName("QR_CODE_"), color: "#000000", val: "https://www.cardsoon.com", black: false }); canvas.renderAll(); updateList(); recordState(); }); } function selectQrCode(target, index) { //字段名 //background-color //color $("#qrcode_Controll").show(); let idx = getIndex(canvas.getActiveObject()); $("#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 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; image.scale(2.4, 2.4) canvas.add(image); objs.push({ type: 9, name: resName("BAR_CODE_"), val: "123456789", color: "#000000", fontSize: 18, font: "Arial", show: false, black: false });//show是取消文字显示 canvas.renderAll(); updateList(); recordState(); }); } function selectBarCode(target, index) { //字段名 //background-color //color $("#barcode_controll").show(); let idx = getIndex(canvas.getActiveObject()); $("#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 addCounter(pointer) { let text = new fabric.Text('0', { left: pointer.x, top: pointer.y, fontSize: 100, fill: "#000000", }); canvas.add(text); objs.push({ type: 10, name: resName("COUNTER_"), start: 0, add: 1, black: false }); canvas.renderAll(); updateList(); recordState(); console.log(canvas.getObjects()) } function selectCounter(target, index) { //起始 //终止 //增量 $("#counter_control").show(); let idx = getIndex(canvas.getActiveObject()); $("#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 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() { //pre_objs.push(JSON.stringify(objs)); //pre_json.push(canvas.toJSON(["selectable","hoverable","hoverCursor","text","fontStyle","fontWeight","underline"])); //next_json = []; //next_objs = []; //recordObjs.push(JSON.stringify(objs)); //recordJson.push(canvas.toJSON(["selectable","hoverable","hoverCursor","text","fontStyle","fontWeight","underline"])); ++step.val; for (let i = step.val; i < recordObjs.length; i++) { recordObjs.pop(); recordJson.pop(); } //recordObjs.splice(step.val, 1, JSON.stringify(objs)) //recordJson.splice(step.val, 1, canvas.toJSON(["selectable","hoverable","hoverCursor","text","fontStyle","fontWeight","underline"])) 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 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); // 圆形文本特殊处理 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(distance * 2 - 500); 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)); } updateRulerGuides(e.e); }); // 2. 鼠标移出 currentCanvas.on("mouse:out", () => { $("#ruler-guide-h").hide(); $("#ruler-guide-v").hide(); }); // 3. 选中处理 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); }); // 4. 鼠标松开 (添加对象) 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", ""); }); // 5. 对象移动 (只保留圆形文本逻辑,去掉了 checkOutOfBounds) 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); 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)) { 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(); }); // ================================================== // 8. 【完美修复版】渲染后绘制:外部蒙版 + 中心圆孔 (修复三角形Bug) // ================================================== currentCanvas.on('after:render', function () { const ctx = currentCanvas.getContext(); let bgObj = (currentCanvas === canvas1) ? background_image1 : background_image2; if (bgObj) { const width = currentCanvas.width; const height = currentCanvas.height; // 1. 计算圆心 const cx = bgObj.left + (bgObj.width * bgObj.scaleX) / 2; const cy = bgObj.top + (bgObj.height * bgObj.scaleY) / 2; // 2. 计算半径 // 外半径 (光盘大小) const r_outer = (bgObj.width * bgObj.scaleX) / 2; // 内半径 (中心孔) const dpi = 600; const mmToPx = dpi / 25.4; let holeDiameterMm = 24; // 默认 15mm // 切换版本判断 (15mm vs 38mm) if (currentCanvas === canvas1 && typeof bg_version !== 'undefined' && bg_version === 2) { holeDiameterMm = 42; } // 比例计算法:确保孔径随缩放适配 // r_inner = r_outer * (孔径 / 光盘标准直径120mm) const r_inner = r_outer * (holeDiameterMm / 120); ctx.save(); ctx.beginPath(); // A. 绘制全屏矩形 (顺时针) ctx.rect(0, 0, width, height); // B. 挖掉外圆 (逆时针) // 【关键修复】画圆前,必须把笔移动到圆的起点 (cx + r, cy) // 否则 Canvas 会画一条线连过来,形成奇怪的三角形 ctx.moveTo(cx + r_outer, cy); ctx.arc(cx, cy, r_outer, 0, Math.PI * 2, true); // C. 填补内圆 (顺时针) // 【关键修复】同理,把笔移动到内圆的起点 ctx.moveTo(cx + r_inner, cy); ctx.arc(cx, cy, r_inner, 0, Math.PI * 2, false); ctx.closePath(); ctx.fillStyle = "rgba(0, 0, 0, 0.9)"; // 90% 遮罩 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(); } }); }); }); } function initRulers() { const dpi = 600; const mmToPx = dpi / 25.4; const rulerHeight = 20; const rulerWidth = 20; const canvasWrapper = $('.canvas-wrapper'); const canvasBox = $('.canvas_box'); // 确保容器定位 if (canvasWrapper.css('position') === 'static') canvasWrapper.css('position', 'relative'); // 初始化尺寸 let canvasWidth = canvasWrapper.width() || 800; let canvasHeight = canvasWrapper.height() || 600; const $rulerH = $('#ruler-h'); const $rulerV = $('#ruler-v'); $rulerH.attr('width', canvasWidth).attr('height', rulerHeight); $rulerV.attr('width', rulerWidth).attr('height', canvasHeight); // --- 1. 绑定拖拽事件 (核心新功能) --- // 定义临时拖拽线 DOM let $dragLine = null; // 通用拖拽开始函数 const startDragGuide = (type, e) => { e.preventDefault(); e.stopPropagation(); // 创建临时显示的线 $dragLine = $('
'); $('.canvas-wrapper').append($dragLine); if (type === 'horizontal') { // 从上往下拖 (水平线) $dragLine.css({ left: 0, width: '100%', height: '1px', top: e.offsetY }); } else { // 从左往右拖 (垂直线) $dragLine.css({ top: 0, height: '100%', width: '1px', left: e.offsetX }); } // 绑定 document 移动事件 $(document).on('mousemove.ruler', function (moveEvent) { let wrapperOffset = canvasWrapper.offset(); if (type === 'horizontal') { let relY = moveEvent.pageY - wrapperOffset.top; $dragLine.css('top', relY + 'px'); } else { let relX = moveEvent.pageX - wrapperOffset.left; $dragLine.css('left', relX + 'px'); } }); // 绑定松开事件 (创建真实的 Fabric 线) $(document).on('mouseup.ruler', function (upEvent) { $(document).off('mousemove.ruler mouseup.ruler'); if (!$dragLine) return; // 获取最终位置 let wrapperOffset = canvasWrapper.offset(); let finalX = upEvent.pageX - wrapperOffset.left; let finalY = upEvent.pageY - wrapperOffset.top; // 移除临时线 $dragLine.remove(); $dragLine = null; // 只有拖入画布区域才创建 if (finalX > rulerWidth && finalY > rulerHeight) { createFabricGuide(type, finalX, finalY); } }); }; // 绑定到标尺 DOM $rulerH.off('mousedown').on('mousedown', (e) => startDragGuide('horizontal', e)); $rulerV.off('mousedown').on('mousedown', (e) => startDragGuide('vertical', e)); // --- 2. 在画布中创建辅助线 --- function createFabricGuide(type, domX, domY) { // 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 = [-lineExtent, canvasY, lineExtent, canvasY]; } else { // 垂直线:从很远的上面到很远的下面 linePoints = [canvasX, -lineExtent, canvasX, lineExtent]; } let guideLine = new fabric.Line(linePoints, { stroke: '#0066FF', strokeWidth: 1 / zoom, opacity: 0.9, shadow: { color: '#0066FF', blur: 6, offsetX: 0, offsetY: 0, affectStroke: true }, selectable: true, evented: true, hasControls: false, hasBorders: false, lockRotation: true, lockScalingX: true, lockScalingY: true, hoverCursor: type === 'horizontal' ? 'ns-resize' : 'ew-resize', perPixelTargetFind: false, // 使用边界框检测而不是像素检测 targetFindTolerance: 10, // 增加点击容差,扩大可点击区域(像素) padding: 10, // 增加选择边界的内边距 isGuideLine: true, ignoreSave: true }); // 限制移动方向 guideLine.on('moving', function (e) { if (type === 'horizontal') { this.left = -lineExtent; } else { this.top = -lineExtent; } }); canvas.add(guideLine); canvas.setActiveObject(guideLine); canvas.renderAll(); } // --- 3. 绘制标尺 (核心绘制循环) --- function updateAndDraw() { // 每次重新获取尺寸,确保使用最新值 // 使用多种方法尝试获取实际尺寸,确保准确 let currentCanvasWidth = canvasWrapper.width() || canvasWrapper.outerWidth() || canvasWrapper[0].getBoundingClientRect().width || 800; let currentCanvasHeight = canvasWrapper.height() || canvasWrapper.outerHeight() || canvasWrapper[0].getBoundingClientRect().height || 600; // 如果还是获取不到,尝试使用父容器的尺寸 if (!currentCanvasWidth || currentCanvasWidth === 0) { const parent = canvasWrapper.parent(); currentCanvasWidth = parent.width() || parent[0].getBoundingClientRect().width || 800; } if (!currentCanvasHeight || currentCanvasHeight === 0) { const parent = canvasWrapper.parent(); currentCanvasHeight = parent.height() || parent[0].getBoundingClientRect().height || 600; } // 更新标尺尺寸 $rulerH.attr('width', currentCanvasWidth).attr('height', rulerHeight); $rulerV.attr('width', rulerWidth).attr('height', currentCanvasHeight); const pxPerMm = zoom * mmToPx; let boxMarginLeft = parseFloat(canvasBox.css('margin-left')) || 0; let boxMarginTop = parseFloat(canvasBox.css('margin-top')) || 0; const rulerHeightOffset = 20; // ruler 的高度 // 原点在标尺的左上角(画布的左上角位置,考虑margin) // 标尺0点 = boxMarginLeft(水平)和 boxMarginTop + rulerHeightOffset(垂直) let originX = boxMarginLeft; let originY = boxMarginTop + rulerHeightOffset; const ctxH = document.getElementById('ruler-h').getContext('2d', { willReadFrequently: true }); const ctxV = document.getElementById('ruler-v').getContext('2d', { willReadFrequently: true }); // 绘制水平标尺 function drawHorizontal() { ctxH.clearRect(0, 0, currentCanvasWidth, rulerHeight); ctxH.strokeStyle = '#666'; ctxH.lineWidth = 1; ctxH.beginPath(); ctxH.moveTo(0, 19); ctxH.lineTo(currentCanvasWidth, 19); ctxH.stroke(); ctxH.fillStyle = '#888'; ctxH.font = '9px Arial'; ctxH.textAlign = 'left'; ctxH.textBaseline = 'top'; const startX = 0; const endX = currentCanvasWidth; // 绘制主要刻度(每10mm) // 从0点向左绘制负值刻度,确保覆盖到startX for (let mm = -10; originX + mm * pxPerMm >= startX; mm -= 10) { const x = originX + mm * pxPerMm; ctxH.beginPath(); ctxH.moveTo(x, 5); ctxH.lineTo(x, 20); ctxH.stroke(); ctxH.fillText(mm + 'mm', x + 2, 2); } // 从0点向右绘制正值刻度,确保覆盖到endX for (let mm = 0; ; mm += 10) { const x = originX + mm * pxPerMm; if (x > endX) break; ctxH.beginPath(); ctxH.moveTo(x, 5); ctxH.lineTo(x, 20); ctxH.stroke(); ctxH.fillText(mm + 'mm', x + 2, 2); } // 绘制次要刻度(每1mm) // 从0点向左绘制负值刻度,确保覆盖到startX for (let mm = -1; originX + mm * pxPerMm >= startX; mm -= 1) { const x = originX + mm * pxPerMm; if (mm % 10 === 0) continue; ctxH.beginPath(); ctxH.moveTo(x, 12); ctxH.lineTo(x, 20); ctxH.stroke(); } // 从0点向右绘制正值刻度,确保覆盖到endX for (let mm = 0; ; mm += 1) { const x = originX + mm * pxPerMm; if (x > endX) break; if (mm % 10 === 0) continue; ctxH.beginPath(); ctxH.moveTo(x, 12); ctxH.lineTo(x, 20); ctxH.stroke(); } } // 绘制垂直标尺 function drawVertical() { ctxV.clearRect(0, 0, rulerWidth, currentCanvasHeight); ctxV.strokeStyle = '#666'; ctxV.lineWidth = 1; ctxV.beginPath(); ctxV.moveTo(19, 0); ctxV.lineTo(19, currentCanvasHeight); ctxV.stroke(); ctxV.fillStyle = '#888'; ctxV.font = '9px Arial'; ctxV.textAlign = 'center'; ctxV.textBaseline = 'middle'; const startY = 0; const endY = currentCanvasHeight; // 绘制主要刻度(每10mm) // 从0点向上绘制负值刻度,确保覆盖到startY for (let mm = -10; originY + mm * pxPerMm >= startY; mm -= 10) { const y = originY + mm * pxPerMm; ctxV.beginPath(); ctxV.moveTo(5, y); ctxV.lineTo(20, y); ctxV.stroke(); ctxV.save(); ctxV.translate(8, y + 2); ctxV.rotate(-Math.PI / 2); ctxV.fillText(mm + 'mm', 0, 0); ctxV.restore(); } // 从0点向下绘制正值刻度,确保覆盖到endY for (let mm = 0; ; mm += 10) { const y = originY + mm * pxPerMm; if (y > endY) break; ctxV.beginPath(); ctxV.moveTo(5, y); ctxV.lineTo(20, y); ctxV.stroke(); ctxV.save(); ctxV.translate(8, y + 2); ctxV.rotate(-Math.PI / 2); ctxV.fillText(mm + 'mm', 0, 0); ctxV.restore(); } // 绘制次要刻度(每1mm) // 从0点向上绘制负值刻度,确保覆盖到startY for (let mm = -1; originY + mm * pxPerMm >= startY; mm -= 1) { const y = originY + mm * pxPerMm; if (mm % 10 === 0) continue; ctxV.beginPath(); ctxV.moveTo(12, y); ctxV.lineTo(20, y); ctxV.stroke(); } // 从0点向下绘制正值刻度,确保覆盖到endY for (let mm = 0; ; mm += 1) { const y = originY + mm * pxPerMm; if (y > endY) break; if (mm % 10 === 0) continue; ctxV.beginPath(); ctxV.moveTo(12, y); ctxV.lineTo(20, y); ctxV.stroke(); } } drawHorizontal(); drawVertical(); } canvas1.on('after:render', updateAndDraw); canvas2.on('after:render', updateAndDraw); $(window).resize(() => setTimeout(updateAndDraw, 100)); updateAndDraw(); } // 辅助函数:更新光标位置 function updateRulerGuides(nativeEvent) { if (!nativeEvent) return; const wrapper = $('.canvas-wrapper'); const offset = wrapper.offset(); if (!offset) return; $("#ruler-guide-h").css({ left: (nativeEvent.pageX - offset.left) + 'px', display: 'block' }); $("#ruler-guide-v").css({ top: (nativeEvent.pageY - offset.top) + 'px', display: 'block' }); } function copySelection() { const activeObjects = canvas.getActiveObjects(); if (!activeObjects || activeObjects.length === 0) { layer.msg(language_str("selectPic") || "请先选择对象"); return; } // 【核心修复】 // 遍历选中的对象,同时保存它的 Fabric 画面数据 和 我们的 objs 业务数据 clipboardData.data = activeObjects.map(obj => { let idx = getIndex(obj); // 获取对象在画布中的索引 // 获取业务属性 (objs数组中对应的那一项) // 注意:idx=0 是背景图,objs数组是从下标0开始存第1个对象的,所以是 idx-1 let appData = (idx > 0 && objs[idx - 1]) ? objs[idx - 1] : { type: 0 }; return { // 1. 保存画面样式 (坐标、颜色、字号等) fabricJson: obj.toObject(['id', 'name', 'selectable', 'evented', 'data']), // 2. 保存业务属性 (深拷贝,防止引用冲突) appData: JSON.parse(JSON.stringify(appData)) }; }); clipboardData.offset = 10; // 重置偏移量 console.log(`已复制 ${activeObjects.length} 个对象`); layer.msg("已复制"); } function pasteFromClipboard() { // 检查是否有数据 if (!clipboardData.data || clipboardData.data.length === 0) { // layer.msg("剪贴板为空"); return; } // 1. 提取 Fabric JSON 准备恢复画面 const jsonToEnliven = clipboardData.data.map(item => item.fabricJson); // 2. 恢复对象 (异步过程) fabric.util.enlivenObjects(jsonToEnliven, function (objects) { canvas.discardActiveObject(); objects.forEach((clone, index) => { // --- A. 处理画面位置 --- clone.set({ left: clone.left + clipboardData.offset, top: clone.top + clipboardData.offset, evented: true }); // 如果是组内对象,需要特殊处理坐标 if (clone.group) { clone.set({ left: clone.group.left + clipboardData.offset, top: clone.group.top + clipboardData.offset }); } // --- B. 处理业务数据 --- // 取出复制时保存的 appData let savedAppData = clipboardData.data[index].appData; let newAppData = JSON.parse(JSON.stringify(savedAppData)); // 再次深拷贝 // 修改名称,防止 ID 冲突 (例如 TEXT_1 -> TEXT_1_copy) if (newAppData.name) { newAppData.name = newAppData.name + "_copy"; } // --- C. 添加到画布和数据数组 --- canvas.add(clone); objs.push(newAppData); // 将新业务数据推入当前页面的 objs 数组 }); // 3. 选中新粘贴出来的对象 let sel = new fabric.ActiveSelection(objects, { canvas: canvas }); canvas.setActiveObject(sel); canvas.requestRenderAll(); // 4. 刷新列表和历史记录 updateList(); recordState(); // 5. 高亮左侧列表最后一项 (即新粘贴的项) if (objs.length > 0) selectObj(objs.length - 1); // 6. 增加下次粘贴的偏移量 (让用户知道粘贴了新东西) clipboardData.offset += 10; }); } function onStateRestored() { // 1. 重新获取背景图引用 (因为旧对象被销毁了) // 注意:canvas 是全局变量,指向当前激活的画板 if (canvas.getObjects().length > 0) { background_image = canvas.getObjects()[0]; background_image.selectable = false; // 确保背景不可选 background_image.evented = false; } // 2. 渲染画布 canvas.requestRenderAll(); // 3. 取消选中状态 selectObj(-1); // 4. 隐藏所有属性面板 $("#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(); // 5. 刷新左侧列表 updateList(); }