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