Files
SoonDesign/lib/design1/core.js
T
2025-12-10 01:40:03 +08:00

2892 lines
116 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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("<option value='" + item + "'>" + item + "</option>");
$("#circle_text_font_family").append("<option value='" + item + "'>" + item + "</option>");
$("#out_text_font_family").append("<option value='" + item + "'>" + item + "</option>");
$("#barcode_font_family").append("<option value='" + item + "'>" + item + "</option>");
$("#count_font_family").append("<option value='" + item + "'>" + item + "</option>");
}
});
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('<font style="color:black">' + language_str("whetherSave") + '</font>', {//是否保存当前文件?
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: `
<div style="padding:20px;background-color:#1E252D;height:${h1};display:flex;align-items: center;overflow:hidden;">
<div style="display: flex;justify-content: center;height: 100%;width: 100%;">
<div class="rotate-div" id="displayImg1" angle="0">
<img src="${printPath}" style="max-width:100%;max-height:100%;"/>
<div class="rotate-wrap" onclick="window.rotate('displayImg1')">
<img src="./public/images/rotate.png" style="height: 64px;width: 64px"/>
</div>
</div>
</div>
</div>`,
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 = `<div style="${rowStyle}">
<div style="${leftStyle}">
<img src="public/images/${iconName}" id="obj${i}" style="width:16px; margin-right:5px;">
<span title="${nameText}">${nameText}</span>
</div>
<span style="${indexStyle}">${displayIndex}</span>
</div>` + 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 = $('<div class="guide-line-drag"></div>');
$('.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();
}