起因
ant-design 的 uploder 在进行图片裁切后可能尺寸变大。蚂蚁官方推荐是使用 ant-img-crop,这个是基于 react-easy-crop 进行了二次的封装的一个库。通过查看源码,这个裁剪图片其实也是基于 canvas 进行重新绘制,同时库里并没有对图片的尺寸进行限制。
解决
官方有相关issue,推荐自行压缩处理。
图片大小变大的原因
目前可复现的是上传图片源尺寸过大,经过裁剪后图片大小变大了
在满足需求且保证清晰度的情况下。我们选择使用二倍图视窗等比压缩,以iPhone X为标准的视窗大小
在图片源尺寸长/宽大小超过二倍图视窗尺寸时,进行等比压缩。否则不压缩。
// 二倍图尺寸
const DEFAULT_MEDIA_SIZE = {
width: 750,
height: 1624,
}
const getMediaSizeBySource = (sourceSize: {
width: number
height: number
}) => {
// 超过二倍图尺寸等比缩放
if (
sourceSize.width > DEFAULT_MEDIA_SIZE.width ||
sourceSize.height > DEFAULT_MEDIA_SIZE.height
) {
const zoom =
sourceSize.width > DEFAULT_MEDIA_SIZE.width
? DEFAULT_MEDIA_SIZE.width / sourceSize.width
: DEFAULT_MEDIA_SIZE.height / sourceSize.height
return {
width: sourceSize.width * zoom,
height: sourceSize.height * zoom,
}
}
return sourceSize
}
我们使用 canvas 进行等比绘制
const compressFileByCanvas = async (file: RcFile) => {
const canvas = document.createElement('canvas') as HTMLCanvasElement
const ctx = canvas.getContext('2d')
const reader = new FileReader()
reader.readAsDataURL(file as File)
const img = new Image()
reader.onloadend = () => {
img.src = reader.result as string
}
img.onload = () => {
const { width, height } = img
const mediaSize = getMediaSizeBySource({ width, height })
canvas.width = mediaSize.width
canvas.height = mediaSize.height
ctx?.drawImage(img, 0, 0, mediaSize.width, mediaSize.height)
}
}
进行绘制后,我们需要将画布内容转换成文件供 antd uploader 使用
const canvasToFile = (
canvas: HTMLCanvasElement,
quality: number,
type: string,
): Promise<Blob> =>
new Promise(resolve => {
canvas.toBlob(blob => resolve(blob as Blob), type, quality)
})
const file = await canvasToFile(canvas, 1, file.type)