主要是用到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,还是可以满足我的需求的,如果你们有更好的,希望可以留言