thread-master/apps/web/src/lib/memberAvatar.ts

65 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** 會員頭像:本機選檔 → 壓縮成可存 localStorage 的 data URL */
const MAX_INPUT_BYTES = 5 * 1024 * 1024;
const MAX_EDGE = 256;
const JPEG_QUALITY = 0.86;
function loadImageFromFile(file: File): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const url = URL.createObjectURL(file);
const img = new Image();
img.onload = () => {
URL.revokeObjectURL(url);
resolve(img);
};
img.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error("無法讀取圖片"));
};
img.src = url;
});
}
/**
* 將本機圖片裁成正方形、縮到 ≤256px輸出 JPEG data URL。
* mock 階段不真上傳;存進租戶會員紀錄。
*/
export async function fileToMemberAvatarDataUrl(file: File): Promise<string> {
if (!file.type.startsWith("image/")) {
throw new Error("請選擇圖片檔JPGPNGWebP");
}
if (file.size > MAX_INPUT_BYTES) {
throw new Error("圖片請在 5MB 以內");
}
const img = await loadImageFromFile(file);
const srcW = img.naturalWidth || img.width;
const srcH = img.naturalHeight || img.height;
if (!srcW || !srcH) throw new Error("無法讀取圖片尺寸");
const side = Math.min(srcW, srcH);
const sx = Math.floor((srcW - side) / 2);
const sy = Math.floor((srcH - side) / 2);
const edge = Math.min(MAX_EDGE, side);
const canvas = document.createElement("canvas");
canvas.width = edge;
canvas.height = edge;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("瀏覽器不支援圖片處理");
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = "high";
ctx.drawImage(img, sx, sy, side, side, 0, 0, edge, edge);
const dataUrl = canvas.toDataURL("image/jpeg", JPEG_QUALITY);
if (!dataUrl.startsWith("data:image/")) throw new Error("壓縮失敗");
if (dataUrl.length > 400_000) {
// 再壓一輪
const smaller = canvas.toDataURL("image/jpeg", 0.7);
if (smaller.length > 400_000) throw new Error("頭像仍過大,請換較簡單的圖");
return smaller;
}
return dataUrl;
}