前置内容
uni-app为图片添加自定义水印(解决生成图片不全问题)
UI
升级
现在水印样式变成这样了:
代码
<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, // 默认高度
}
}
},
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) {
let results = []
if(imgPathArr.length > 0) {
let addIndex = 0
while(addIndex < imgPathArr.length) {
const tempFilePath = await this.addWaterMark(imgPathArr[addIndex])
results.push(tempFilePath)
addIndex = addIndex + 1
}
}
return results
},
addWaterMark(src) {
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 } = this.waterMarkParams
// 绘制前清空画布
context.clearRect(0, 0, canvasWidth, canvasHeight);
// 将图片src放到cancas内,宽高必须为图片大小
context.drawImage(src, 0, 0, canvasWidth, canvasHeight, canvasWidth, canvasHeight);
// 防伪码的位置
const fangweiY = 320;
// 保证水印能完整显示出来
const [x, y] = [canvasWidth / 2, canvasHeight - (fangweiY + 100)];
context.translate(x, y);
// 标记一下坐标系,更容易理解
/* context.beginPath();
context.lineWidth = 2;
context.strokeStyle = 'white';
context.moveTo(- x, 0);
context.lineTo(x, 0);
context.moveTo(0, -y);
context.lineTo(0, canvasHeight - y);
context.stroke();
context.closePath(); */
fillText(context, 0, 70, '上海市·金山区', 'bold 56px "Microsoft YaHei"', 'white', 'center');
fillText(context, -20, 220, '16:08', 'bold 150px "Microsoft YaHei"', 'white', 'right');
fillRect(context, -4, 100, 8, 120, '#346DFF');
fillText(context, 20, 140, '2024.04.18', 'bold 40px "Microsoft YaHei"', 'white', 'left');
fillText(context, 20, 210, '星期四 晴 21℃', 'bold 40px "Microsoft YaHei"', 'white', 'left');
fillText(context, 0, fangweiY, `防伪:JY20240418160748XIAOMI`, 'bold 40px "Microsoft YaHei"', 'white', 'center');
/* 绘制marker图标 */
// 蓝色外圆
fillCircle(context, -260, 30, 30, '#346DFF');
// 白色内圆
fillCircle(context, -260, 30, 12, 'white');
// 蓝色三角
fillTriangle(context, -260, 78, -235, 48, -285, 48, '#346DFF');
// 坐标原点移动到画布右上角
context.translate(canvasWidth / 2, -y);
const [rectWidth, rectHeight, rectX, rectY, lineWidth, lineHeight] = [200, 90, -550, 60, 300, 6];
// 右上角矩形
fillRect(context, rectX, 60, rectWidth, rectHeight, '#346DFF');
// 右上角下划线
fillRect(context, rectX + rectWidth, rectY + rectHeight - lineHeight, lineWidth, lineHeight, '#346DFF');
// 右上角姓名
fillText(context, rectX + rectWidth / 2, 120, '鹏北海', 'bold 40px "Microsoft YaHei"', 'white', 'center');
// 右上角手机号码
fillText(context, rectX + rectWidth + lineWidth / 2, 120, '15888888888', 'bold 34px "Microsoft YaHei"', 'white', 'center');
// 一定要加上一个定时器否则进入到页面第一次可能会无法正常拍照,后几次才正常
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);
})
}
})
})
// 绘制文字
function fillText(context, x, y, content, font, fontStyle, textAlign) {
// 保存当前绘图状态
context.save();
// 设置字体样式
context.font = font
// 设置文字颜色为白色
context.fillStyle = fontStyle
// 设置文字水平居中对齐
context.textAlign = textAlign
context.fillText(content, x, y)
// 恢复到之前保存的绘图状态,清除样式设置
context.restore();
}
// 绘制圆
function fillCircle(context, x, y, r, fillStyle) {
// 保存当前绘图状态
context.save();
context.beginPath();
context.arc(x, y, r, 0, 2 * Math.PI);
context.fillStyle = fillStyle;
context.fill();
context.closePath();
// 恢复到之前保存的绘图状态,清除样式设置
context.restore();
}
// 绘制三角形
function fillTriangle(context, x1, y1, x2, y2, x3, y3, fillStyle) {
// 保存当前绘图状态
context.save();
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(x3, y3);
context.fillStyle = fillStyle;
context.fill();
context.closePath();
// 恢复到之前保存的绘图状态,清除样式设置
context.restore();
}
// 绘制矩形
function fillRect(context, x, y, width, height, fillStyle) {
// 保存当前绘图状态
context.save();
context.fillStyle = fillStyle;
context.fillRect(x, y, width, height);
// 恢复到之前保存的绘图状态,清除样式设置
context.restore();
}
},
}
}
</script>