127 lines
3.5 KiB
TypeScript
127 lines
3.5 KiB
TypeScript
/**
|
|
* 图片处理工具函数
|
|
* 用于压缩和切片图片
|
|
*/
|
|
|
|
/**
|
|
* 加载图片文件为HTMLImageElement
|
|
*/
|
|
export function loadImage(file: File): Promise<HTMLImageElement> {
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof window === 'undefined') {
|
|
reject(new Error('仅在浏览器环境中支持图片处理'))
|
|
return
|
|
}
|
|
const img = window.document.createElement('img')
|
|
img.onload = () => {
|
|
URL.revokeObjectURL(img.src)
|
|
resolve(img)
|
|
}
|
|
img.onerror = (err) => {
|
|
URL.revokeObjectURL(img.src)
|
|
reject(err)
|
|
}
|
|
img.src = URL.createObjectURL(file)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 将Canvas转换为File
|
|
*/
|
|
export function canvasToFile(
|
|
canvas: HTMLCanvasElement,
|
|
origin: File,
|
|
quality = 0.85,
|
|
suffix = ''
|
|
): Promise<File> {
|
|
return new Promise((resolve, reject) => {
|
|
canvas.toBlob(
|
|
(blob) => {
|
|
if (!blob) {
|
|
reject(new Error('图片压缩失败'))
|
|
return
|
|
}
|
|
const baseName = origin.name.replace(/\.[^/.]+$/, '')
|
|
const ext = '.jpg'
|
|
const fileName = suffix ? `${baseName}${suffix}${ext}` : `${baseName}${ext}`
|
|
resolve(new File([blob], fileName, { type: 'image/jpeg' }))
|
|
},
|
|
'image/jpeg',
|
|
quality
|
|
)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 预览图:只做压缩,不切片
|
|
*/
|
|
export async function compressPreviewImage(file: File): Promise<File> {
|
|
const img = await loadImage(file)
|
|
const maxWidth = 800
|
|
const maxHeight = 2000
|
|
let { width, height } = img
|
|
const ratio = Math.min(maxWidth / width, maxHeight / height, 1)
|
|
const canvas = document.createElement('canvas')
|
|
canvas.width = Math.round(width * ratio)
|
|
canvas.height = Math.round(height * ratio)
|
|
const ctx = canvas.getContext('2d')
|
|
if (!ctx) throw new Error('无法获取画布上下文')
|
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
|
|
return await canvasToFile(canvas, file, 0.85)
|
|
}
|
|
|
|
/**
|
|
* 展示图:压缩 + 切片,高度>1500则按 1500px 一段切
|
|
*/
|
|
export async function compressAndSliceDisplayImage(file: File): Promise<File[]> {
|
|
const img = await loadImage(file)
|
|
const maxWidth = 800
|
|
const maxHeight = 2000
|
|
let { width, height } = img
|
|
const ratio = Math.min(maxWidth / width, maxHeight / height, 1)
|
|
const targetWidth = Math.round(width * ratio)
|
|
const targetHeight = Math.round(height * ratio)
|
|
const sliceMaxHeight = 1500
|
|
|
|
const results: File[] = []
|
|
|
|
if (targetHeight <= sliceMaxHeight) {
|
|
const canvas = document.createElement('canvas')
|
|
canvas.width = targetWidth
|
|
canvas.height = targetHeight
|
|
const ctx = canvas.getContext('2d')
|
|
if (!ctx) throw new Error('无法获取画布上下文')
|
|
ctx.drawImage(img, 0, 0, targetWidth, targetHeight)
|
|
const f = await canvasToFile(canvas, file, 0.85)
|
|
results.push(f)
|
|
return results
|
|
}
|
|
|
|
// 需要切片
|
|
const sliceCount = Math.ceil(targetHeight / sliceMaxHeight)
|
|
for (let i = 0; i < sliceCount; i++) {
|
|
const sliceTop = i * sliceMaxHeight
|
|
const sliceHeight = Math.min(sliceMaxHeight, targetHeight - sliceTop)
|
|
const canvas = document.createElement('canvas')
|
|
canvas.width = targetWidth
|
|
canvas.height = sliceHeight
|
|
const ctx = canvas.getContext('2d')
|
|
if (!ctx) throw new Error('无法获取画布上下文')
|
|
ctx.drawImage(
|
|
img,
|
|
0,
|
|
sliceTop / ratio,
|
|
width,
|
|
sliceHeight / ratio,
|
|
0,
|
|
0,
|
|
targetWidth,
|
|
sliceHeight
|
|
)
|
|
const f = await canvasToFile(canvas, file, 0.85, `-part${i + 1}`)
|
|
results.push(f)
|
|
}
|
|
return results
|
|
}
|
|
|