【自用笔记】小程序 van-uploader 压缩图片

文章讲述了如何在微信小程序中使用van-uploader组件进行文件上传,通过before-read事件对图片进行压缩,确保图片大小在600像素内,并展示了如何使用canvas和wx.canvasToTempFilePath进行图片质量压缩和临时路径获取的过程。
摘要由CSDN通过智能技术生成

主要是用到canvas进行压缩,参考地方找不到了,如果找到,会修改添加

下面直接上代码:

我使用的是van-uploader组件进行上传的,所以我们可以在before-read事件中进行处理,那么,我的 .wxml的代码如下(去除了不相关部分,仅余压缩相关内容)

<!-- 设置use-before-read为true,并控制before-read事件进行压缩 -->
<van-uploader file-list="{{ imgList }}" max-count="5" upload-icon="scan" bind:after-read="afterRead" bind:delete="imgDelete" use-before-read="{{ true }}" bind:before-read="beforeRead" />
<!-- 在压缩中需要用到,这里通过css控制在界面上不可见 -->
<canvas style="width:{{canvasWidth}}px;height:{{canvasHeight}}px;position:fixed;left:100%;" id="picCanvas" type="2d"></canvas>

.js中代码如下(仅余压缩相关内容,其他部分已去除)

page ({
  data:{
   canvasWidth: '',  //画布宽度
   canvasHeight: '', //画布高度
  }

 //before-read事件
 beforeRead(event) {
    const { file, callback } = event.detail;
    //进入压缩
    this.compressImg(file).then(res => {
        // res为暴露返回的图片地址,赋值给file后再在after中接收
        file.url = res;  
        file.path = res; //和url相同,但在这里不做处理
        callback(file);
    })
  },
   //图片压缩
    compressImg(file){
        let _this = this;
        return new Promise((resolve, reject) => {
        //获取原图片信息 canvas压缩实现
        wx.getImageInfo({
        src: file.url || file.path,
        success: function (res) {
        let canvasRatio = 1.1;
        let picWidth = res.width //图片原始宽高
        let picHeight = res.height
        while (picWidth > 600 || picHeight > 600) { // 保证宽高在600以内(这里可以自行配置)
            picWidth = Math.trunc(res.width / canvasRatio)
            picHeight = Math.trunc(res.height / canvasRatio)
            canvasRatio = canvasRatio + 0.1;
        }
        const query = wx.createSelectorQuery()
        query.select('#picCanvas').fields({
            node: true,
            size: true
        }).exec((res) => {
            try {
            const canvas = res[0].node
            const ctx = canvas.getContext('2d')
            let dpr = wx.getSystemInfoSync().pixelRatio
            //这里是设置css样式的宽高。
            //属性宽高是css样式宽高的dpr倍,兼容不同机型,避免绘制的内容模糊。
            _this.setData({
                canvasWidth: picWidth / dpr,
                canvasHeight: picHeight / dpr
            })
            canvas.width = picWidth
            canvas.height = picHeight

            const img = canvas.createImage()
            img.src = file.url || file.path
            console.log(img.src,'=> path')
            img.onload = () => {
                console.log('延迟1111600')
                ctx.drawImage(img, 0, 0, picWidth, picHeight); //先画出图片
                //延迟600ms,避免部分机型未绘制出图片内容就执行保存操作,导致最终的图片是块白色画板。
                setTimeout(() => {
                    console.log('延迟600')
                wx.canvasToTempFilePath({
                    fileType: "jpg",
                    canvas: canvas,
                    destWidth: picWidth,
                    destHeight: picHeight,
                    success: function (res) {
                    return resolve(res.tempFilePath) //将压缩后的图片地址暴露出去
                    }
                }, _this)
                }, 600)
            }
            } catch (e) {
                //异常后的操作
            }
        })
        }
    })
    })
    },
   //图片上传
  afterRead(e) {
    console.log(e,'=> after')
    let _this = this
    // 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
    const { file } = e.detail;

    // 接下下来就可以进行你的业务操作啦,这里file.url || file.path就已经是压缩后的图片啦
  }


})

延伸一个添加水印的,我在添加水印前,会先压缩一下图,不然太大的图添加水印过慢或者无法添加,这里的压缩是压缩图片质量,大概核心就是算出图片比,然后赋值给canvas宽高,逻辑你们自己想吧,我在这个过程中也是懵懵的,总之是实现效果了,wxml里面同样要加canvas标签,声明canvas宽高变量,然后直接在afterRead事件中获取图片大小(我这里同样封装了,只展示相关内容,其他无关已经剔除):

import { compressImg } from "../../utils/compressImg.js" // 我放在了utils里面,这里的路径要根据你自己的去修改哦

data(){
    canvasWidth: 0, //画布宽
    canvasHeight: 0, //画布高度
}
 
//图片上传
  afterRead(e) {
    let _this = this
    const { file } = e.detail;
    wx.getImageInfo({
      src: file.url || file.path,
      success: function (res) {
        let oWidth = res.width;
        let oHeight = res.height;
        let maxWidth = 1200;
        let maxHeight = 1600;
        let tWidth = oWidth;
        let tHeight = oHeight;
        if (oWidth > maxWidth || oHeight > maxHeight) {
            if (oWidth/oHeight > maxWidth/maxHeight) {
                tWidth = maxWidth;
                tHeight = Math.round(maxWidth * (oHeight/oWidth));
            } else {
                tHeight = maxHeight;
                tWidth = Math.round(maxHeight * (oWidth/oHeight));
            }
        } 
        _this.setData({
          canvasWidth:  tWidth,
          canvasHeight: tHeight
        })
        console.log(_this.data.canvasWidth,_this.data.canvasHeight)
        // _this.addWatermark(file.url || file.path,e.target.dataset.type,e.target.dataset.index);
        let address = _this.data.locationAdds || '';
        let img = file.url || file.path;
        _this.compressUploadImg(img,true,address)
      },
      fail(err) {
          let address = _this.data.locationAdds || '';
          _this.compressUploadImg(file.url || file.path,false,address)
      }
    })
  },
  //压缩添加水印
  compressUploadImg(imgUrl,isMark,adress){
    wx.showLoading({
        title: '图片上传中...',
      })
    let _this = this;
    compressImg(imgUrl,isMark,adress,isUpload).then(res => {
        wx.hideLoading();
        //这里是你获取到的图片回调,进行业务操作
    })
  },

utils - compressImg.js : 

const { timeNow } = require("./util.js")

/**
 * base6转为图片地址
 * @param {*} base64 图片base64
 * @param {*} cb callback回调
 */
const base64ImgtoFile = (base64, cb) => {
    const fsm = wx.getFileSystemManager();
    const FILE_BASE_NAME = new Date().getTime(); //自定义文件名(这里以时间戳为文件名)
    const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
    if (!format) {
        return (new Error('ERROR_BASE64SRC_PARSE'));
    }
    const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.png`;
    const buffer = wx.base64ToArrayBuffer(bodyData);
    fsm.writeFile({
        filePath,
        data: buffer,
        encoding: 'binary',
        success() {
          cb(filePath);
        },
        fail() {
          return (new Error('ERROR_BASE64SRC_WRITE'));
        },
    });
}

/**
 * 给图片添加水印
 * @param {String} imageUrl  图片链接
 * @param {*} canvasWidth    图片宽度
 * @param {*} canvasHeight   图片高度
 * @param {String} address  水印地址
 */
  const addWatermark = (imageUrl,canvasWidth,canvasHeight,address) => {
    return new Promise((resolve, reject)=> {
        wx.showLoading({
            title: '图片生成中...',
        })
        const query = wx.createSelectorQuery();
        query.select('#canvas').fields({
          node: true,
          size: true
        }).exec((res) => {
            const canvas = res[0].node;
            const ctx = canvas.getContext('2d');
            canvas.width = canvasWidth
            canvas.height = canvasHeight
             // 绘制背景图片
            const image = canvas.createImage();
            image.src = imageUrl;
            image.onload = () => {
                ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight);   // 绘制图片到画布上
                let fontSi = canvasWidth / 780 * 18
                ctx.font = `normal ${fontSi}px null`;
                ctx.fillStyle = '#fff';
                ctx.textBaseline = 'bottom';
                ctx.shadowColor = "black"; // 阴影颜色
                ctx.shadowOffsetX = 0 // 水平偏移量
                ctx.shadowOffsetY = 0  // 垂直偏移量
                ctx.shadowBlur = 10  // 模糊度

                // 绘制经纬度
                ctx.fillText(address || '', 10, canvasHeight - 5);

                // 绘制时间
                ctx.fillText(timeNow(), 10, canvasHeight - fontSi - 5);

                // 绘制星期
                // ctx.fillText(this.data.week, 20, canvasHeight - 115);

                wx.canvasToTempFilePath({
                    canvas,
                    success: (res) => { 
                        resolve(res.tempFilePath)
                    },
                    fail: () => {
                        imgurl = imageUrl
                        reject(new Error('转换为图片失败'));
                    }
                })
            }
        })
    })
  }


/**
 * 压缩图片\添加水印
 * @param {*} imageUrl         图片地址 
 * @param {Boolean} isMark     是否要添加水印 
 * @param {String} address     水印地址 
 */
const compressImg = (imageUrl,isMark,address) => {
    var imageUrl = imageUrl;
    var isMark = isMark || false;
    let _this = this;
    return new Promise((resolve, reject) => {
      //获取原图片信息 canvas压缩实现
      wx.getImageInfo({
        src: imageUrl,
        success: async function (res) {
             if(isMark) {
                await addWatermark(imageUrl,res.width,res.height,address).then(res => {
                    imageUrl = res
                }) 
            }
            wx.showLoading({
                title: '图片压缩中...',
            })
            //  我怕之前的压缩无效,这里会再次进行压缩(你们可以自行删减)
            var quality = 0.9;   //压缩系数0-1之间,越小越模糊
            let picWidth = res.width  //图片原始宽高
            let picHeight = res.height
            let maxWidth = 1200;  //最大宽幅
            let maxHeight = 1600; //最大高度
            let tWidth = picWidth;
            let tHeight = picHeight;
            if (picWidth > maxWidth || picHeight > maxHeight) {
                if (picWidth/picHeight > maxWidth/maxHeight) {
                    tWidth = maxWidth;
                    tHeight = Math.round(maxWidth * (picHeight/picWidth));
                } else {
                    tHeight = maxHeight;
                    tWidth = Math.round(maxHeight * (picWidth/picHeight));
                }
            }
            const query = wx.createSelectorQuery()
            query.select('#picCanvas').fields({
                node: true,
                size: true
            }).exec((res) => {
                try {
                const canvas = res[0].node
                const ctx = canvas.getContext('2d')
                canvas.width = tWidth
                canvas.height = tHeight
                const img = canvas.createImage()
                img.src = imageUrl
                img.onload = () => {
                    ctx.drawImage(img, 0, 0, tWidth, tHeight); //先画出图片
                    var base64 = canvas.toDataURL("image/jpeg",quality ); //压缩语句(压缩体积,不改变宽高大小,quality可以动态改变)
                    base64ImgtoFile(base64, resCurrent => {
                         let objInfo = {};
                            objInfo.imageUrl = resCurrent;
                            return resolve(objInfo)
                    })
                }
                } catch (e) {
                    //异常后的操作
                }
            })
        }
      })
    })
}


module.exports = {
    compressImg
}

目前这样的操作,在添加完水印的情况下,可以让一个11M的图片压缩到576KB,还是可以满足我的需求的,如果你们有更好的,希望可以留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值