需求背景
APP
中上传图片可以拍照单个上传,也可以从相册中选择多个上传,要为它们都添加上水印,这里底层使用的就是 uni.chooseImage:
代码逻辑
废话不多说,直接上核心代码:
html
<template>
<canvas v-if="waterMarkParams.display" canvas-id="waterMarkCanvas" :style="canvasStyle"/>
</template>
<script>
export default {
data() {
return {
waterMarkParams: {
display: false, // 控制 canvas 创建与销毁
canvasWidth: 300, // 默认宽度
canvasHeight: 225, // 默认高度
contentHeight: 150, // 将要被绘制到图像中的矩形的高度(px)
},
}
},
computed: {
canvasStyle() {
return {
position: 'fixed', // 移除到屏幕外
left: '9999px',
width: this.waterMarkParams.canvasWidth + 'px',
height: this.waterMarkParams.canvasHeight + 'px',
}
}
},
methods: {
chooseImage() {
uni.chooseImage({
sourceType: ['all'],
success: async ({ tempFilePaths, tempFiles }) => {
// 这里就得到了带水印的图片路径列表
const imgFileArr = await this.callAddWaterMark(tempFilePaths)
}
})
},
// 因为有可能在相册中选择多个图片,所以这里要依次生成水印
async callAddWaterMark(imgPathArr, sourceType) {
let results = []
if(imgPathArr.length > 0) {
let addIndex = 0
while(addIndex < imgPathArr.length) {
const tempFilePath = await this.addWaterMark(imgPathArr[addIndex], sourceType)
results.push(tempFilePath)
addIndex = addIndex + 1
}
}
return results
},
// 添加水印
addWaterMark(src, sourceType) {
return new Promise((resolve, reject) => {
// 获取图片信息,配置 canvas 尺寸
uni.getImageInfo({
src,
success: res => {
// 修复部分手机(如红米9)手机屏幕比较窄拍摄出来的图片水印压缩着覆盖的问题
this.waterMarkParams.canvasWidth = Math.max(res.width, 886);
this.waterMarkParams.canvasHeight = res.height;
this.waterMarkParams.display = true
console.log('当前图片信息waterMarkParams:', this.waterMarkParams);
// 等待 canvas 元素创建
this.$nextTick(() => {
let context = uni.createCanvasContext("waterMarkCanvas", this);
/* 绘制 */
const { canvasWidth, canvasHeight, contentHeight } = this.waterMarkParams
// 绘制前清空画布
context.clearRect(0, 0, canvasWidth, canvasHeight);
// 将图片src放到cancas内,宽高必须为图片大小
context.drawImage(src, 0, 0, canvasWidth, canvasHeight, canvasWidth, canvasHeight);
// 设置边框的透明度
context.setGlobalAlpha(0.3);
context.beginPath();
// 绘制底部的白色背景
context.rect(0, canvasHeight - contentHeight, canvasWidth, contentHeight);
context.setFillStyle("white");
context.fill();
// 设置文字的透明度
context.setGlobalAlpha(1);
// 3.绘制底部的文字
context.setFontSize(32);
context.setTextAlign("left");
context.setFillStyle("black");
context.fillText(`拍摄人:${this.$store.state.userModule.name}`, 50 , canvasHeight - 90 );
context.fillText(`拍摄时间:${this.$u.timeFormat(new Date(), "yyyy-mm-dd hh:MM:ss")}`, 50, canvasHeight - 40);
// 一定要加上一个定时器否则进入到页面第一次可能会无法正常拍照,后几次才正常
setTimeout(() => {
// 本次绘画完重开开始绘画,并且在绘画完毕之后再保存图片,不然页面可能会出现白屏等情况
context.draw(false, () => {
console.log('!!!!!开始绘画', canvasWidth, canvasHeight);
uni.canvasToTempFilePath({
canvasId: "waterMarkCanvas",
fileType: "jpg",
width: canvasWidth,
height: canvasHeight,
destWidth: canvasWidth,
destHeight: canvasHeight,
success: ({ tempFilePath }) => {
console.log('绘制成功', tempFilePath);
this.waterMarkParams.display = false
resolve(tempFilePath)
},
fail: err => {
reject(err)
console.log(err);
}
}, this)
})
}, 1000);
})
}
})
})
},
}
}
</script>
上面最重要的一点就是每次生成水印前要通过切换 this.waterMarkParams.display
的值让 canvas
元素每次都重新创建,否则调用 uni.canvasToTempFilePath
生成的图片经常会只有原图左上角的一部分。
效果
参考
感谢我的好室友的文章给了我参考,他的原文链接放在这了:uniapp制作水印相机给图片添加水印并且保存图片至本地