前端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,
  }
}
相关推荐
好家伙VCC11 小时前
# 发散创新:用 Rust构建高并发虚拟世界引擎核心模块在当今游戏开发与元宇宙构建中,**虚拟世界的性能瓶颈往往不是图形渲染,而是底
java·开发语言·python·rust·图形渲染
Liu6288811 小时前
C++中的状态模式
开发语言·c++·算法
smchaopiao11 小时前
使用C语言打印几何图形:从三角形到菱形
c语言·开发语言·算法
JohnsonXin11 小时前
一次线上白屏排查:静态 import 是如何悄悄破坏 Webpack 共享 Chunk 的
前端·webpack·node.js
爱滑雪的码农11 小时前
Java基础六:条件语句与switch case
java·开发语言
我喜欢就喜欢11 小时前
Word 模板匹配与样式同步技术详解
开发语言·c++·qt·word·模板匹配
Mr -老鬼11 小时前
前后端联调避坑!Vue优先IPv6导致接口不通,Rust Salvo这样解决
前端·vue.js·rust
jzlhll12311 小时前
Kotlin Mutex vs Java ReentrantLock vs synchronized
java·开发语言·kotlin
予你@。11 小时前
# Vue2 + Element UI 表格合并实战:第二列按「第一列 + 第二列」条件合并
前端·javascript·vue.js