微信小程序 图片压缩
主要是通过
canvas
进行压缩 兼容wx.compressImage
API
wx.compressImage
还没测试,canvas
缩放宽高比才能达到压缩
utils/compressImage.js
// compressImage.js
/**
* @param {object} img 包含path:图片的path,size:图片的大小
* @param {object} canvas canvas对象
* @param {number} fileLimit 文件大小限制
* @returns {Promise} 返回Promise对象
*/
function _compressImage(img, canvas, fileLimit) {
return wx.getSystemInfo().then(res => {
let {
// 设备像素比
pixelRatio,
// 设备品牌
system
} = res;
// 是否是IOS系统
let isIOS = /(ios)/ig.test(system);
// 文件限制
fileLimit = fileLimit || 2 * 1024 * 1024;
// 基础大小
let baseSize = 1280;
// 大于文件限制,手动压缩
if (img.size > fileLimit) {
return compressImg({src:img.path, size:img.size, canvas, baseSize, isIOS, pixelRatio}).then(response => {
return Promise.resolve(response);
});
}
return Promise.resolve(img.path);
});
}
/**
* @description 根据图片的大小选择压缩的方式
* @param {string} src 图片的path
* @param {number} size 图片的大小
* @param {object} canvas canvas对象
* @param {number} baseSize 基础尺寸
* @param {boolean} isIOS 是否是IOS系统
* @returns {Promise} 返回Promise对象
*/
function compressImg({src, size, canvas, baseSize, isIOS, pixelRatio}) {
return new Promise((resolve, reject) => {
wx.getImageInfo({
src
}).then(res => {
let imgWidth = res.width;
let imgHeight = res.height;
if (imgWidth <= 4096 && imgHeight <= 4096) {
// 小于4096使用canvas压缩
canvasToImage({src, size, imgWidth, imgHeight, canvas, baseSize, isIOS, pixelRatio}).then(response => {
resolve(response);
});
} else {
// 超过4096使用强制压缩
compressImage(src, size, isIOS).then(response => {
resolve(response);
});
}
}).catch(err => {
// 使用强制压缩
compressImage(src, size, isIOS).then(response => {
resolve(response);
});
});
});
}
/**
* @description 使用wx.compressImage压缩图片
* @param {string} src 图片的path
* @param {number} size 图片的大小
* @param {boolean} isIOS 是否是IOS系统
* @returns {Promise} 返回Promise对象
*/
function compressImage(src, size, isIOS) {
return new Promise((resolve, reject) => {
let quality = 100;
if (isIOS) {
quality = 0.1;
} else {
let temp = 30 - (size / 1024 / 1024);
quality = temp < 10 ? 10 : temp;
}
wx.compressImage({
src,
quality,
success: (res) => {
resolve(res.tempFilePath);
},
fail: () => {
// 压缩失败返回传递的图片src
resolve(src);
}
});
});
}
/**
* @description 使用canvans压缩图片
* @param {string} src 图片的path
* @param {number} size 图片的大小
* @param {number} imgWidth 图片的宽度
* @param {number} imgHeight 图片的高度
* @param {object} canvas canvas对象
* @param {number} baseSize 基础尺寸
* @param {boolean} isIOS 是否是IOS系统
* @param {number} pixelRatio 设备像素比
* @returns {Promise} 返回Promise对象
*/
function canvasToImage({src, size, imgWidth, imgHeight, canvas, baseSize, isIOS, pixelRatio}) {
return new Promise((resolve, reject) => {
if (!canvas) {
compressImage(src, size).then(res => {
resolve(res);
});
return;
}
// 设置canvas宽度和高度
let canvasWidth = 0;
let canvasHeight = 0;
let quality = 1;
// 图片的宽度和高度都小于baseSize,宽高不变
if (imgWidth <= baseSize && imgHeight <= baseSize) {
canvasWidth = imgWidth;
canvasHeight = imgHeight;
quality = 0.4;
} else {
let compareFlag = true;
// 图片的一边大于baseSize,宽高不变
if (pixelRatio > 2 && (imgWidth > baseSize || imgHeight > baseSize) && (imgWidth < baseSize || imgHeight < baseSize)) {
canvasWidth = imgWidth;
canvasHeight = imgHeight;
quality = 0.4;
} else {
// 按照原图的宽高比压缩
compareFlag = pixelRatio > 2 ? (imgWidth > imgHeight) : (imgWidth > imgHeight);
// 宽比高大,宽按基准比例缩放,高设置为基准值,高比宽大,高按基准比例缩放,宽设置为基准值。
canvasWidth = compareFlag ? parseInt(imgWidth / (imgHeight / baseSize)) : baseSize;
canvasHeight = compareFlag ? baseSize : parseInt(imgHeight / (imgWidth / baseSize));
quality = 0.4;
}
}
//将画布缩小,否则质量还是很大
canvasWidth = canvasWidth * quality
canvasHeight = canvasHeight * quality
//宽高必须和绘制尺寸一样。否则展示不全
canvas.width = canvasWidth;
canvas.height = canvasHeight;
let pic = canvas.createImage();
pic.src = src;
pic.onerror = function () {
// 加载失败使用强制压缩
compressImage(src, size, isIOS).then(response => {
resolve(response);
});
}
pic.onload = function () {
// 获取绘画上下文
let ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(pic, 0, 0, canvasWidth, canvasHeight);
// 导出图片
wx.canvasToTempFilePath({
canvas,
width: canvasWidth,
height: canvasHeight,
destHeight: canvasHeight,
destWidth: canvasWidth,
fileType:'jpg',
quality,
success: (res) => {
resolve(res.tempFilePath);
},
fail: (err) => {
// 压缩失败使用强制压缩
compressImage(src, size, isIOS).then(response => {
resolve(response);
});
}
});
}
});
}
/**
* @description 循环压缩图片
* @param {object} img 包含path:图片的path,size:图片的大小
* @param {object} canvas canvas对象
* @param {number} fileLimit 文件大小限制
* @returns {Promise} 返回Promise对象
*/
async function cycleCompressImg(img, canvas, fileLimit) {
let fileSystemManager = wx.getFileSystemManager();
function getFileInfoPromise(src) {
return new Promise((resolve, reject) => {
fileSystemManager.getFileInfo({
filePath: src,
success: (res) => {
resolve(res);
},
fail: (err) => {
reject(err);
}
});
});
}
let size = await getFileInfoPromise(img.path).size;
let path = img.path;
while (size > fileLimit) {
path = await _compressImage(img, canvas, fileLimit);
}
return path;
}
module.exports = {
_compressImage,
cycleCompressImg
};
wxml
<canvas type="2d" id="uploadCanvas" class="canvas" ></canvas>
wxss
.canvas {
width: 100rpx;
height: 100rpx;
opacity: 0;
position: absolute;
z-index: -1;
display: none;
}
js
const {
compressImage ,
_compressImage
} = require('../../utils/compressImage.js');
Page({
//获取文件信息
getFileInfo(path){
return new Promise((resolve, reject) => {
wx.getFileSystemManager().getFileInfo({
filePath:path,
success(rr){
resolve(rr)
},
fail(err){
reject(err)
}
})
})
},
//选择图片
chooseImg(e) {
const { id } = e.currentTarget.dataset
if (this.data[id]) {
return
}
const that = this
// 选择图片
wx.chooseImage({
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
count: 1,
success: async (res)=> {
let path = res.tempFilePaths[0]
// 如果大于1mb则去压缩
if (res.tempFiles[0].size > 1 * 1024 * 1024) {
let canvas = await that.getCanvas('uploadCanvas')
if (canvas) {
//此处1 * 1024 * 1024 是否大于1mb,大于则压缩
path = await _compressImage(res.tempFiles[0], canvas, 1 * 1024 * 1024)
let fileInfo = await that.getFileInfo(path)
if (fileInfo.size > 1 * 1024 * 1024 ) {//判断是否压缩过后依然大于1mb
wx.showToast({
title: '上传图片文件必须小于5MB', //因为根据压缩比例,5mb的文件通过压缩后大概有几百k,所以实际上传能有5mb
icon: "none"
})
return
}
}
}
that.setData({
[id]: path
})
}
})
},
// 获取canvas,用于压缩图片
getCanvas(id) {
return new Promise((resolve, reject) => {
let query = wx.createSelectorQuery();
query.in(this);
query.select(`#${id}`).fields({
node: true,
size: true
}).exec((res) => {
if (res[0]) {
resolve(res[0].node);
} else {
resolve(null);
}
});
});
},
})