1 Commits

Author SHA1 Message Date
3f720f02d2 v1.2.7: 修复合并图片紧密拼接问题 2026-04-21 21:41:10 +08:00
5 changed files with 206 additions and 123 deletions

10
app.py
View File

@@ -1,7 +1,7 @@
""" """
图片编辑器 - Image Editor v1.2.6 图片编辑器 - Image Editor v1.2.7
前端图片处理工具:合并、分割、挖孔填充、圆形切图、文字图片等 前端图片处理工具:合并、分割、挖孔填充、圆形切图、文字图片等
v1.2.6: 操作后显示预览效果,保留原始图片 v1.2.7: 修复合并图片紧密拼接问题
端口: 19018 端口: 19018
""" """
@@ -25,7 +25,7 @@ def index():
@app.route('/api/health') @app.route('/api/health')
def health(): def health():
return jsonify({'status': 'ok', 'version': '1.2.6', 'time': datetime.now().isoformat()}) return jsonify({'status': 'ok', 'version': '1.2.7', 'time': datetime.now().isoformat()})
@app.route('/api/save', methods=['POST']) @app.route('/api/save', methods=['POST'])
def save_image(): def save_image():
@@ -64,9 +64,9 @@ def list_images():
if __name__ == '__main__': if __name__ == '__main__':
print("=" * 50) print("=" * 50)
print("图片编辑器 - Image Editor v1.2.6") print("图片编辑器 - Image Editor v1.2.7")
print("=" * 50) print("=" * 50)
print("操作后显示预览效果,保留原始图片") print("修复合并图片紧密拼接问题")
print(f"访问地址: http://localhost:19018") print(f"访问地址: http://localhost:19018")
print("=" * 50) print("=" * 50)
app.run(host='0.0.0.0', port=19018, debug=True) app.run(host='0.0.0.0', port=19018, debug=True)

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片编辑器 - Image Editor v1.2.6</title> <title>图片编辑器 - Image Editor v1.2.7</title>
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
<style> <style>
@@ -1081,20 +1081,76 @@
return { width: size.width || item.width, height: size.height || item.height }; return { width: size.width || item.width, height: size.height || item.height };
}; };
// 计算画布尺寸
let canvasWidth = 0, canvasHeight = 0;
if (uniformSize) {
// 统一尺寸:使用固定单元格大小
let maxWidth = 0, maxHeight = 0; let maxWidth = 0, maxHeight = 0;
state.mergeImageOrder.forEach(item => { state.mergeImageOrder.forEach(item => {
const size = getActualSize(item); const size = getActualSize(item);
maxWidth = Math.max(maxWidth, size.width); maxWidth = Math.max(maxWidth, size.width);
maxHeight = Math.max(maxHeight, size.height); maxHeight = Math.max(maxHeight, size.height);
}); });
canvasWidth = cols * maxWidth + (cols - 1) * gap;
canvasHeight = rows * maxHeight + (rows - 1) * gap;
} else {
// 不统一尺寸:紧密拼接,累加实际尺寸
if (direction === 'horizontal') {
// 横向:累加宽度,取最大高度
state.mergeImageOrder.forEach(item => {
const size = getActualSize(item);
canvasWidth += size.width + gap;
canvasHeight = Math.max(canvasHeight, size.height);
});
canvasWidth -= gap; // 去掉最后一个多余的gap
} else if (direction === 'vertical') {
// 纵向:累加高度,取最大宽度
state.mergeImageOrder.forEach(item => {
const size = getActualSize(item);
canvasHeight += size.height + gap;
canvasWidth = Math.max(canvasWidth, size.width);
});
canvasHeight -= gap;
} else {
// 网格:每行累加宽度,累加行高度
for (let r = 0; r < rows; r++) {
let rowWidth = 0, rowHeight = 0;
for (let c = 0; c < cols; c++) {
const idx = r * cols + c;
if (idx < state.mergeImageOrder.length) {
const size = getActualSize(state.mergeImageOrder[idx]);
rowWidth += size.width + gap;
rowHeight = Math.max(rowHeight, size.height);
}
}
rowWidth -= gap;
canvasWidth = Math.max(canvasWidth, rowWidth);
canvasHeight += rowHeight + gap;
}
canvasHeight -= gap;
}
}
canvas.width = cols * maxWidth + (cols - 1) * gap; canvas.width = canvasWidth;
canvas.height = rows * maxHeight + (rows - 1) * gap; canvas.height = canvasHeight;
// 绘制合并结果到画布(预览) // 绘制合并结果到画布(预览)
ctx.fillStyle = bgColor; ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制图片
let currentX = 0, currentY = 0;
if (uniformSize) {
// 统一尺寸:固定单元格,居中绘制
let maxWidth = 0, maxHeight = 0;
state.mergeImageOrder.forEach(item => {
const size = getActualSize(item);
maxWidth = Math.max(maxWidth, size.width);
maxHeight = Math.max(maxHeight, size.height);
});
state.mergeImageOrder.forEach((orderItem, index) => { state.mergeImageOrder.forEach((orderItem, index) => {
if (index >= cols * rows) return; if (index >= cols * rows) return;
const col = index % cols; const col = index % cols;
@@ -1110,6 +1166,33 @@
ctx.drawImage(originalImg.img, x + offsetX, y + offsetY, size.width, size.height); ctx.drawImage(originalImg.img, x + offsetX, y + offsetY, size.width, size.height);
} }
}); });
} else {
// 不统一尺寸:紧密拼接,无居中偏移
state.mergeImageOrder.forEach((orderItem, index) => {
const size = getActualSize(orderItem);
const originalImg = state.images.find(img => img.name === orderItem.name);
if (!originalImg) return;
if (direction === 'horizontal') {
ctx.drawImage(originalImg.img, currentX, 0, size.width, size.height);
currentX += size.width + gap;
} else if (direction === 'vertical') {
ctx.drawImage(originalImg.img, 0, currentY, size.width, size.height);
currentY += size.height + gap;
} else {
// 网格模式
const col = index % cols;
const row = Math.floor(index / cols);
if (col === 0) currentX = 0;
ctx.drawImage(originalImg.img, currentX, currentY, size.width, size.height);
currentX += size.width + gap;
if (col === cols - 1 || index === state.mergeImageOrder.length - 1) {
currentY += size.height + gap;
}
}
});
}
// 显示预览效果(不清除原始图片) // 显示预览效果(不清除原始图片)
const mergedData = canvas.toDataURL('image/png'); const mergedData = canvas.toDataURL('image/png');