feat: add product archive frontend client
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import type { CanvasDocument, StickerAsset } from '../types';
|
||||
import { serializeDocument } from './svgExport';
|
||||
|
||||
const PNG_TYPE = 'image/png';
|
||||
|
||||
/**
|
||||
* Render the full visible canvas document to a PNG Blob. WebKit returns the
|
||||
* Blob directly from toBlob; the standard-track callback form and the
|
||||
* data-URL fallback keep other browsers working.
|
||||
*/
|
||||
export async function createDesignPreviewBlob(
|
||||
document: CanvasDocument,
|
||||
stickers: Map<string, StickerAsset>,
|
||||
): Promise<Blob> {
|
||||
const width = Math.max(1, Math.floor(document.width || 0));
|
||||
const height = Math.max(1, Math.floor(document.height || 0));
|
||||
const svgMarkup = await serializeDocument(document, stickers, { includeBackground: true });
|
||||
const svgBlob = new Blob([svgMarkup], { type: 'image/svg+xml;charset=utf-8' });
|
||||
const objectUrl = URL.createObjectURL(svgBlob);
|
||||
try {
|
||||
const image: HTMLImageElement =
|
||||
typeof Image === 'function' ? new Image() : globalThis.document.createElement('img');
|
||||
image.src = objectUrl;
|
||||
await image.decode();
|
||||
|
||||
const canvas = globalThis.document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) throw new Error('设计预览画布上下文不可用');
|
||||
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
const pngBlob = await canvasToPngBlob(canvas);
|
||||
if (pngBlob.type !== PNG_TYPE || pngBlob.size === 0) {
|
||||
throw new Error('设计预览未生成有效的 PNG 输出');
|
||||
}
|
||||
return pngBlob;
|
||||
} finally {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
async function canvasToPngBlob(canvas: HTMLCanvasElement): Promise<Blob> {
|
||||
if (typeof canvas.toBlob === 'function') {
|
||||
try {
|
||||
const direct = (canvas.toBlob as unknown as (type: string) => Blob | undefined)('image/png');
|
||||
if (direct) return direct;
|
||||
} catch {
|
||||
// Non-WebKit browsers wait for the callback form below.
|
||||
}
|
||||
return new Promise<Blob>((resolve, reject) => {
|
||||
canvas.toBlob(blob => {
|
||||
if (blob) resolve(blob);
|
||||
else reject(new Error('设计预览画布未生成 PNG Blob'));
|
||||
}, 'image/png');
|
||||
});
|
||||
}
|
||||
if (typeof canvas.toDataURL === 'function') {
|
||||
const res = await fetch(canvas.toDataURL('image/png'));
|
||||
if (!res.ok) throw new Error('设计预览 PNG 输出读取失败');
|
||||
return res.blob();
|
||||
}
|
||||
throw new Error('当前浏览器不支持设计预览 PNG 输出');
|
||||
}
|
||||
Reference in New Issue
Block a user