将需要压缩的图片绘制在canvas,然后利用微信提供的canvasToTempFilePath()方法保存成一个图片,保存时可以根据所需指定生成图片的质量。
通过canvas配合这个接口,可以实现两种图片的压缩:1.声明好图片高度/宽度临界值,压缩图片的宽高;2.不改变图片的宽高,只改变图片的质量;
首先页面需要声明一个canvas画布:其中cWidth和cHeight分别是渲染的图片高度和宽度,根据上传的图片信息获取;-9999px是为了使画布偏离屏幕;
<canvas
canvas-id="canvas"
class="my-canvas"
:style="{width:cWidth+'px',height:cHeight+'px'}">
</canvas>
.canvas {
position: absolute;
left: -9999px;
top: -9999px
}
1.通过修改图片的宽高来压缩图片
通过修改宽高来压缩图片的原理是获取图片的宽高后(res.width/res.height),判断是否满足自己的最高宽/高度要求,如果不满足,则根据比例缩小,直到获取到满足要求的宽高,将其渲染到页面上,这里加一个setTimeout是因为canvas绘制需要一定的时间,需要等canvas绘制完成后将其通过canvasToTempFilePath方法导出;
wx.canvasToTempFilePath(Object object, Object this) | 微信开放文档
通过这种方法有两个弊端:
1.无法完全确定图片的高度,而且在一定情况下两张图片宽高差距比较大(宽高2049px和宽高2047px的两张图片),解决办法:
1.1 减小ratio的初始值,同时减小增长间隔:初始值1.2,增长间隔0.2(ratio += 0.2);
1.2 完全固定图片的高度或者宽度,等比例调整至固定的大小,弊端就是小图片会被拉长;
2.无法实时的判断图片是否渲染完毕(目前我是没找到方法,欢迎指导),尤其是渲染的图片宽高特别大的时候会特别耗时,所设置的延迟可能会导致导出的图片不全/拉伸等情况,解决办法:
2.1 这种修改图片的宽高方法,可以找到一个最大宽高所需渲染的时间,比这个宽高小的图片在当前时间内一定会渲染完成;如果是第二种方法,修改图片的质量,则需要预留一个比较充足的时间,但是特别大的图片可能还会有一样的问题。
async imgCompress(tempFilePaths, index) {
let _this = this
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: tempFilePaths,
success: function(res) {
let ratio = 2;
let canvasWidth = res.width
let canvasHeight = res.height
while (canvasWidth > 1024 || canvasHeight > 1024) {
canvasWidth = Math.trunc(res.width / ratio)
canvasHeight = Math.trunc(res.height / ratio)
ratio++;
}
_this.cWidth = canvasWidth
_this.cHeight = canvasHeight
let ctx = wx.createCanvasContext('canvas')
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(res.path, 0, 0, canvasWidth, canvasHeight)
ctx.draw(false, _this.timer = setTimeout(function() {
wx.canvasToTempFilePath({
canvasId: 'canvas',
destWidth: canvasWidth, // 输出的图片的宽度
destHeight: canvasHeight, // 输出的图片的高度
fileType: 'png', // 图片输出格式
quality: 1, // 图片质量 0-1
success: function(res) {
clearTimeout(_this.timer)
resolve(res.tempFilePath);
},
fail: function(res) {
clearTimeout(_this.timer)
reject(err.errMsg)
}
})
}, 600)) //留一定的时间绘制canvas
},
fail: function(res) {
reject(err.errMsg)
},
})
})
},
2.通过修改图片导出的质量来压缩图片
看过第一种方法的代码,也应该知道这种方法怎么实现了,绘制图片时以原始的宽高来绘制,只是在导出图片的时候,通过修改quality(0-1)的大小来实现压缩图片;
通过这种方法的弊端:
1. quality目前只支持jpg图片,和微信提供的wx.compressImage()方法一样,这就要求导出的图片类型必须是jpg图片;
2. 和第一种方法一样的问题,无法确切的知道图片渲染完成的时间........
async imgCompress(tempFilePaths, index) {
let _this = this
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: tempFilePaths,
success: function(res) {
let ratio = 2;
let canvasWidth = res.width
let canvasHeight = res.height
_this.cWidth = canvasWidth
_this.cHeight = canvasHeight
let ctx = wx.createCanvasContext('canvas')
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(res.path, 0, 0, canvasWidth, canvasHeight)
ctx.draw(false, _this.timer = setTimeout(function() {
wx.canvasToTempFilePath({
canvasId: 'canvas',
destWidth: canvasWidth, // 输出的图片的宽度
destHeight: canvasHeight, // 输出的图片的高度
fileType: 'jpg', // 图片输出格式
quality: 0.2, // 图片质量,0-1
success: function(res) {
clearTimeout(_this.timer)
resolve(res.tempFilePath);
},
fail: function(res) {
clearTimeout(_this.timer)
reject(err.errMsg)
}
})
}, 600)) //留一定的时间绘制canvas
},
fail: function(res) {
reject(err.errMsg)
},
})
})
},