复制代码
import { ref } from 'vue';
export const useCompressImage = () => {
const compressionConfig = ref({
maxSize: 5 * 1024 * 1024, // 默认5MB 超过5MB进行压缩
maxQuality: 0.9, // 最高压缩质量
minQuality: 0.1, // 最低压缩质量
qualityStep: 0.1 // 质量递减步长
})
const originalImage = ref('')
const compressedImage = ref('')
const compressedSize = ref(0)
const originalDimensions = ref('')
const compressedDimensions = ref('')
const compressing = ref(false)
const originalSize = ref(0)
const onFileChange = (file) => {
originalSize.value = file.size
originalImage.value = URL.createObjectURL(file)
const img = new Image()
img.onload = () => {
originalDimensions.value = `${img.width} x ${img.height}`
}
img.src = originalImage.value
}
const onCompressImage = async (file, config) => {
config = config || compressionConfig.value
return new Promise((resolve1, reject) => {
// 显示压缩状态,清空之前的压缩结果
compressing.value = true
compressedImage.value = ''
try {
// 创建Canvas元素用于图片处理
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const img = new Image()
img.onload = async () => {
// 设置Canvas尺寸与原图一致,保持图片尺寸不变
canvas.width = img.width
canvas.height = img.height
// 将原始图片绘制到Canvas上
ctx.drawImage(img, 0, 0)
// 使用配置参数设置初始压缩质量
let quality = config.maxQuality
let compressedBlob = file
// 检查文件大小是否超过配置的最大值
if (file.size <= config.maxSize) {
// 文件已经小于配置的最大值,无需压缩,直接使用原文件
compressedBlob = file
compressedSize.value = file.size
compressedImage.value = originalImage.value
compressedDimensions.value = originalDimensions.value
compressing.value = false
console.log('文件大小已符合要求,无需压缩')
resolve1({
originalImage: originalImage.value,
compressedImage: compressedImage.value,
compressedSize: compressedSize.value,
originalDimensions: originalDimensions.value,
compressedDimensions: compressedDimensions.value,
compressing: compressing.value,
})
return
}
// 使用递归方式替代循环,避免死循环
const tryCompress = async (currentQuality) => {
return new Promise((resolve) => {
canvas.toBlob(async (blob) => {
if (blob) {
// 检查压缩后的文件是否满足大小要求
if (blob.size <= config.maxSize) {
// 压缩成功,更新状态
compressedSize.value = blob.size
compressedImage.value = URL.createObjectURL(blob)
// 获取压缩后图片的尺寸信息
const compressedImg = new Image()
compressedImg.onload = () => {
compressedDimensions.value = `${compressedImg.width} x ${compressedImg.height}`
}
compressedImg.src = compressedImage.value
compressing.value = false
console.log(`图片压缩完成,目标大小: ${formatFileSize(config.maxSize)}`)
resolve(blob)
resolve1({
originalImage: originalImage.value,
compressedImage: compressedImage.value,
compressedSize: compressedSize.value,
originalDimensions: originalDimensions.value,
compressedDimensions: compressedDimensions.value,
compressing: compressing.value,
})
return
}
// 如果已经降到最低质量但仍未达到目标大小,使用最低质量并结束
if (currentQuality <= config.minQuality) {
compressedSize.value = blob.size
compressedImage.value = URL.createObjectURL(blob)
// 获取最终压缩后图片的尺寸
const compressedImg = new Image()
compressedImg.onload = () => {
compressedDimensions.value = `${compressedImg.width} x ${compressedImg.height}`
}
compressedImg.src = compressedImage.value
compressing.value = false
console.log(`图片已压缩到最小尺寸,当前大小: ${formatFileSize(blob.size)}`)
resolve(blob)
return
}
// 继续尝试更低的质量压缩
const nextQuality = Math.max(currentQuality - config.qualityStep, config.minQuality)
await tryCompress(nextQuality).then(resolve)
}
}, 'image/jpeg', currentQuality)
})
}
// 开始压缩过程
await tryCompress(quality)
}
// 加载原始图片到Image对象,触发图片加载流程
img.src = originalImage.value
} catch (error) {
// 压缩过程中的异常处理
console.error('压缩失败:', error)
ElMessage.error('图片压缩失败')
compressing.value = false
reject(error)
}
})
}
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
return {
originalImage,
compressedImage,
compressedSize,
originalDimensions,
compressedDimensions,
compressing,
onFileChange,
onCompressImage,
}
}