feat(design1): 光盘导出环形裁剪与画布/缩略图样式统一
导出与预览 PNG 按光盘外圆与中心孔透明裁剪;画布蒙版改为与背景同色 #31373D;缩略图改 PNG 环形铺底;Web/Electron 同步。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1751,7 +1751,7 @@ function initCanvasEvents() {
|
|||||||
const ctx = currentCanvas.getContext();
|
const ctx = currentCanvas.getContext();
|
||||||
let bgObj = (currentCanvas === canvas1) ? background_image1 : background_image2;
|
let bgObj = (currentCanvas === canvas1) ? background_image1 : background_image2;
|
||||||
|
|
||||||
if (bgObj) {
|
if (bgObj && !window._soonSkipDesign1CdMask) {
|
||||||
const width = currentCanvas.width;
|
const width = currentCanvas.width;
|
||||||
const height = currentCanvas.height;
|
const height = currentCanvas.height;
|
||||||
|
|
||||||
@@ -1795,7 +1795,7 @@ function initCanvasEvents() {
|
|||||||
ctx.arc(cx, cy, r_inner, 0, Math.PI * 2, false);
|
ctx.arc(cx, cy, r_inner, 0, Math.PI * 2, false);
|
||||||
|
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fillStyle = "rgba(0, 0, 0, 0.9)"; // 90% 遮罩
|
ctx.fillStyle = '#31373D';
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,91 @@ async function saveImageAsPNG(buffer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function soonDesign1GetHoleDiameterMm(canvas) {
|
||||||
|
var hole = 24;
|
||||||
|
if (canvas === window.canvas1 && typeof window.bg_version !== 'undefined' && window.bg_version === 2) {
|
||||||
|
hole = 42;
|
||||||
|
}
|
||||||
|
return hole;
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign1ApplyDiscAlpha(tctx, w, h, holeDiameterMm) {
|
||||||
|
var cx = w / 2;
|
||||||
|
var cy = h / 2;
|
||||||
|
var rOuter = w / 2;
|
||||||
|
var rInner = rOuter * (holeDiameterMm / 120);
|
||||||
|
tctx.save();
|
||||||
|
tctx.globalCompositeOperation = 'destination-in';
|
||||||
|
tctx.fillStyle = '#fff';
|
||||||
|
tctx.beginPath();
|
||||||
|
tctx.arc(cx, cy, rOuter, 0, Math.PI * 2);
|
||||||
|
tctx.moveTo(cx + rInner, cy);
|
||||||
|
tctx.arc(cx, cy, rInner, 0, Math.PI * 2, true);
|
||||||
|
tctx.fill('evenodd');
|
||||||
|
tctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign1ApplyDiscMatte(tctx, w, h, holeDiameterMm, color) {
|
||||||
|
var snap = document.createElement('canvas');
|
||||||
|
snap.width = w;
|
||||||
|
snap.height = h;
|
||||||
|
snap.getContext('2d').drawImage(tctx.canvas, 0, 0);
|
||||||
|
tctx.fillStyle = color;
|
||||||
|
tctx.fillRect(0, 0, w, h);
|
||||||
|
tctx.save();
|
||||||
|
var cx = w / 2;
|
||||||
|
var cy = h / 2;
|
||||||
|
var rOuter = w / 2;
|
||||||
|
var rInner = rOuter * (holeDiameterMm / 120);
|
||||||
|
tctx.beginPath();
|
||||||
|
tctx.arc(cx, cy, rOuter, 0, Math.PI * 2);
|
||||||
|
tctx.moveTo(cx + rInner, cy);
|
||||||
|
tctx.arc(cx, cy, rInner, 0, Math.PI * 2, true);
|
||||||
|
tctx.clip('evenodd');
|
||||||
|
tctx.drawImage(snap, 0, 0);
|
||||||
|
tctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc, discMatte) {
|
||||||
|
var bg = window.background_image1 || window.background_image;
|
||||||
|
if (!canvas || !bg) return '';
|
||||||
|
var srcEl = canvas.lowerCanvasEl;
|
||||||
|
if (!srcEl || !srcEl.getContext) return '';
|
||||||
|
var sx = bg.scaleX == null ? 1 : bg.scaleX;
|
||||||
|
var sy = bg.scaleY == null ? 1 : bg.scaleY;
|
||||||
|
var sw = bg.width * sx;
|
||||||
|
var sh = bg.height * sy;
|
||||||
|
var retina = (typeof canvas.getRetinaScaling === 'function') ? canvas.getRetinaScaling() : 1;
|
||||||
|
outW = outW || Math.round(sw);
|
||||||
|
outH = outH || Math.round(sh);
|
||||||
|
var tmp = document.createElement('canvas');
|
||||||
|
tmp.width = outW;
|
||||||
|
tmp.height = outH;
|
||||||
|
var tctx = tmp.getContext('2d');
|
||||||
|
tctx.drawImage(
|
||||||
|
srcEl,
|
||||||
|
bg.left * retina, bg.top * retina, sw * retina, sh * retina,
|
||||||
|
0, 0, outW, outH
|
||||||
|
);
|
||||||
|
if (discMatte) {
|
||||||
|
soonDesign1ApplyDiscMatte(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas), discMatte);
|
||||||
|
} else if (clipDisc && format !== 'jpeg') {
|
||||||
|
soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas));
|
||||||
|
}
|
||||||
|
if (format === 'jpeg') return tmp.toDataURL('image/jpeg', quality == null ? 0.75 : quality);
|
||||||
|
return tmp.toDataURL('image/png');
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign1CaptureDiscPng(canvas, outW, outH) {
|
||||||
|
window._soonSkipDesign1CdMask = true;
|
||||||
|
if (canvas.discardActiveObject) canvas.discardActiveObject();
|
||||||
|
canvas.renderAll();
|
||||||
|
var url = soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
|
||||||
|
window._soonSkipDesign1CdMask = false;
|
||||||
|
canvas.renderAll();
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
// 将 display_func 附加到 window 对象,确保全局可访问
|
// 将 display_func 附加到 window 对象,确保全局可访问
|
||||||
window.display_func = function display_func(img1, img2, img3) {
|
window.display_func = function display_func(img1, img2, img3) {
|
||||||
$("#base_control").hide();
|
$("#base_control").hide();
|
||||||
@@ -217,13 +302,16 @@ window.display_func = function display_func(img1, img2, img3) {
|
|||||||
left: all_left
|
left: all_left
|
||||||
});
|
});
|
||||||
|
|
||||||
g3.push(img1);
|
var hiddenForPreview = [];
|
||||||
let url3 = new fabric.Group(g3).toDataURL({
|
canvas1.getObjects().forEach(function (o) {
|
||||||
height: 2787,
|
if (o.isGuideLine) {
|
||||||
width: 2787,
|
hiddenForPreview.push(o);
|
||||||
top: all_top,
|
o.visible = false;
|
||||||
left: all_left
|
}
|
||||||
});
|
});
|
||||||
|
let url3 = soonDesign1CaptureDiscPng(canvas1, 2787, 2787);
|
||||||
|
hiddenForPreview.forEach(function (o) { o.visible = true; });
|
||||||
|
canvas1.renderAll();
|
||||||
let url5 = new fabric.Group(g5).toDataURL({
|
let url5 = new fabric.Group(g5).toDataURL({
|
||||||
height: 2787,
|
height: 2787,
|
||||||
width: 2787,
|
width: 2787,
|
||||||
@@ -596,12 +684,16 @@ window.output = function output(callback = null, _save = save) {
|
|||||||
left: all_left
|
left: all_left
|
||||||
});
|
});
|
||||||
g3.push(img1);
|
g3.push(img1);
|
||||||
let url3 = new fabric.Group(g3).toDataURL({
|
var hiddenForSave = [];
|
||||||
height: 2787,
|
canvas1.getObjects().forEach(function (o) {
|
||||||
width: 2787,
|
if (o.isGuideLine) {
|
||||||
top: all_top,
|
hiddenForSave.push(o);
|
||||||
left: all_left
|
o.visible = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
let url3 = soonDesign1CaptureDiscPng(canvas1, 2787, 2787);
|
||||||
|
hiddenForSave.forEach(function (o) { o.visible = true; });
|
||||||
|
canvas1.renderAll();
|
||||||
let url5 = new fabric.Group(g5).toDataURL({
|
let url5 = new fabric.Group(g5).toDataURL({
|
||||||
height: 2787,
|
height: 2787,
|
||||||
width: 2787,
|
width: 2787,
|
||||||
|
|||||||
@@ -1889,7 +1889,7 @@ function initCanvasEvents() {
|
|||||||
ctx.arc(cx, cy, r_inner, 0, Math.PI * 2, false);
|
ctx.arc(cx, cy, r_inner, 0, Math.PI * 2, false);
|
||||||
|
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fillStyle = "rgba(0, 0, 0, 0.9)"; // 90% 遮罩
|
ctx.fillStyle = '#31373D';
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,8 +52,55 @@ async function saveImageAsPNG(buffer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 从画布像素截取光盘外接方(含 after:render 圆外暗蒙版),与编辑器所见一致 */
|
/** 孔径规则与 core.js after:render 一致 */
|
||||||
function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality) {
|
function soonDesign1GetHoleDiameterMm(canvas) {
|
||||||
|
var hole = 24;
|
||||||
|
if (canvas === window.canvas1 && typeof window.bg_version !== 'undefined' && window.bg_version === 2) {
|
||||||
|
hole = 42;
|
||||||
|
}
|
||||||
|
return hole;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 圆外四角与中心孔 alpha 置 0,保留环形可打印区 */
|
||||||
|
function soonDesign1ApplyDiscAlpha(tctx, w, h, holeDiameterMm) {
|
||||||
|
var cx = w / 2;
|
||||||
|
var cy = h / 2;
|
||||||
|
var rOuter = w / 2;
|
||||||
|
var rInner = rOuter * (holeDiameterMm / 120);
|
||||||
|
tctx.save();
|
||||||
|
tctx.globalCompositeOperation = 'destination-in';
|
||||||
|
tctx.fillStyle = '#fff';
|
||||||
|
tctx.beginPath();
|
||||||
|
tctx.arc(cx, cy, rOuter, 0, Math.PI * 2);
|
||||||
|
tctx.moveTo(cx + rInner, cy);
|
||||||
|
tctx.arc(cx, cy, rInner, 0, Math.PI * 2, true);
|
||||||
|
tctx.fill('evenodd');
|
||||||
|
tctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 圆外与中心孔铺画布底色,保留环形内容(缩略图用) */
|
||||||
|
function soonDesign1ApplyDiscMatte(tctx, w, h, holeDiameterMm, color) {
|
||||||
|
var snap = document.createElement('canvas');
|
||||||
|
snap.width = w;
|
||||||
|
snap.height = h;
|
||||||
|
snap.getContext('2d').drawImage(tctx.canvas, 0, 0);
|
||||||
|
tctx.fillStyle = color;
|
||||||
|
tctx.fillRect(0, 0, w, h);
|
||||||
|
tctx.save();
|
||||||
|
var cx = w / 2;
|
||||||
|
var cy = h / 2;
|
||||||
|
var rOuter = w / 2;
|
||||||
|
var rInner = rOuter * (holeDiameterMm / 120);
|
||||||
|
tctx.beginPath();
|
||||||
|
tctx.arc(cx, cy, rOuter, 0, Math.PI * 2);
|
||||||
|
tctx.moveTo(cx + rInner, cy);
|
||||||
|
tctx.arc(cx, cy, rInner, 0, Math.PI * 2, true);
|
||||||
|
tctx.clip('evenodd');
|
||||||
|
tctx.drawImage(snap, 0, 0);
|
||||||
|
tctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc, discMatte) {
|
||||||
var bg = window.background_image1 || window.background_image;
|
var bg = window.background_image1 || window.background_image;
|
||||||
if (!canvas || !bg) return '';
|
if (!canvas || !bg) return '';
|
||||||
var srcEl = canvas.lowerCanvasEl;
|
var srcEl = canvas.lowerCanvasEl;
|
||||||
@@ -74,10 +121,26 @@ function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality) {
|
|||||||
bg.left * retina, bg.top * retina, sw * retina, sh * retina,
|
bg.left * retina, bg.top * retina, sw * retina, sh * retina,
|
||||||
0, 0, outW, outH
|
0, 0, outW, outH
|
||||||
);
|
);
|
||||||
|
if (discMatte) {
|
||||||
|
soonDesign1ApplyDiscMatte(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas), discMatte);
|
||||||
|
} else if (clipDisc && format !== 'jpeg') {
|
||||||
|
soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas));
|
||||||
|
}
|
||||||
if (format === 'jpeg') return tmp.toDataURL('image/jpeg', quality == null ? 0.75 : quality);
|
if (format === 'jpeg') return tmp.toDataURL('image/jpeg', quality == null ? 0.75 : quality);
|
||||||
return tmp.toDataURL('image/png');
|
return tmp.toDataURL('image/png');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 导出/打印:跳过编辑器暗蒙版,按光盘环形裁剪 */
|
||||||
|
function soonDesign1CaptureDiscPng(canvas, outW, outH) {
|
||||||
|
window._soonSkipDesign1CdMask = true;
|
||||||
|
if (canvas.discardActiveObject) canvas.discardActiveObject();
|
||||||
|
canvas.renderAll();
|
||||||
|
var url = soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
|
||||||
|
window._soonSkipDesign1CdMask = false;
|
||||||
|
canvas.renderAll();
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
function soonBuildDesign1SavePreviewPic(canvas) {
|
function soonBuildDesign1SavePreviewPic(canvas) {
|
||||||
var THUMB_MAX = 102400;
|
var THUMB_MAX = 102400;
|
||||||
var c1 = window.canvas1;
|
var c1 = window.canvas1;
|
||||||
@@ -90,17 +153,18 @@ function soonBuildDesign1SavePreviewPic(canvas) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (canvas.discardActiveObject) canvas.discardActiveObject();
|
if (canvas.discardActiveObject) canvas.discardActiveObject();
|
||||||
|
window._soonSkipDesign1CdMask = true;
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
|
|
||||||
var url = '';
|
var url = '';
|
||||||
var sizes = [320, 280, 240, 200];
|
var sizes = [280, 240, 200, 180, 160];
|
||||||
var quals = [0.82, 0.68, 0.52, 0.38];
|
|
||||||
for (var i = 0; i < sizes.length; i++) {
|
for (var i = 0; i < sizes.length; i++) {
|
||||||
url = soonDesign1CaptureCanvasRect(canvas, sizes[i], sizes[i], 'jpeg', quals[i]);
|
url = soonDesign1CaptureCanvasRect(canvas, sizes[i], sizes[i], 'png', null, false, '#31373D');
|
||||||
if (url && url.indexOf('data:image/') === 0 && url.length <= THUMB_MAX) break;
|
if (url && url.indexOf('data:image/') === 0 && url.length <= THUMB_MAX) break;
|
||||||
url = '';
|
url = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window._soonSkipDesign1CdMask = false;
|
||||||
hidden.forEach(function (obj) { obj.visible = true; });
|
hidden.forEach(function (obj) { obj.visible = true; });
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
if (canvas.requestRenderAll) canvas.requestRenderAll();
|
if (canvas.requestRenderAll) canvas.requestRenderAll();
|
||||||
@@ -280,9 +344,7 @@ window.display_func = function display_func(img1, img2, img3) {
|
|||||||
o.visible = false;
|
o.visible = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
canvas1.discardActiveObject();
|
let url3 = soonDesign1CaptureDiscPng(canvas1, 2787, 2787);
|
||||||
canvas1.renderAll();
|
|
||||||
let url3 = soonDesign1CaptureCanvasRect(canvas1, 2787, 2787, 'png');
|
|
||||||
hiddenForPreview.forEach(function (o) { o.visible = true; });
|
hiddenForPreview.forEach(function (o) { o.visible = true; });
|
||||||
canvas1.renderAll();
|
canvas1.renderAll();
|
||||||
let url5 = new fabric.Group(g5).toDataURL({
|
let url5 = new fabric.Group(g5).toDataURL({
|
||||||
@@ -675,12 +737,16 @@ window.output = function output(callback = null, _save = save) {
|
|||||||
left: all_left
|
left: all_left
|
||||||
});
|
});
|
||||||
g3.push(img1);
|
g3.push(img1);
|
||||||
let url3 = new fabric.Group(g3).toDataURL({
|
var hiddenForSave = [];
|
||||||
height: 2787,
|
canvas1.getObjects().forEach(function (o) {
|
||||||
width: 2787,
|
if (o.isGuideLine) {
|
||||||
top: all_top,
|
hiddenForSave.push(o);
|
||||||
left: all_left
|
o.visible = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
let url3 = soonDesign1CaptureDiscPng(canvas1, 2787, 2787);
|
||||||
|
hiddenForSave.forEach(function (o) { o.visible = true; });
|
||||||
|
canvas1.renderAll();
|
||||||
let url5 = new fabric.Group(g5).toDataURL({
|
let url5 = new fabric.Group(g5).toDataURL({
|
||||||
height: 2787,
|
height: 2787,
|
||||||
width: 2787,
|
width: 2787,
|
||||||
|
|||||||
Reference in New Issue
Block a user