前端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,
  }
}
相关推荐
森叶几秒前
Electron 多进程下的“库引入“全解析:核心模块、Electron API、第三方依赖与 utilityProcess 的依赖处理
运维·javascript·electron
cft56200_ln2 分钟前
TDA4时间同步3 网卡添加虚拟时间戳
c语言·开发语言·arm开发·驱动开发·嵌入式硬件·网络协议
LaughingZhu3 分钟前
Product Hunt 每日热榜 | 2026-06-10
前端·人工智能·经验分享·chatgpt·html
打小就很皮...3 分钟前
基于 Python + LangChain + React 实现智能发票识别与验真系统实战
前端·react.js·langchain·ocr·发票识别
惢雨3 分钟前
ts中的特殊符号说明并举例,如 ?. 、?:、??等
前端·typescript
小此方4 分钟前
【别传:Web前端开发(三)】重塑动态视界:JS底层逻辑、数据类型流转与WebAPI交互全景草稿
前端·javascript·交互
粉末的沉淀4 分钟前
css:隐藏video标签的下载按钮
前端·css
仰望.4 分钟前
vue表格使用 vxe-table 展开行实现产品列表与明细列表
前端·javascript·vue.js·vxe-table
geovindu8 分钟前
go: Coroutines Pattern
开发语言·后端·设计模式·golang·协程模式
Stick_ZYZ9 分钟前
A2A:让 Agent 从单兵作战走向团队协作
java·开发语言·网络·人工智能·python·ai