在 uni-app 小程序 中实现 多图选择 → 压缩 → 保存相册 的功能。
一、介绍
- 压缩参数设置:用户通过滑块设置压缩质量和缩放比例
- 保存到相册:压缩完成后,保存到手机相册
- 效果预览:
- 在线体验:
二、核心逻辑
1. 静态页面
<canvas
canvas-id="compressCanvas"
id="compressCanvas"
:style="`position:absolute;left:-9999px;width:${canvasWidth}px;height:${canvasHeight}px;`"
>
</canvas>
2. 选择图片
selectImage() {
uni.chooseImage({
count: 9,
sourceType: ['album', 'camera'],
success: (res) => {
this.imageList = this.imageList.concat(res.tempFilePaths);
}
});
}
从 相册 和 相机 选择,最多可选 9 张。
3. 批量压缩与进度提示
async startCompress() {
if (this.imageList.length === 0) return;
this.compressProgress = 0;
let completed = 0;
for (const img of this.imageList) {
const compressed = await this.compressImage(img);
await this.saveToAlbum(compressed);
completed++;
this.compressProgress = Math.floor((completed / this.imageList.length) * 100);
}
uni.showToast({ title: '已保存到相册!', icon: 'success' });
}
4. 压缩图片
压缩采用 Canvas 绘制方式,先根据设置的缩放比例计算目标宽高,再通过 canvasToTempFilePath 导出。
compressImage(imgPath) {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: imgPath,
success: (imgInfo) => {
// 目标宽高(根据缩放比例计算)
const targetWidth = Math.max(1, Math.floor(imgInfo.width * (this.size / 100)));
const targetHeight = Math.max(1, Math.floor(imgInfo.height * (this.size / 100)));
// 动态修改 canvas 实际大小
this.canvasWidth = targetWidth;
this.canvasHeight = targetHeight;
// 延时 300ms,等待 canvas 宽高改变
setTimeout(() => {
// 创建画布上下文
const ctx = uni.createCanvasContext('compressCanvas', this);
// 清理画布
ctx.clearRect(0, 0, targetWidth, targetHeight);
// 绘制整张图片 → 覆盖整个画布
ctx.drawImage(imgPath, 0, 0, targetWidth, targetHeight);
// 必须等 draw 执行完,再导出
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'compressCanvas',
x: 0,
y: 0,
width: targetWidth, // 截取区域
height: targetHeight, // 截取区域
destWidth: targetWidth, // 导出宽
destHeight: targetHeight, // 导出高
fileType: 'jpg',
quality: this.quality / 100,
success: (res) => {
resolve({
originalPath: imgPath,
compressedPath: res.tempFilePath,
width: targetWidth,
height: targetHeight
});
},
fail: (err) => {
console.error('导出失败', err);
reject(err);
}
}, this);
});
}, 300);
},
fail: (err) => reject(err)
});
});
}
5. 保存到相册
压缩后的临时文件路径可以直接用 uni.saveImageToPhotosAlbum 保存。
saveToAlbum(filePath) {
return new Promise((resolve, reject) => {
uni.saveImageToPhotosAlbum({
filePath,
success: () => resolve(filePath),
fail: reject
});
});
}
五、注意事项
- 图片被裁剪:原因是canvas宽高与图片不一致,compressImage方法中使用setTimeout等待canvas的宽高生效,确保图片完整(nextTick不行,暂不清楚原因 )。
- 图片未保存到相册:小程序预览时没问题,线上无法保存图片到相册,需要在微信公众平台完善用户隐私协议。