refactor: 拆分 ShareCardModal(951 行 → 4 个模块)

- ShareCardModal.tsx (199 行): 模态框编排逻辑(图片生成、保存、分享)
- RestaurantShareCard.tsx: 餐厅分享卡片纯展示组件
- BlindboxShareCard.tsx: 盲盒分享卡片纯展示组件
- shareImage.ts: 图片工具函数(toPng 封装、dataUrl 转换、下载)
This commit is contained in:
2026-02-26 19:54:12 +08:00
parent 07541ed686
commit 423b94440d
4 changed files with 776 additions and 760 deletions
+52
View File
@@ -0,0 +1,52 @@
import { toPng } from "html-to-image";
export async function loadImageAsDataUrl(src: string): Promise<string | null> {
try {
const img = new Image();
img.crossOrigin = "anonymous";
img.src = src;
await new Promise<void>((resolve, reject) => {
img.onload = () => resolve();
img.onerror = () => reject();
});
const canvas = document.createElement("canvas");
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext("2d")!;
ctx.drawImage(img, 0, 0);
return canvas.toDataURL("image/jpeg", 0.85);
} catch {
return null;
}
}
export async function generateImage(el: HTMLElement): Promise<string> {
return toPng(el, {
pixelRatio: 2,
quality: 0.95,
cacheBust: true,
skipAutoScale: true,
filter: (node) => {
if (node instanceof HTMLElement && node.dataset.shareExclude === "true") {
return false;
}
return true;
},
});
}
export function downloadDataUrl(dataUrl: string, filename: string) {
const link = document.createElement("a");
link.download = filename;
link.href = dataUrl;
link.click();
}
export function dataUrlToFile(dataUrl: string, filename: string): File {
const arr = dataUrl.split(",");
const mime = arr[0].match(/:(.*?);/)?.[1] || "image/png";
const bstr = atob(arr[1]);
const u8 = new Uint8Array(bstr.length);
for (let i = 0; i < bstr.length; i++) u8[i] = bstr.charCodeAt(i);
return new File([u8], filename, { type: mime });
}