This commit is contained in:
24kycj
2025-12-06 00:26:57 +08:00
parent ff5c511eca
commit bcd5a1bd5e
4 changed files with 836 additions and 132 deletions
+346 -51
View File
@@ -15,6 +15,45 @@
height: 2787px;
border-radius: 50%;
}
/* 标尺样式 */
.ruler-container {
position: relative;
background-color: #2C2C2C;
border: 1px solid #1E1E1E;
overflow: hidden;
}
.ruler-horizontal {
position: absolute;
top: 0;
left: 0;
height: 20px;
background-color: #2C2C2C;
border-bottom: 1px solid #1E1E1E;
z-index: 1000;
}
.ruler-vertical {
position: absolute;
top: 0;
left: 0;
width: 20px;
background-color: #2C2C2C;
border-right: 1px solid #1E1E1E;
z-index: 1000;
}
.ruler-corner {
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
background-color: #2C2C2C;
border-right: 1px solid #1E1E1E;
border-bottom: 1px solid #1E1E1E;
z-index: 1001;
}
.canvas-wrapper {
position: relative;
}
</style>
</head>
@@ -195,7 +234,7 @@
</select>
</div>
<div class="component-line">
<span class="name" language="m" zh="自动圆心" ozh="自動圆心" en="Automatic center:"></span>
<span class="name" language="m" zh="中心辅助调整圆弧" ozh="中心輔助調整圓弧" en="Adjust the arc using the center:"></span>
<input type="checkbox" name="circle_text_center" id="circle_text_center" class="radio">
</div>
<div class="component-line">
@@ -575,9 +614,14 @@
</div>
<div class="right" style="background-color: #1E252D">
<div style="background-color: #1E252D;" id="canvas-div">
<div class="canvas_box">
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
<div class="canvas-wrapper">
<div class="ruler-corner"></div>
<canvas class="ruler-horizontal" id="ruler-h"></canvas>
<canvas class="ruler-vertical" id="ruler-v"></canvas>
<div class="canvas_box" style="margin-left: 20px; margin-top: 20px;">
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
</div>
</div>
<div class="canvas_bg">
<!-- 类型改变 -->
@@ -912,6 +956,10 @@
let 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 + ')')
@@ -3164,7 +3212,8 @@
_inputEdit = false
})
$("body").keyup(function (event) {
if (event.keyCode == 17) {
// 兼容三端的 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) {
@@ -3172,7 +3221,74 @@
};
})
$("body").keydown(function (event) {
if (event.keyCode == 17) {
// 统一的快捷键处理(兼容 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) {
//同时按下且在编辑状态
@@ -3191,7 +3307,7 @@
if (event.keyCode == 37) {
//left
let v = -1;
if (_keyctrl) {
if (_keyctrl || event.ctrlKey || event.metaKey) {
v = -5;
}
let x = canvas.getActiveObject().get("left");
@@ -3204,7 +3320,7 @@
if (event.keyCode == 38) {
//up
let v = -1;
if (_keyctrl) {
if (_keyctrl || event.ctrlKey || event.metaKey) {
v = -5;
}
let y = canvas.getActiveObject().get("top");
@@ -3217,7 +3333,7 @@
if (event.keyCode == 39) {
//right
let v = 1;
if (_keyctrl) {
if (_keyctrl || event.ctrlKey || event.metaKey) {
v = 5;
}
let x = canvas.getActiveObject().get("left");
@@ -3230,7 +3346,7 @@
if (event.keyCode == 40) {
//down
let v = +1;
if (_keyctrl) {
if (_keyctrl || event.ctrlKey || event.metaKey) {
v = 5;
}
let y = canvas.getActiveObject().get("top");
@@ -3240,25 +3356,27 @@
recordState();
updateControls();
}
if (event.keyCode != 46 || _inputEdit || _textEdit) return;
let actObjs = canvas.getActiveObjects();
let allObjs = canvas.getObjects();
let flag = 0;
for (let item of actObjs) {
if (item != allObjs[0]) {
let i = getIndex(item);//0是背景,所以从1开始,但objs不包含背景图片,故要-1
objs.splice(i - 1, 1);
canvas.remove(item);
flag = 1;
// 普通删除键(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);//0是背景,所以从1开始,但objs不包含背景图片,故要-1
objs.splice(i - 1, 1);
canvas.remove(item);
flag = 1;
}
}
updateList();
canvas.discardActiveObject();
if (flag == 1) {
recordState();
canvas.renderAll();
handleObjectSelected()
}
}
updateList();
canvas.discardActiveObject();
if (flag == 1) {
recordState();
canvas.renderAll();
handleObjectSelected()
}
});
let pre_add_image;
@@ -4211,7 +4329,9 @@
openAs.name = result.filePath;
layer.msg(language_str("saveSucc") + openAs.name);//'保存成功至'
saveHistory();
callback();
if (callback && typeof callback === 'function') {
callback();
}
}).catch(err => {
console.log()
})
@@ -4262,7 +4382,9 @@
fs.writeFileSync(openAs.name, JSON.stringify(con_o))
layer.msg(language_str("saveSucc") + openAs.name);//'保存成功至'
saveHistory();
callback();
if (callback && typeof callback === 'function') {
callback();
}
return;
}
dialog.showSaveDialog({
@@ -4281,7 +4403,9 @@
openAs.name = result.filePath;
layer.msg(language_str("saveSucc") + openAs.name);//'保存成功至'
saveHistory();
callback();
if (callback && typeof callback === 'function') {
callback();
}
}).catch(err => {
console.log()
})
@@ -6331,27 +6455,6 @@
}
updateControls();
});
// 监听键盘事件
window.addEventListener('keydown', handleKeyDown);
function handleKeyDown(e) {
// 检测 Ctrl+C
if ((e.ctrlKey || e.metaKey) && e.keyCode === 67 && e.target.nodeName == 'BODY') {
copySelection();
e.preventDefault(); // 阻止默认复制行为
}
// 检测 Ctrl+V
if ((e.ctrlKey || e.metaKey) && e.keyCode === 86 && e.target.nodeName == 'BODY') {
pasteFromClipboard();
e.preventDefault(); // 阻止默认粘贴行为
}
// 检测 Ctrl+S
if ((e.ctrlKey || e.metaKey) && e.keyCode === 83) {
output();
e.preventDefault(); // 阻止默认粘贴行为
}
}
// 复制选中对象
function copySelection() {
@@ -6997,6 +7100,198 @@
canvas.renderAll();
}
});
// 标尺绘制函数
function initRulers() {
const dpi = 600;
const mmToPx = dpi / 25.4; // 1mm = 23.622px (600 DPI)
const rulerHeight = 20;
const rulerWidth = 20;
// 获取实际画布的像素尺寸(px)
const actualCanvasWidth = canvas1.width || 2787; // 实际画布宽度(px
const actualCanvasHeight = canvas1.height || 2787; // 实际画布高度(px
// 获取显示区域的尺寸(包括 margin)
const canvasBox = $('.canvas_box');
const canvasWrapper = $('.canvas-wrapper');
// 使用 canvas-wrapper 的宽度,如果没有则使用 canvas_box + margin
let canvasWidth = canvasWrapper.width();
let canvasHeight = canvasWrapper.height();
if (!canvasWidth || canvasWidth === 0) {
canvasWidth = canvasBox.width() + 20; // 加上 margin-left: 20px
}
if (!canvasHeight || canvasHeight === 0) {
canvasHeight = canvasBox.height() + 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');
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 startX = 0;
const endX = canvasWidth;
// 绘制主要刻度(每10mm)
for (let mm = 0; mm * pxPerMm <= endX; mm += 10) {
const x = startX + 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)
for (let mm = 0; mm * pxPerMm <= endX; mm += 1) {
const x = startX + 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');
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 startY = 0;
const endY = canvasHeight;
// 绘制主要刻度(每10mm)
for (let mm = 0; mm * pxPerMm <= endY; mm += 10) {
const y = startY + 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)
for (let mm = 0; mm * pxPerMm <= endY; mm += 1) {
const y = startY + 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 canvasBox = $('.canvas_box');
const canvasWrapper = $('.canvas-wrapper');
let newWidth = canvasWrapper.width();
let newHeight = canvasWrapper.height();
if (!newWidth || newWidth === 0) {
newWidth = canvasBox.width() + 20; // 加上 margin-left: 20px
}
if (!newHeight || newHeight === 0) {
newHeight = canvasBox.height() + 20; // 加上 margin-top: 20px
}
if (newWidth !== canvasWidth || newHeight !== canvasHeight) {
canvasWidth = newWidth;
canvasHeight = newHeight;
$('#ruler-h').attr('width', newWidth);
$('#ruler-v').attr('height', newHeight);
$('.ruler-horizontal').css('width', newWidth + 'px');
$('.ruler-vertical').css('height', newHeight + 'px');
drawHorizontalRuler();
drawVerticalRuler();
}
}, 100);
});
// 监听画布缩放变化
canvas1.on('after:render', function() {
drawHorizontalRuler();
drawVerticalRuler();
});
canvas2.on('after:render', function() {
drawHorizontalRuler();
drawVerticalRuler();
});
}
})
});