javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > 微信小程序图片压缩

微信小程序开发指南之图片压缩解决方案

作者:国服第二切图仔

在项目开发过程中遇到一个需要从小程序上传图片的需求,此需求实现起来并不难,下面这篇文章主要给大家介绍了关于微信小程序开发指南之图片压缩解决方案的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

前言:

由于公司业务拓展,急需基于uniapp生成支付宝小程序。之前已经成功将微信小程序和H5融合成一套码,故得知此需求的时候,笔者信心十足,但是本着实践出真知的想法,觉得还是得先调研一下uniapp在支付宝小程序的兼容性,并集成已有项目主体关键功能,为后续的技术调研方案做准备。在调研过程中,发现之前封装好的图片压缩方法在支付宝小程序上无法正常使用,重新阅读了官方文档后,又双更新了项目的图片压缩方法的使用流程。

问题:现有的压缩方案支付宝小程序不生效

之前封装好的压缩方案,原理是使用canvas现实的,但是在支付宝小程序端不生效,canvas相关的api存在但是不可用。

解决方案:

查阅文档后,给canvas添加了id区分支付宝小程序,可兼容之。

以下是官方文档原文

uni.createCanvasContext(canvasId, this)

#定义

画布表示,传入定义在 <canvas/> 的 canvas-id或id(支付宝小程序是id、其他平台是canvas-id)

核心代码展示:

1,封装公用工具类compressImage.js

/**
 * 给的文件资源是否小于LimitSize (M), 小于走lessCallBack, 大于走moreCallBack
 */
export function imageSizeIsLessLimitSize(imagePath, limitSize, lessCallBack, moreCallBack) {
  uni.getFileInfo({
    filePath: imagePath,
 
    success(res) {
      console.log('压缩前图片大小:', res.size / 1024, 'kb')
 
      if (res.size > 1024 * 1024 * limitSize) {
        moreCallBack()
      } else {
        lessCallBack()
      }
    }
  })
} // 主调用方法
 
/**
 * 获取小于限制大小的Image, limitSize默认为1M,递归调用。
 */
 
export function getLessLimitSizeImage(canvasId, imagePath, limitSize = 1, drawWidth, callBack) {
  imageSizeIsLessLimitSize(
    imagePath,
    limitSize,
    (lessRes) => {
      callBack(imagePath)
    },
    (moreRes) => {
      uni.getImageInfo({
        src: imagePath,
        success: function(imageInfo) {
          var maxSide = Math.max(imageInfo.width, imageInfo.height) //画板的宽高默认是windowWidth
 
          var windowW = drawWidth
          var scale = 1
 
          if (maxSide > windowW) {
            scale = windowW / maxSide
          }
 
          var imageW = Math.floor(imageInfo.width * scale)
          var imageH = Math.floor(imageInfo.height * scale)
          console.log('调用压缩', imageW, imageH)
          getCanvasImage(canvasId, imagePath, imageW, imageH, (pressImgPath) => {
            getLessLimitSizeImage(canvasId, pressImgPath, limitSize, drawWidth * 0.7, callBack)
          })
        }
      })
    }
  )
}
/**
 * 获取画布图片
 */
 
export function getCanvasImage(canvasId, imagePath, imageW, imageH, getImgsuccess) {
  const ctx = uni.createCanvasContext(canvasId)
  ctx.drawImage(imagePath, 0, 0, imageW, imageH)
  ctx.draw(false, () => {
    uni.canvasToTempFilePath({
      canvasId: canvasId,
      x: 0,
      y: 0,
      width: imageW,
      height: imageH,
      quality: 1,
 
      success(res) {
        getImgsuccess(res.tempFilePath)
      }
    })
  })
}
export default {
  getLessLimitSizeImage,
  imageSizeIsLessLimitSize,
  getCanvasImage
}

2,html调用并上传服务器:

<template>
  <view class="upload-page">
    <view class="upload-tips">您最多可上传{{maxCount}}张图片</view>
    <view class="image-list">
      <view v-for="(item, index) in fileList"
            :key="index"
            class="image-item">
        <image class="image"
               mode="aspectFit"
               :src="item.path"
               @click="viewImage(index)"></image>
        <image class="image-delete"
               :src="imgUrl + 'doctor/img_delete.png'"
               @tap.stop="deleteImage"
               :data-index="index"></image>
      </view>
      <view v-if="fileList.length < maxCount"
            class="image-item"
            @tap="openAlbum">
        <image class="image"
               :src="imgUrl + 'doctor/img_add.png'"></image>
      </view>
    </view>
    <view class="upload-btn"
          @tap="confirmUpload">确定</view>
 
    <canvas canvas-id="pressCanvas"
            id="pressCanvas"
            class="press-canvas"></canvas>
  </view>
</template>
 
<script>
import { getLessLimitSizeImage } from './compressImage.js'
 
 
export default {
  data() {
    return {
      imgUrl: process.env.VUE_APP_RESOURCE_URL,
      page: {
        json: {}
      },
      type: '',
      fileList: [],
      maxCount: 9
    }
  },
 
  components: {},
  props: {},
 
  onLoad(options) {
    this.type = options.type
    this.page.json = options
  },
 
  methods: {
    confirmUpload() {
      if (this.fileList.length === 0) {
        this.$util.showToast('请至少选择一张图片')
        return false
      }
      this.$Router.back()
      uni.$emit(this.page.json.emit, this.fileList)
    },
 
    // 查看图片
    viewImage(index) {
      let copyData = [],
        data = [].concat(this.fileList)
      data.forEach((v) => {
        copyData.push(v.path)
      })
      uni.previewImage({
        current: copyData[index],
        urls: copyData
      })
    },
 
    // 删除图片
    deleteImage(e) {
      let { index } = e.currentTarget.dataset
      this.fileList.splice(index, 1)
    },
 
    // 打开相册
    openAlbum() {
      let length = this.maxCount - this.fileList.length
      uni.chooseImage({
        count: length,
        sizeType: ['original', 'compressed'],
        sourceType: ['album', 'camera'],
        success: (res) => {
          this.upLoadImgs(res.tempFilePaths, this.type)
        }
      })
    },
 
    // 上传多张图片
    upLoadImgs(files, type) {
      uni.showLoading()
      let promises = files.map((item) => {
        return this.uploadImg(item, type)
      })
      Promise.all(promises)
        .then((datas) => {
          // 所有上传完成后
          this.fileList = datas.length > 0 && this.fileList.concat(datas)
 
          uni.hideLoading()
        })
        .catch(() => {
          uni.hideLoading()
        })
    },
 
    // 上传图片
    uploadImg(file, type) {
      return new Promise((resolve, reject) => {
        getLessLimitSizeImage('pressCanvas', file, 1, 750, (imagePath) => {
          /* #ifdef H5 */
          let devicetype = 'h5'
          /* #endif */
          /* #ifdef MP-WEIXIN */
          let devicetype = 'applet'
          /* #endif */
 
          /* #ifdef MP-ALIPAY */
          let devicetype = 'alipay'
          /* #endif */
 
          uni.uploadFile({
            url: process.env.VUE_APP_API_URL + 'client/v1/file/images',
            header: {
              'access-token': this.$store.state.user.accessToken,
              version: process.env.VUE_APP_VERSION,
              'version-code': process.env.VUE_APP_VERSION_CODE,
              devicetype: devicetype
            },
            fileType: 'image',
            filePath: imagePath,
            name: 'file',
            formData: {
              source: 'inquiryApply',
              type:  ''
            },
            success: (res) => {
              let image = JSON.parse(res.data)
              console.log('uploadFile success:', image)
 
              if (image.code === 200) {
                resolve(image.data[0])
              } else {
                this.$util.showModal(image.msg || '图片上传失败')
                reject(image)
              }
            },
            fail: (err) => {
              console.log('uploadFile fail:', JSON.stringify(err || {}))
 
              if (err.hasOwnProperty('errMsg') && err.errMsg.indexOf('timeout') > 0) {
                this.$util.showModal('上传超时,请稍后再试')
              } else {
                this.$util.showModal('图片上传失败,请稍后再试')
              }
 
              reject(err)
            },
            complete: () => {}
          })
        })
      })
    }
  }
}
</script>
<style lang="scss" scoped>
.upload-page {
  position: relative;
  width: 750rpx;
  height: 100vh;
  background-color: #ffffff;
}
 
.upload-tips {
  width: 750rpx;
  height: 80rpx;
  background-color: #fffbe8;
  font-size: 28rpx;
  color: #ed6a0c;
  line-height: 80rpx;
  text-align: center;
}
 
.image-list {
  box-sizing: border-box;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: center;
  width: 750rpx;
  padding: 30rpx;
}
 
.image-item {
  position: relative;
  box-sizing: border-box;
  width: 210rpx;
  height: 210rpx;
  margin-right: 30rpx;
  margin-bottom: 30rpx;
  border: 1rpx solid #ebedf0;
}
 
.image-item:nth-child(3n) {
  margin-right: 0;
}
 
.image-item .image {
  width: 210rpx;
  height: 210rpx;
}
 
.image-item .image-delete {
  position: absolute;
  top: -30rpx;
  right: -30rpx;
  width: 40rpx;
  height: 40rpx;
  padding: 10rpx;
}
 
.upload-btn {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 750rpx;
  height: 100rpx;
  background-color: $primary;
  font-size: 30rpx;
  color: #ffffff;
  line-height: 100rpx;
  text-align: center;
}
 
.press-canvas {
  position: absolute;
  top: -1000px;
  left: -1000px;
  background-color: gray;
  width: 750px;
  height: 750px;
}
</style>

附:微信小程序图片压缩(支持多张图)

原理上是使用canvas实现的,小于100k的图片不做压缩。

var {getCanvasImg} = require('../../common/img-compression.js'); // 在page上面按需引入

然后在wx.chooseImage接口成功回调中调用(使用tempFiles字段,因为它包含了图片大小)

let tempFiles = result.tempFiles;
let i = 0;
var arr = [];
// par1:this; par2:图片压缩数组,含size字段;par3:递增值,默认先传入0; par4:接收压缩结果数组; par5:设置画布大小回调函数; par6:设置压缩后的图片数组
getCanvasImg(that, tempFiles, i, that.data.quality, arr, that.setCanvasSize, that.setCompImg);

在定义两个回调函数,用于设置画布大小和接收压缩结果

// 设置画布大小
setCanvasSize(cWidth, cHeight){
    this.setData({
      cWidth,
      cHeight
    });
  },
  // 接收压缩结果
  setCompImg(imagePath){
    this.setData({
      imagePath // 压缩结果
    })
  },

至此使用步骤已介绍完毕

小结:

以上就是笔者分享的图片压缩上传的方法封装及使用啦,完美兼容支付宝小程序,微信小程序及H5三端。

到此这篇关于微信小程序开发指南之图片压缩解决方案的文章就介绍到这了,更多相关微信小程序图片压缩内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文