squoosh
browser-image-compression
- 前端压缩图片经常能搜到这个npm包,实际体验很差
- 有些图片压缩很慢,压缩一分钟的都有
- 压缩完一些图片还会变的更大
基于canvas手搓
压缩方法
export function compressImage(file, options = {}) {
const {
quality = 0.75,
format = 'image/jpeg',
maxWidth = 1920,
maxHeight = 1080
} = options
return new Promise((resolve, reject) => {
try {
const img = new Image()
img.onload = async () => {
try {
let { width, height } = img
const aspectRatio = width / height
if (width > maxWidth) {
width = maxWidth
height = Math.round(width / aspectRatio)
}
if (height > maxHeight) {
height = maxHeight
width = Math.round(height * aspectRatio)
}
const canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
const ctx = canvas.getContext('2d')
ctx.imageSmoothingEnabled = true
ctx.imageSmoothingQuality = 'high'
ctx.drawImage(img, 0, 0, width, height)
canvas.toBlob((blob) => {
if (blob) {
resolve(blob)
} else {
reject(new Error('Failed to create blob'))
}
}, format, quality)
} catch (e) {
reject(e)
}
}
img.onerror = () => {
reject(new Error('Failed to load image'))
}
img.src = URL.createObjectURL(file)
} catch (e) {
reject(e)
}
})
}
export function getDimensions(file) {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = () => resolve({ width: img.width, height: img.height })
img.onerror = reject
img.src = URL.createObjectURL(file)
})
}
export function formatSize(bytes) {
if (bytes < 1024) return bytes + ' B'
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
return (bytes / (1024 * 1024)).toFixed(2) + ' MB'
}
export function getCompressionRatio(original, compressed) {
return Math.round((1 - compressed / original) * 100)
}
使用
export function compressImgHandler(file) {
return new Promise((resolve) => {
compressImage(file).then(compressedBlob => {
const compressedFile = new File([compressedBlob], file.name, {
type: compressedBlob.type,
lastModified: Date.now(),
})
console.log('比原图小了', getCompressionRatio(file.size, compressedFile.size) + '%')
if (compressedFile.size > file.size) {
resolve({ file })
} else {
resolve({ file: compressedFile })
}
}).catch(error => {
console.error('图片压缩失败:', error)
resolve({ file })
})
})
}