Files
GERP/src/utils/imageCompress.ts
T

62 lines
2.3 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.
/**
* 图片压缩工具 —— 头像上传前的前端预压缩,降低网络延迟
*
* 头像展示尺寸很小(会话列表 38px、成员/消息 30px、编辑预览 96px),
* 上传前压成 AVATAR_UPLOAD_SIZE 见方的 JPEG,可显著减少请求体体积。
*/
/** 头像上传目标边长(正方形,px) */
export const AVATAR_UPLOAD_SIZE = 50
/** 载入图片为可绘制源(优先 ImageBitmap,回退 HTMLImageElement) */
async function LoadImageSource(file: File): Promise<ImageBitmap | HTMLImageElement> {
if (typeof createImageBitmap === 'function') {
return await createImageBitmap(file)
}
const url = URL.createObjectURL(file)
try {
return await new Promise<HTMLImageElement>((resolve, reject) => {
const el = new Image()
el.onload = () => resolve(el)
el.onerror = () => reject(new Error('图片解码失败'))
el.src = url
})
} finally {
URL.revokeObjectURL(url)
}
}
/**
* 将图片居中裁剪为正方形并缩放为 Size×Size 的 JPEG File。
*
* - 透明区域以白色填充(JPEG 不支持透明,避免出现黑底)
* - 非图片、canvas 不可用或解码失败时原样返回,保证上传不中断
*/
export async function compressImageSquare(file: File, Size = AVATAR_UPLOAD_SIZE): Promise<File> {
if (!file.type.startsWith('image/')) return file
try {
const source = await LoadImageSource(file)
const canvas = document.createElement('canvas')
canvas.width = Size
canvas.height = Size
const ctx = canvas.getContext('2d')
if (!ctx) return file
// 居中裁剪正方形后缩放
const side = Math.min(source.width, source.height)
const sx = (source.width - side) / 2
const sy = (source.height - side) / 2
ctx.fillStyle = '#ffffff'
ctx.fillRect(0, 0, Size, Size)
ctx.imageSmoothingEnabled = true
ctx.imageSmoothingQuality = 'high'
ctx.drawImage(source, sx, sy, side, side, 0, 0, Size, Size)
if (typeof ImageBitmap !== 'undefined' && source instanceof ImageBitmap) source.close()
const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, 'image/jpeg', 0.9))
if (!blob) return file
const base = file.name.replace(/\.[^.]+$/, '')
return new File([blob], `${base}.jpg`, { type: 'image/jpeg', lastModified: Date.now() })
} catch {
return file
}
}