zoukankan      html  css  js  c++  java
  • 前端/JS/React/ES6:纯前端实现图片压缩技术

    网上图片压缩文章抄来抄去,真没意思,我自己写一个

    import React from 'react';
    // import logo from './logo.svg';
    import './App.css';
    
    function App() {
    
      const handleChange = e => {
        e.persist() // 为了看到原生file的内容,不加这句,原生file内容会被react隐藏
        let fileObj = e.target.files[0]
        // console.log(e)
        console.log(fileObj)
    
        // 设定标准上限为1MB,进行压缩处理
        compress(fileObj, 1024, (data) => {
          console.log(data)
        })
      }
    
      // 异步读取图片的promise
      const loadImageAsync = (url) => {
        return new Promise(function (resolve, reject) {
          const image = new Image()
    
          image.onload = function () {
            resolve(image)
          };
    
          image.onerror = function () {
            reject(new Error('Could not load image at ' + url))
          };
    
          image.src = url
        })
      }
    
      // 异步转换成base64编码的promise
      const fileToImgAsync = (file) => {
        return new Promise(function (resolve, reject) {
          const reader = new FileReader()
    
          reader.onload = function (e) {
            resolve(e.target.result);
          };
    
          reader.onerror = function () {
            reject(new Error('readAsDataURL:fail'))
          };
    
          reader.readAsDataURL(file)
        });
      }
    
      const downloadFileByBlob = (blobUrl, filename) => {
        const a = document.createElement('a')
        a.download = filename
        a.style.display = 'none'
        a.href = blobUrl
        // 触发点击
        document.body.appendChild(a)
        a.click()
        // 然后移除
        document.body.removeChild(a)
      }
    
      // async 搭配 promise 使用
      const compress = async (file, maxSizeKB, succFunc) => {
        if (file.size > 3 * 1024 * 1024) {
          let rate = 0 // 压缩率
    
          // 文件转图片
          const dataUrl = await fileToImgAsync(file)
    
          // 图片转画布
          const image = await loadImageAsync(dataUrl)
          // console.log(dataUrl, image)
    
          // 文件大小KB, file.size给的是字节Byte
          const fileSizeKB = file.size / 1024
          console.log(fileSizeKB)
    
          // 当图片大小超标,才进行压缩
          if (fileSizeKB > maxSizeKB) {
            // 计算压缩率
            rate = (fileSizeKB - maxSizeKB) / fileSizeKB
            console.log('压缩率:', rate)
            console.log('压缩后文件大小:', fileSizeKB * (1 - rate), 'kb')
          }
    
          // 画布执行压缩
          let canvas = document.createElement('canvas')
          let context = canvas.getContext('2d')
          const cvWidth = image.width * (1 - rate)
          const cvHeight = image.height * (1 - rate)
          console.log(image.width, image.height, cvWidth, cvHeight)
    
          canvas.height = cvHeight
          canvas.width = cvWidth
          context.clearRect(0, 0, cvWidth, cvHeight)
          context.drawImage(image, 0, 0, cvWidth, cvWidth)
    
          // 导出图片
          canvas.toBlob((blob) => {
    
            // 方式一:下载到本地
            // const blobUrl = window.URL.createObjectURL(blob)
            // downloadFileByBlob(blobUrl, file.name)
    
            // 方式二:生成网页可读取的对象
            const newImage = new File([blob], file.name, { type: file.type });
            succFunc(newImage)
          });
        }
      }
    
      return (
        <div className="App">
          <input type="file" id="file" onChange={handleChange} />
        </div>
      );
    }
    
    export default App;

    其实我们可以发现一个问题,导出的图片,在window系统中,大小是 207kb并不是1024kb,很明显size的比例用在width height的比例上有出入,要加入调节因子,因此算法还可以优化,如下

    import React from 'react';
    // import logo from './logo.svg';
    import './App.css';
    
    function App() {
    
      const handleChange = e => {
        e.persist() // 为了看到原生file的内容,不加这句,原生file内容会被react隐藏
        let fileObj = e.target.files[0]
        // console.log(e)
        console.log(fileObj)
    
        // 设定标准上限为1MB,进行压缩处理
        compress(fileObj, 1024, (data) => {
          console.log(data)
        })
      }
    
      // 异步读取图片的promise
      const loadImageAsync = (url) => {
        return new Promise(function (resolve, reject) {
          const image = new Image()
    
          image.onload = function () {
            resolve(image)
          };
    
          image.onerror = function () {
            reject(new Error('Could not load image at ' + url))
          };
    
          image.src = url
        })
      }
    
      // 异步转换成base64编码的promise
      const fileToImgAsync = (file) => {
        return new Promise(function (resolve, reject) {
          const reader = new FileReader()
    
          reader.onload = function (e) {
            resolve(e.target.result);
          };
    
          reader.onerror = function () {
            reject(new Error('readAsDataURL:fail'))
          };
    
          reader.readAsDataURL(file)
        });
      }
    
      const downloadFileByBlob = (blobUrl, filename) => {
        const a = document.createElement('a')
        a.download = filename
        a.style.display = 'none'
        a.href = blobUrl
        // 触发点击
        document.body.appendChild(a)
        a.click()
        // 然后移除
        document.body.removeChild(a)
      }
    
      // async 搭配 promise 使用
      const compress = async (file, maxSizeKB, succFunc) => {
        if (file.size > maxSizeKB * 1024) {
          let rate = 0 // 压缩率
    
          // 文件转图片
          const dataUrl = await fileToImgAsync(file)
    
          // 图片转画布
          const image = await loadImageAsync(dataUrl)
          // console.log(dataUrl, image)
    
          // 文件大小KB, file.size给的是字节Byte
          const fileSizeKB = file.size / 1024
          console.log(fileSizeKB)
    
          // 当图片大小超标,才进行压缩
          if (fileSizeKB > maxSizeKB) {
            // 计算压缩率
            rate = (fileSizeKB - maxSizeKB) / fileSizeKB
            console.log('压缩率:', rate)
            console.log('压缩后文件大小:', fileSizeKB * (1 - rate), 'kb')
          }
    
          // 纠正因子,不加会导致压缩出的文件太小
          const factor = 0.2
    
          // 画布执行压缩
          let canvas = document.createElement('canvas')
          let context = canvas.getContext('2d')
          const cvWidth = image.width * (1 - rate + factor)
          const cvHeight = image.height * (1 - rate + factor)
          console.log(image.width, image.height, cvWidth, cvHeight)
    
          canvas.height = cvHeight
          canvas.width = cvWidth
          context.clearRect(0, 0, cvWidth, cvHeight)
          context.drawImage(image, 0, 0, cvWidth, cvWidth)
    
          // 导出图片
          canvas.toBlob((blob) => {
    
            // 方式一:下载到本地
            const blobUrl = window.URL.createObjectURL(blob)
            downloadFileByBlob(blobUrl, file.name)
    
            // 方式二:生成网页可读取的对象
            // const newImage = new File([blob], file.name, { type: file.type });
            // succFunc(newImage)
          });
        }
      }
    
      return (
        <div className="App">
          <input type="file" id="file" onChange={handleChange} />
        </div>
      );
    }
    
    export default App;

     就这样吧,勉强能用,现在导出的都有800-900kb,还行吧

    参考:https://www.cnblogs.com/chenbeibei520/p/12534798.html

  • 相关阅读:
    hdu 5723 Abandoned country 最小生成树 期望
    OpenJ_POJ C16G Challenge Your Template 迪杰斯特拉
    OpenJ_POJ C16D Extracurricular Sports 打表找规律
    OpenJ_POJ C16B Robot Game 打表找规律
    CCCC 成都信息工程大学游记
    UVALive 6893 The Big Painting hash
    UVALive 6889 City Park 并查集
    UVALive 6888 Ricochet Robots bfs
    UVALive 6886 Golf Bot FFT
    UVALive 6885 Flowery Trails 最短路
  • 原文地址:https://www.cnblogs.com/ww01/p/13328664.html
Copyright © 2011-2022 走看看