美文网首页微信小程序
微信小程序使用canvas进行按需图片列表压缩

微信小程序使用canvas进行按需图片列表压缩

作者: 羞羞的小桔 | 来源:发表于2020-08-20 11:51 被阅读0次

    概述

    ​ 在微信小程序中使用canvas来对图片进行压缩的例子网上已经有很多了,但是网上的例子都只能对单张图片进行压缩。由于官方接口wx.chooseImage()在实际生产常常被用于进行多张图片的选择,所以我们需要一个方法来对图片列表进行压缩。

    ​ 可是已经有了压缩单张图片的方法,那么循环调用它不就能对图片列表进行压缩了吗?实际上不是。由于在微信小程序中只能够使用wx.createCanvasContext()接口通过wxml中定义的canvas-id来获取canvas对象,继而使用这个对象来进行绘图。因此当循环调用图片压缩方法时,就会导致每次循环都使用同一个canvas对象绘图、导出图片,并且由于canvas的绘图方法是异步进行的,因此最终获取到的压缩后的图片列表就会出现紊乱(亲测)。除非你愿意在wxml中定义多个id不同的canvas。

    ​ 循环压缩图片行不通,因此本文介绍了使用递归的方法来对图片列表进行压缩。由于使用递归来实现列表的循环压缩,因此能够避免canvas异步绘图方法带来的影响。

    其实说到递归,思路就很明显了,不过其中也有一些细节和坑值得记录一下。

    具体实现

    实现步骤

    1. 使用wx.chooseImage()接口选择图片,接口返回图片地址列表。
    2. 递归调用图片压缩方法。
    3. 图片压缩方法中使用wx.getImageInfo()接口获取图片长宽信息。
    4. 根据图片长宽信息计算图片大小,并根据目标图片大小对图片的长宽进行裁剪,以达到压缩图片大小的目的。
    5. 根据canvas-id获取canvas绘图上下文,绘制最终图片后将画布内容导出为图片。

    代码实现

    wxml代码

    • 在wxml文件末尾定义canvas组件,通过设置绝对定位使canvas离屏显示。
    • 设置canvas-id来获取canvas绘图上下文。
    
    <canvas canvas-id="canvas" style="width:{{cWidth}}px;height:{{cHeight}}px;position: absolute;left:-1000px;top:-1000px;"></canvas>
    
    

    js代码

    • 在data中定义cWidth和cHeight属性用以动态设置canvas的长宽。
    • pics数组用以存放和显示压缩后的图片列表。
    
    data: { cWidth: 0, cHeight: 0, pics: []}
    
    
    • wx.chooseImage()的处理。在页面的js文件中调用chooseImage()方法选择图片,继而将选择的图片传递到cutBase64()方法中进行压缩处理。
    chooseImg() {
        // 限制最多选择5张图片
        const count = 5 - this.data.pics.length;
        wx.chooseImage({
            count: count,
            // 在小程序的api中说明设置sizeType=compressed后会返回压缩后的图片,但测试发现,只有图片大小超出某个范围之后才会对图片进行压缩处理
            sizeType: ['original', 'compressed'],
            sourceType: ['album', 'camera'],
            success: res => {
                const urls = res.tempFilePaths;
                // 参数分别为:图片url数组、wxml中设置的canvasId、递归所需index、设置canvas长宽的方法、处理压缩后图片的回调方法
                util.cutBase64(urls, 'canvasId', 0, this.setCanvas, this.setPics);
            }
        })
    },
    /**
     * 通过这个方法设置canvas的长宽,设置后调用回调方法。
     * 如果不使用回调方法,而是直接设置canvas长宽后就进行下一步处理,最终得到的图片大小可能会有跟预期有所差异
     */
    setCanvas(width, height, callback) {
        this.setData({
            cWidth: width,
            cHeight: height
        }, callback);
    },
    /**
     * 对压缩后的图片进行处理
     */
    setPics(url) {
        // 这样处理是因为在我的使用过程中需要得到图片的base64数据,也可以根据实际需求对url做出不同的处理
        wx.getFileSystemManager().readFile({
            filePath: url, //选择图片返回的相对路径
            encoding: 'base64', //编码格式
            success: res => {
                const base64 = 'data:image/png;base64,' + res.data;
                // 打印出压缩后的图片大小
                console.log(base64.length);
                this.setData({
                    pics: this.data.pics.push(base64);
                });
            }
        })
    }
    

    Util工具类:cutBase64()

    const cutBase64 = (urls, canvasId, index, setCanvas, callback) => {
        if (index == urls.length) {
            return;
        }
        // 根据urls及index获取图片信息
        wx.getImageInfo({
            src: urls[index],
            success: (res) => {
                // 设置初始裁剪比例,当图片大小大于设置阈值时,则对图片长宽按次比例进行裁剪
                let ratio = 2;
                let canvasWidth = res.width;
                let canvasHeight = res.height;
                // 根据比例对图片长宽进行裁剪,若裁剪后图片大小依然大于设置阈值时,则提高裁剪比例继续对图片进行裁剪
                // 此处假定需要将图片裁剪至200kb以内,因此 阈值 = 200 * 1024 = 204800
                while (canvasWidth * canvasHeight > 204800) {
                    canvasWidth = Math.trunc(res.width / ratio);
                    canvasHeight = Math.trunc(res.height / ratio);
                    ratio++;
                }
                // 调用setCanvas()方法设置canvas的长宽,设置完成后开始绘图,否则可能会出现"fail canvas is empty"的错误
                setCanvas(canvasWidth, canvasHeight, () => {
                    // 获取canvasId对应的绘图上下文对象
                    let ctx = wx.createCanvasContext(canvasId);
                    ctx.drawImage(urls[index], 0, 0, canvasWidth, canvasHeight);
                    // draw()的第一个参数为false表示不保留canvas上一次的绘制结果
                    // 这里设置100毫秒的延迟是为了在canvas绘图完成后再获取图片信息,否则获取到的图片可能是空白
                    ctx.draw(false, setTimeout(() => {
                        // 获取canvas绘制内容并转为图片
                        wx.canvasToTempFilePath({
                            canvasId: canvasId,
                            destWidth: canvasWidth,
                            destHeight: canvasHeight,
                            fileType: 'jpg', // 可选的参数有:jpg、png
                            success: res => {
                                // 图片导出成功后调用回调
                                callback(res.tempFilePath);
                                // 进行下一次递归,由于此时canvas的绘制与图片的导出都已经结束了,因此不会对canvas后续的绘制、导出产生影响
                                cutBase64(urls, canvasId, ++index, setCanvas, callback);
                            }
                        })
                    }, 100));
                });
            }
        })
    }
    

    相关文章

      网友评论

        本文标题:微信小程序使用canvas进行按需图片列表压缩

        本文链接:https://www.haomeiwen.com/subject/keknrktx.html