前端js图片压缩

图片压缩

复制代码
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,
  }
}
相关推荐
焰火19993 分钟前
[Vue]可重置的响应式状态reactive
前端·vue.js
陆枫Larry5 分钟前
CSS transform scale:图片放大效果背后的原理
前端
昇腾CANN13 分钟前
TileLang-Ascend 算子性能优化方法与实操
开发语言·javascript·性能优化·昇腾·cann
老王以为14 分钟前
为什么 React 和 Vue 不一样?
前端·vue.js·react.js
web打印社区16 分钟前
2026最新Web静默打印解决方案,无插件无预览,完美替代Lodop
前端·javascript·vue.js·electron·pdf
沐知全栈开发24 分钟前
ionic 手势事件详解
开发语言
这个DBA有点耶31 分钟前
分组排名不用窗口函数?那你还在写几十行的子查询
前端·代码规范
ZhiqianXia34 分钟前
《The Design of Design》阅读笔记
前端·笔记·microsoft
有马贵将42 分钟前
【5】微前端知识点总结
前端·架构
mkae44 分钟前
eBPF高性能版fail2ban
前端