微信小程序 图片压缩

主要是通过canvas进行压缩 兼容wx.compressImage API
wx.compressImage 还没测试,canvas 缩放宽高比才能达到压缩

utils/compressImage.js

// compressImage.js
/**
 * @param {object} img 包含path:图片的path,size:图片的大小
 * @param {object} canvas canvas对象
 * @param {number} fileLimit 文件大小限制
 * @returns {Promise} 返回Promise对象
 */
function _compressImage(img, canvas, fileLimit) {
    return wx.getSystemInfo().then(res => {
      let {
        // 设备像素比
        pixelRatio,
        // 设备品牌
        system
      } = res;
      // 是否是IOS系统
      let isIOS = /(ios)/ig.test(system);
      // 文件限制
      fileLimit = fileLimit || 2 * 1024 * 1024;
      // 基础大小
      let baseSize = 1280;
      // 大于文件限制,手动压缩
      if (img.size > fileLimit) {
        return compressImg({src:img.path, size:img.size, canvas, baseSize, isIOS, pixelRatio}).then(response => {
          return Promise.resolve(response);
        });
      }
      return Promise.resolve(img.path);
    });
  }
  
  /**
   * @description 根据图片的大小选择压缩的方式
   * @param {string} src 图片的path
   * @param {number} size 图片的大小
   * @param {object} canvas canvas对象
   * @param {number} baseSize 基础尺寸
   * @param {boolean} isIOS 是否是IOS系统 
   * @returns {Promise} 返回Promise对象
   */
  function compressImg({src, size, canvas, baseSize, isIOS, pixelRatio}) {
    return new Promise((resolve, reject) => {
      wx.getImageInfo({
        src
      }).then(res => {
        let imgWidth = res.width;
        let imgHeight = res.height;
        if (imgWidth <= 4096 && imgHeight <= 4096) {
          // 小于4096使用canvas压缩
          canvasToImage({src, size, imgWidth, imgHeight, canvas, baseSize, isIOS, pixelRatio}).then(response => {
            resolve(response);
          });
        } else {
          // 超过4096使用强制压缩
          compressImage(src, size, isIOS).then(response => {
            resolve(response);
          });
        }
      }).catch(err => {
        // 使用强制压缩
        compressImage(src, size, isIOS).then(response => {
          resolve(response);
        });
      });
    });
  }
  
  /**
   * @description 使用wx.compressImage压缩图片
   * @param {string} src 图片的path
   * @param {number} size 图片的大小
   * @param {boolean} isIOS 是否是IOS系统
   * @returns {Promise} 返回Promise对象
   */
  function compressImage(src, size, isIOS) {
    return new Promise((resolve, reject) => {
      let quality = 100;
      if (isIOS) {
        quality = 0.1;
      } else {
        let temp = 30 - (size / 1024 / 1024);
        quality = temp < 10 ? 10 : temp;
      }
      wx.compressImage({
        src,
        quality,
        success: (res) => {
          resolve(res.tempFilePath);
        },
        fail: () => {
          // 压缩失败返回传递的图片src
          resolve(src);
        }
      });
    });
  }
  
  /**
   * @description 使用canvans压缩图片
   * @param {string} src 图片的path
   * @param {number} size 图片的大小
   * @param {number} imgWidth 图片的宽度
   * @param {number} imgHeight 图片的高度
   * @param {object} canvas canvas对象
   * @param {number} baseSize 基础尺寸
   * @param {boolean} isIOS 是否是IOS系统
   * @param {number} pixelRatio 设备像素比
   * @returns {Promise} 返回Promise对象
   */
  function canvasToImage({src, size, imgWidth, imgHeight, canvas, baseSize, isIOS, pixelRatio}) {
    return new Promise((resolve, reject) => {
      if (!canvas) {
        compressImage(src, size).then(res => {
          resolve(res);
        });
        return;
      }
      // 设置canvas宽度和高度
      let canvasWidth = 0;
      let canvasHeight = 0;
      let quality = 1;
      // 图片的宽度和高度都小于baseSize,宽高不变
      if (imgWidth <= baseSize && imgHeight <= baseSize) {
        canvasWidth = imgWidth;
        canvasHeight = imgHeight;
        quality = 0.4;
      } else {
        let compareFlag = true;
        // 图片的一边大于baseSize,宽高不变
        if (pixelRatio > 2 && (imgWidth > baseSize || imgHeight > baseSize) && (imgWidth < baseSize || imgHeight < baseSize)) {
          canvasWidth = imgWidth;
          canvasHeight = imgHeight;
          quality = 0.4;
        } else {
          // 按照原图的宽高比压缩
          compareFlag = pixelRatio > 2 ? (imgWidth > imgHeight) : (imgWidth > imgHeight);
          // 宽比高大,宽按基准比例缩放,高设置为基准值,高比宽大,高按基准比例缩放,宽设置为基准值。
          canvasWidth = compareFlag ? parseInt(imgWidth / (imgHeight / baseSize)) : baseSize;
          canvasHeight = compareFlag ? baseSize : parseInt(imgHeight / (imgWidth / baseSize));
          quality = 0.4;
        }
      }
      //将画布缩小,否则质量还是很大
      canvasWidth = canvasWidth * quality
      canvasHeight = canvasHeight * quality
      //宽高必须和绘制尺寸一样。否则展示不全
      canvas.width = canvasWidth;
      canvas.height = canvasHeight;
      let pic = canvas.createImage();
      pic.src = src;
      pic.onerror = function () {
        // 加载失败使用强制压缩
        compressImage(src, size, isIOS).then(response => {
          resolve(response);
        });
      }
      pic.onload = function () {
        // 获取绘画上下文
        let ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
        ctx.drawImage(pic, 0, 0, canvasWidth, canvasHeight);
        // 导出图片
        wx.canvasToTempFilePath({
          canvas,
          width: canvasWidth,
          height: canvasHeight,
          destHeight: canvasHeight,
          destWidth: canvasWidth,
          fileType:'jpg',
          quality,
          success: (res) => {
            resolve(res.tempFilePath);
          },
          fail: (err) => {
            // 压缩失败使用强制压缩
            compressImage(src, size, isIOS).then(response => {
              resolve(response);
            });
          }
        });
      }
    });
  }
  
  /**
   * @description 循环压缩图片
   * @param {object} img 包含path:图片的path,size:图片的大小
   * @param {object} canvas canvas对象
   * @param {number} fileLimit 文件大小限制
   * @returns {Promise} 返回Promise对象
   */
  async function cycleCompressImg(img, canvas, fileLimit) {
    let fileSystemManager = wx.getFileSystemManager();
  
    function getFileInfoPromise(src) {
      return new Promise((resolve, reject) => {
        fileSystemManager.getFileInfo({
          filePath: src,
          success: (res) => {
            resolve(res);
          },
          fail: (err) => {
            reject(err);
          }
        });
      });
    }
    let size = await getFileInfoPromise(img.path).size;
    let path = img.path;
    while (size > fileLimit) {
      path = await _compressImage(img, canvas, fileLimit);
    }
    return path;
  }
  
  module.exports = {
    _compressImage,
    cycleCompressImg
  };
  

wxml

<canvas type="2d" id="uploadCanvas" class="canvas" ></canvas>

wxss

.canvas {
    width: 100rpx;
    height: 100rpx;
    opacity: 0;
    position: absolute;
    z-index: -1;
    display: none;
  }
  

js

const {
    compressImage ,
    _compressImage
    } = require('../../utils/compressImage.js');
    
	Page({
	
	  //获取文件信息
  getFileInfo(path){
    return new Promise((resolve, reject) => {
        wx.getFileSystemManager().getFileInfo({
            filePath:path,
            success(rr){
                resolve(rr)
            },
            fail(err){
                reject(err)
            }
          })
    })
  },
  //选择图片
  chooseImg(e) {
    const { id } = e.currentTarget.dataset
    if (this.data[id]) {
      return
    }
    const that = this
    // 选择图片
    wx.chooseImage({
      sizeType: ['compressed'],
      sourceType: ['album', 'camera'],
      count: 1,
      success:  async (res)=> {
         let path = res.tempFilePaths[0]
        //  如果大于1mb则去压缩
         if (res.tempFiles[0].size > 1 * 1024 * 1024) {
            let canvas = await that.getCanvas('uploadCanvas')
            if (canvas) {
			//此处1 * 1024 * 1024 是否大于1mb,大于则压缩
                path = await _compressImage(res.tempFiles[0], canvas, 1 * 1024 * 1024)
                let fileInfo =  await that.getFileInfo(path) 
                if (fileInfo.size > 1 * 1024 * 1024 ) {//判断是否压缩过后依然大于1mb
                    wx.showToast({
                        title: '上传图片文件必须小于5MB',  //因为根据压缩比例,5mb的文件通过压缩后大概有几百k,所以实际上传能有5mb
                        icon: "none"
                    })
                    return
                }
            }
        }
        that.setData({
            [id]: path
        })
      }
    })
  },
  // 获取canvas,用于压缩图片
  getCanvas(id) {
    return new Promise((resolve, reject) => {
      let query = wx.createSelectorQuery();
      query.in(this);
      query.select(`#${id}`).fields({
        node: true,
        size: true
      }).exec((res) => {
        if (res[0]) {
          resolve(res[0].node);
        } else {
          resolve(null);
        }
      });
    });
  },
	})
posted @ 2024-03-31 21:08  混名汪小星  阅读(167)  评论(0编辑  收藏  举报