前端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,
  }
}
相关推荐
jllllyuz2 小时前
室外可见光通信信道建模与MATLAB实现(直射链路与反射链路)
开发语言·matlab
切糕师学AI2 小时前
Vue 中的 keep-alive 组件
前端·javascript·vue.js
散峰而望2 小时前
【数据结构】假如数据排排坐:顺序表的秩序世界
java·c语言·开发语言·数据结构·c++·算法·github
superman超哥2 小时前
自定义序列化逻辑:掌控数据编码的每一个细节
开发语言·rust·编程语言·rust自定义序列化·rust数据编码
可问春风_ren2 小时前
Git命令大全
前端·javascript·git·后端
她说彩礼65万2 小时前
Jquery总结
前端·javascript·jquery
jiayong232 小时前
JVM垃圾回收机制面试题
java·开发语言·jvm
得一录2 小时前
ES6核心语法
前端·ecmascript·es6
光影少年2 小时前
前端如何定位组件变化及性能问题
前端·javascript·react.js