头像裁剪改为保留源格式上传,避免服务端只存 WebP。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -73,7 +73,7 @@ export default function AvatarCropDialog({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>裁剪头像</DialogTitle>
|
<DialogTitle>裁剪头像</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
拖动图片调整位置,滚轮或滑块缩放。GIF 裁剪后将变为静态 JPG。
|
拖动图片调整位置,滚轮或滑块缩放。裁剪结果按原图格式保存(JPG→JPEG,PNG 保留透明);GIF 裁剪后变为静态 JPG。服务端会额外生成 WebP。
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,13 @@ export function validateAvatarOutput(file: File, maxMb: number): string | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 将裁剪区域渲染为 WebP 文件(体积更小;不支持时回退 JPEG) */
|
/** 将裁剪区域渲染为上传「原图」;服务端再衍生 WebP。
|
||||||
|
* 优先保留源格式:PNG 保透明;其余(含 JPG/GIF/WebP)导出为 JPEG。
|
||||||
|
*/
|
||||||
export async function getCroppedAvatarFile(
|
export async function getCroppedAvatarFile(
|
||||||
imageSrc: string,
|
imageSrc: string,
|
||||||
pixelCrop: Area,
|
pixelCrop: Area,
|
||||||
originalName = 'avatar.webp',
|
originalName = 'avatar.jpg',
|
||||||
): Promise<File> {
|
): Promise<File> {
|
||||||
const image = await loadImage(imageSrc);
|
const image = await loadImage(imageSrc);
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
@@ -67,13 +69,21 @@ export async function getCroppedAvatarFile(
|
|||||||
size,
|
size,
|
||||||
);
|
);
|
||||||
|
|
||||||
const tryTypes: { mime: string; quality: number; ext: string }[] = [
|
const srcExt = (originalName.match(/\.([^.]+)$/)?.[1] || '').toLowerCase();
|
||||||
{ mime: 'image/webp', quality: 0.86, ext: 'webp' },
|
const preferPng = srcExt === 'png';
|
||||||
{ mime: 'image/jpeg', quality: 0.92, ext: 'jpg' },
|
// 勿优先 WebP:否则服务端只会存一份 WebP,丢失用户原图格式
|
||||||
];
|
const tryTypes: { mime: string; quality?: number; ext: string }[] = preferPng
|
||||||
|
? [
|
||||||
|
{ mime: 'image/png', ext: 'png' },
|
||||||
|
{ mime: 'image/jpeg', quality: 0.92, ext: 'jpg' },
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{ mime: 'image/jpeg', quality: 0.92, ext: 'jpg' },
|
||||||
|
{ mime: 'image/png', ext: 'png' },
|
||||||
|
];
|
||||||
|
|
||||||
let blob: Blob | null = null;
|
let blob: Blob | null = null;
|
||||||
let picked = tryTypes[1];
|
let picked = tryTypes[0];
|
||||||
for (const t of tryTypes) {
|
for (const t of tryTypes) {
|
||||||
blob = await new Promise<Blob | null>(resolve => {
|
blob = await new Promise<Blob | null>(resolve => {
|
||||||
canvas.toBlob(b => resolve(b), t.mime, t.quality);
|
canvas.toBlob(b => resolve(b), t.mime, t.quality);
|
||||||
|
|||||||
Reference in New Issue
Block a user