目录
Canvas 是 HTML5 提供的一个用于在网页上绘制图形的元素,它可以实现图片压缩的功能。而结和UnoApp是一种基于Canvas的图形编辑工具库,它提供了丰富的API和功能,可以帮助开发者在网页上实现各种图形编辑和处理操作。
实现图片压缩的原理在结和UnoApp中也是类似的,主要是通过Canvas的绘图功能来重新绘制图片并调整尺寸和质量。下面将详细介绍结和UnoApp实现图片压缩的步骤:
创建组件
<template>
<view class="compress" v-if="canvasId">
<canvas :canvas-id="canvasId" :style="{ width: canvasSize.width, height: canvasSize.height }"></canvas>
</view>
</template>
<script>
export default {
data() {
return {
pic: '',
canvasSize: {
width: 0,
height: 0
},
canvasId: ''
};
},
mounted() {
// 创建 canvasId
if (!uni || !uni._helang_compress_canvas) {
uni._helang_compress_canvas = 1;
} else {
uni._helang_compress_canvas++;
}
this.canvasId = `compress-canvas${uni._helang_compress_canvas}`;
},
methods: {
// 压缩
compressFun(params) {
return new Promise(async (resolve, reject) => {
// 等待图片信息
let info = await this.getImageInfo(params.src)
.then((info) => info)
.catch(() => null);
if (!info) {
reject('获取图片信息异常');
return;
}
// 设置最大 & 最小 尺寸
const maxSize = params.maxSize || 1080;
const minSize = params.minSize || 640;
// 当前图片尺寸
let { width, height } = info;
// 非 H5 平台进行最小尺寸校验
// #ifndef H5
if (width <= minSize && height <= minSize) {
resolve(params.src);
return;
}
// #endif
// 最大尺寸计算
//(图像的宽度和高度是否超过最大尺寸。如果其中任一维度超过最大尺寸,代码将对图像进行调整,以使其适应最大尺寸并保持其宽高比。)
// 这样可以确保图像在调整大小后仍保持原始比例,并且不会超过指定的最大尺寸
if (width > maxSize || height > maxSize) {
if (width > height) {
height = Math.floor(height / (width / maxSize));
width = maxSize;
} else {
width = Math.floor(width / (height / maxSize));
height = maxSize;
}
}
// 设置画布尺寸
this.$set(this, 'canvasSize', {
width: `${width}px`,
height: `${height}px`
});
// Vue.nextTick 回调在 App 有异常,则使用 setTimeout 等待DOM更新
setTimeout(() => {
// 创建 canvas 绘图上下文(指定 canvasId)。在自定义组件下,第二个参数传入组件实例this,以操作组件内 <canvas/> 组件
// Tip: 需要指定 canvasId,该绘图上下文只作用于对应的 <canvas/>
const ctx = uni.createCanvasContext(this.canvasId, this);
// 清除画布上在该矩形区域内的内容。(x,y,宽,高)
ctx.clearRect(0, 0, width, height);
// 绘制图像到画布。(所要绘制的图片资源,x,y,宽,高)
ctx.drawImage(info.path, 0, 0, width, height);
// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中。
// 本次绘制是否接着上一次绘制,即reserve参数为false,则在本次调用drawCanvas绘制之前native层应先清空画布再继续绘制;若reserver参数为true,则保留当前画布上的内容,本次调用drawCanvas绘制的内容覆盖在上面,默认 false
// 绘制完成后回调
ctx.draw(false, () => {
// 把当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径。在自定义组件下,第二个参数传入自定义组件实例,以操作组件内 <canvas> 组件。
uni.canvasToTempFilePath(
{
x: 0, //画布x轴起点(默认0)
y: 0, //画布y轴起点(默认0)
width: width, //画布宽度(默认为canvas宽度-x)
height: height, //画布高度(默认为canvas高度-y
destWidth: width, //图片宽度(默认为 width * 屏幕像素密度)
destHeight: height, //输出图片高度(默认为 height * 屏幕像素密度)
canvasId: this.canvasId, //画布标识,传入 <canvas/> 的 canvas-id(支付宝小程序是id、其他平台是canvas-id)
fileType: params.fileType || 'png', //目标文件的类型,只支持 'jpg' 或 'png'。默认为 'png'
quality: params.quality || 0.9, //图片的质量,取值范围为 (0, 1],不在范围内时当作1.0处理
success: (res) => {
// 在H5平台下,tempFilePath 为 base64
resolve(res.tempFilePath);
},
fail: (err) => {
reject(null);
}
},
this
);
});
}, 300);
});
},
// 获取图片信息
getImageInfo(src) {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src,
success: (info) => {
resolve(info);
},
fail: (err) => {
console.log(err, 'err===获取图片信息');
reject(null);
}
});
});
},
// 批量压缩
async compress(params) {
// 初始化状态变量
let [index, done, fail] = [0, 0, 0];
let paths = [];
// 处理待压缩图片列表
let waitList = Array.isArray(params.src) ? params.src : [params.src];
// 批量压缩方法
let batch = async () => {
while (index < waitList.length) {
try {
const path = await next();
done++;
paths.push(path);
params.progress?.({ done, fail, count: waitList.length });
} catch (error) {
fail++;
params.progress?.({ done, fail, count: waitList.length });
}
index++;
}
};
// 单个图片压缩方法
let next = () => {
const currentSrc = waitList[index];
return this.compressFun({
src: currentSrc,
maxSize: params.maxSize,
fileType: params.fileType,
quality: params.quality,
minSize: params.minSize
});
};
// 返回Promise并处理结果
return new Promise((resolve, reject) => {
try {
batch()
.then(() => {
if (typeof params.src === 'string') {
resolve(paths[0]);
} else {
resolve(paths);
}
})
.catch((error) => {
reject(error);
});
} catch (error) {
reject(error);
}
});
}
}
};
</script>
<style lang="scss" scoped>
.compress {
position: fixed;
width: 12px;
height: 12px;
overflow: hidden;
top: -99999px;
left: 0;
}
</style>
在 script
中引用组件
import helangCompress from '../../components/helang-compress/helang-compress';
export default {
components: {
helangCompress
}
}
在 template
中添加组件
<helang-compress ref="helangCompress"></helang-compress>
方法说明:
方法名 | 可传参数 | 必填 | 说明 |
---|---|---|---|
compress() | Object | 是 | 【单张压缩】设置压缩的图片及压缩尺寸和压缩质量,参数详情请阅读下方 compress()方法参数 |
compress() 方法参数:
参数名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
src | String / Array | 是 | - | 待压缩的图片地址,由相册选择或拍照获取。注意:若src 的值为String 压缩成功后则返回String ,若为Array ,压缩成功后则返回Array |
maxSize | Number | 否 | 1080 | 压缩后的最大尺寸 |
fileType | String | 否 | 'png' | 压缩后的文件类型,可选值 jpg、png |
quality | Number | 否 | 0.9 | 压缩后的质量(仅jpg类型有效,原因可自行阅读官方canvas文档),可选值 0 ~ 1,值越大越清晰(图片也越大) |
minSize | Number | 否 | 640 | 最小压缩尺寸,图片尺寸小于该时值不压缩,非H5平台有效。若想忽略该设置可以将该值设置为一个极小的值,比如一个负数(不能是0) |
为使插件在 H5 平台统一返回 base64 数据,则故意取消 H5 平台对 minSize 的设置。H5平台压缩后的图片路径是 base64 格式,有跨平台需求须注意
progress 方法回调对象属性详细说明:
参数名 | 类型 | 说明 |
---|---|---|
done | Number | 已完成压缩的数量 |
fail | Number | 压缩失败的数量 |
count | Number | 总共需要压缩的数量 |
源码示例:
// 单张压缩
this.$refs.helangCompress.compressFun({
src:this.paths[0],
maxSize:800,
fileType:'jpg',
quality:0.85,
minSize:640 //最小压缩尺寸,图片尺寸小于该时值不压缩,非H5平台有效。若需要忽略该设置,可设置为一个极小的值,比如负数。
}).then((res)=>{
// 压缩成功回调
}).catch((err)=>{
// 压缩失败回调
})
// 批量压缩
this.$refs.helangCompress.compress({
src:this.paths,
maxSize:800,
fileType:'jpg',
quality:0.85,
progress:(res)=>{
console.log('压缩进度');
console.log(res);
}
}).then((res)=>{
// 压缩成功回调
}).catch((err)=>{
// 压缩成功回调
})