fix(web): design1/2 缩略图与 design2 设为背景交互修复
- design1:预览/最近文件缩略图按 background 边界截取,去掉多余白盘 - design2:设为背景贴齐 1012x648 卡框,保存/打开保留用户背景 src - design2:列表可选中锁定对象并加载完整属性,缩略图叠加 front_bg 模板 - design2:固定卡框锚点,避免选中背景时标尺/蒙版随属性面板漂移 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,3 +1,99 @@
|
||||
/** design2 正面预览/缩略图:1012×648 视口,与 display_func 中 url3 一致 */
|
||||
function soonCollectDesign2FrontPreview(canvas, bgImage, objMetaList) {
|
||||
if (!canvas || !bgImage || typeof canvas.getObjects !== 'function') return null;
|
||||
var list = canvas.getObjects().filter(function (obj) { return !obj.isGuideLine; });
|
||||
var g3 = [];
|
||||
var all_left = 0;
|
||||
var all_top = 0;
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var item = list[i];
|
||||
var idx = getIndex(item, canvas);
|
||||
var metaIdx = typeof window.soonDesign2ResolveObjMetaIndex === 'function'
|
||||
? window.soonDesign2ResolveObjMetaIndex(item, canvas)
|
||||
: (idx > 0 ? idx - 1 : -1);
|
||||
if (metaIdx < 0 && idx !== 0) continue;
|
||||
if (metaIdx >= 0 && (!objMetaList[metaIdx] || typeof objMetaList[metaIdx].type === 'undefined')) continue;
|
||||
if (getCoordsMinX(bgImage.getCoords()) - getCoordsMinX(item.getCoords()) > all_left) {
|
||||
all_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(bgImage.getCoords()));
|
||||
}
|
||||
if (getCoordsMinY(bgImage.getCoords()) - getCoordsMinY(item.getCoords()) > all_top) {
|
||||
all_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(bgImage.getCoords()));
|
||||
}
|
||||
g3.push(fabric.util.object.clone(item));
|
||||
}
|
||||
return { g3: g3, all_left: all_left, all_top: all_top };
|
||||
}
|
||||
|
||||
function soonExportDesign2PreviewDataUrl(g3, all_left, all_top, quality, multiplier) {
|
||||
var z = (typeof zoom !== 'undefined' ? zoom : null) || window.zoom || 1;
|
||||
var opts = {
|
||||
height: 648,
|
||||
width: 1012,
|
||||
top: all_top / z,
|
||||
left: all_left / z,
|
||||
format: 'jpeg',
|
||||
quality: quality == null ? 0.72 : quality
|
||||
};
|
||||
if (multiplier != null && multiplier > 0) opts.multiplier = multiplier;
|
||||
return new fabric.Group(g3).toDataURL(opts);
|
||||
}
|
||||
|
||||
/** 缩略图/预览:叠加系统卡面模板 front_bg(与编辑区 source_front 一致) */
|
||||
function soonDesign2AppendTemplateOverlayToGroup(g3, canvas, bgImage) {
|
||||
if (!g3 || !canvas) return false;
|
||||
var sf = document.getElementById('source_front');
|
||||
if (!sf || !(sf.complete && sf.naturalWidth > 0)) return false;
|
||||
var frame = typeof window.soonDesign2FrameOrigin === 'function'
|
||||
? window.soonDesign2FrameOrigin(canvas)
|
||||
: null;
|
||||
if (!frame && bgImage && bgImage.width) {
|
||||
var sx = bgImage.scaleX == null ? 1 : bgImage.scaleX;
|
||||
var sy = bgImage.scaleY == null ? 1 : bgImage.scaleY;
|
||||
frame = { left: bgImage.left, top: bgImage.top, width: bgImage.width * sx, height: bgImage.height * sy };
|
||||
}
|
||||
if (!frame || !(frame.width > 0 && frame.height > 0)) return false;
|
||||
g3.push(new fabric.Image(sf, {
|
||||
left: frame.left,
|
||||
top: frame.top,
|
||||
originX: 'left',
|
||||
originY: 'top',
|
||||
scaleX: frame.width / sf.naturalWidth,
|
||||
scaleY: frame.height / sf.naturalHeight
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
function soonBuildDesign2SavePreviewPic(canvas) {
|
||||
var THUMB_MAX = 102400;
|
||||
var c1 = window.canvas1;
|
||||
if (!canvas || (c1 && canvas !== c1)) return '';
|
||||
var bg = window.background_image1 || window.background_image;
|
||||
var meta = window.objs1;
|
||||
if (!bg || !Array.isArray(meta)) return '';
|
||||
if (typeof canvas.renderAll === 'function') canvas.renderAll();
|
||||
var pack = soonCollectDesign2FrontPreview(canvas, bg, meta);
|
||||
if (!pack || !pack.g3.length) return '';
|
||||
soonDesign2AppendTemplateOverlayToGroup(pack.g3, canvas, bg);
|
||||
var quality = 0.72;
|
||||
var mult = 0.55;
|
||||
var url = '';
|
||||
for (var i = 0; i < 4; i++) {
|
||||
try {
|
||||
url = soonExportDesign2PreviewDataUrl(pack.g3, pack.all_left, pack.all_top, quality, mult);
|
||||
if (url && url.indexOf('data:image/') === 0 && url.length <= THUMB_MAX) return url;
|
||||
} catch (e) { /* ignore */ }
|
||||
quality -= 0.16;
|
||||
mult -= 0.08;
|
||||
if (mult < 0.28) mult = 0.28;
|
||||
}
|
||||
try {
|
||||
url = soonExportDesign2PreviewDataUrl(pack.g3, pack.all_left, pack.all_top, 0.32, 0.28);
|
||||
if (url && url.indexOf('data:image/') === 0) return url;
|
||||
} catch (e2) { /* ignore */ }
|
||||
return '';
|
||||
}
|
||||
window.soonBuildDesign2SavePreviewPic = soonBuildDesign2SavePreviewPic;
|
||||
|
||||
window.display_func = function display_func(img1, img2, img3) {
|
||||
$('#base_control').hide()
|
||||
$('#line_control').hide()
|
||||
@@ -1046,9 +1142,11 @@ window.cleanupSrcFields = function cleanupSrcFields(jsonObjects, objsArray) {
|
||||
let obj = jsonObjects[i];
|
||||
// 只处理 fabric.Image 对象且有 src 字段的
|
||||
if (obj && obj.type === 'image' && obj.src) {
|
||||
// 背景图片(索引 0)- 删除 src
|
||||
// 模板背景(索引 0)删 src;用户设为背景时 fo[0] 为 type 1,须保留
|
||||
if (i === 0) {
|
||||
delete obj.src;
|
||||
if (!(objsArray[0] && objsArray[0].type === 1)) {
|
||||
delete obj.src;
|
||||
}
|
||||
}
|
||||
// 其他对象,通过 objs 数组判断类型
|
||||
else if (i - 1 < objsArray.length) {
|
||||
@@ -1338,6 +1436,8 @@ window.openFile = function open(file, jAlready) {
|
||||
return;
|
||||
}
|
||||
openAs.name = file;
|
||||
const fo = j.fo || []
|
||||
const bo = j.bo || []
|
||||
var left = background_image.left
|
||||
let top = background_image.top
|
||||
let new_left = j.f.objects[0].left
|
||||
@@ -1350,18 +1450,25 @@ window.openFile = function open(file, jAlready) {
|
||||
o.left = o.left - new_left + left
|
||||
o.top = o.top - new_top + top
|
||||
}
|
||||
// 如果背景图片没有src,从路径恢复(因为保存时删除了src以减小文件大小)
|
||||
if (j.f.objects[0] && !j.f.objects[0].src) {
|
||||
var isUserBgFront = typeof window.soonDesign2IsUserBgMeta === 'function'
|
||||
? window.soonDesign2IsUserBgMeta(fo)
|
||||
: (fo[0] && fo[0].type === 1)
|
||||
var isUserBgBack = typeof window.soonDesign2IsUserBgMeta === 'function'
|
||||
? window.soonDesign2IsUserBgMeta(bo)
|
||||
: (bo[0] && bo[0].type === 1)
|
||||
if (j.f.objects[0] && !j.f.objects[0].src && !isUserBgFront) {
|
||||
j.f.objects[0].src = soonAsset('bg_front_2.png')
|
||||
} else if (j.f.objects[0] && !j.f.objects[0].src && isUserBgFront && j.f.objects.length > 1) {
|
||||
j.f.objects[0].src = soonAsset('bg_front_2.png')
|
||||
}
|
||||
if (j.b.objects[0] && !j.b.objects[0].src) {
|
||||
if (j.b.objects[0] && !j.b.objects[0].src && !isUserBgBack) {
|
||||
j.b.objects[0].src = soonAsset('bg_back_2.png')
|
||||
} else if (j.b.objects[0] && !j.b.objects[0].src && isUserBgBack && j.b.objects.length > 1) {
|
||||
j.b.objects[0].src = soonAsset('bg_back_2.png')
|
||||
}
|
||||
|
||||
// 为二维码和条形码对象创建临时的 src(Fabric.js 需要 src 来创建 Image 对象)
|
||||
// 加载完成后会重新生成,所以这里只需要一个占位符
|
||||
const fo = j.fo || []
|
||||
const bo = j.bo || []
|
||||
const placeholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='
|
||||
|
||||
// 为正面二维码、条形码和合成图片创建临时 src
|
||||
@@ -1492,6 +1599,9 @@ window.openFile = function open(file, jAlready) {
|
||||
}
|
||||
|
||||
canvas1.loadFromJSON(j.f, function () {
|
||||
if (typeof window.soonDesign2NormalizeUserBg === 'function') {
|
||||
window.soonDesign2NormalizeUserBg(canvas1, fo);
|
||||
}
|
||||
// 加载后解锁所有对象(背景除外)
|
||||
if (typeof window.unlockAllObjects === 'function') {
|
||||
window.unlockAllObjects(canvas1);
|
||||
@@ -1508,6 +1618,9 @@ window.openFile = function open(file, jAlready) {
|
||||
saveInitialState()
|
||||
})
|
||||
canvas2.loadFromJSON(j.b, function () {
|
||||
if (typeof window.soonDesign2NormalizeUserBg === 'function') {
|
||||
window.soonDesign2NormalizeUserBg(canvas2, bo);
|
||||
}
|
||||
// 加载后解锁所有对象(背景除外)
|
||||
if (typeof window.unlockAllObjects === 'function') {
|
||||
window.unlockAllObjects(canvas2);
|
||||
|
||||
Reference in New Issue
Block a user