65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
/** 會員頭像:本機選檔 → 壓縮成可存 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("請選擇圖片檔(JPG/PNG/WebP)");
|
||
}
|
||
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;
|
||
}
|