前端拍照添加水印
注:本篇文章使用的是vant UI 的upload上传功能
一、upload 调起摄像头拍照
<van-uploader
v-model="fileList"
:before-read="beforeRead"
:after-read="afterRead"
@delete="delImgUrl"
:max-count="9"
class="uploader"
capture="camera"
accept="image/*"
preview-image
>
v-model 绑定图片列表
before-read 上传的前置操作,可做图片压缩
after-read 成功读取upload上传文件的回调,===**在这里添加水印并上传图片给后端,最终返回添加了水印的图片网址。
delete 点击图片删除图标的操作
max-count 限制上传数量
capture=“camera” 调起摄像头(移动端),pc端只能添加本地图片。
accept=“image/*” 允许上传的文件类型
preview-image 图片上传完成后展示预览图
二、使用步骤
1.基础数据
代码如下(示例):
fileList: [],// [{url:'XXX'},{url:'XXX'}]
wmConfig1: {
//水印配置1
font: "microsoft yahei", //字体
textArray: [], //水印文本内容,允许数组最大长度3 即:3行水印
density: 1, //密度 建议取值范围1-5 值越大,水印越多,可能会导致水印重叠等问题,慎重!!!
texttranslate: [40, 80],
position: "top",
},
wmConfig2: {
//水印配置2
font: "microsoft yahei",
textArray: [],
density: 1,
texttranslate: [40, 580],
position: "bom",
},
2.具体操作,
这里将文件写成了组件
注:photoUrl 是父组件传值格式:photoUrl :“XXX;XXX;XXX”。
其中XXX代表每一个图片地址。所以子组件需要修改为fileList:[{url:‘XXX’},{url:‘XXX’}]数组的格式来显示图片。
代码如下(示例):
<template>
<div>
<van-uploader
v-model="fileList"
:after-read="afterRead"
:disabled="!isCanEdit"
:deletable="isCanEdit"
@delete="delImgUrl"
:max-count="9"
class="uploader"
capture="camera"
accept="image/*"
name="pictureFile"
preview-image
>
<!-- <span>点击调用摄像头进行拍摄capture="camera" accept="image/*"</span> -->
</van-uploader>
</div>
</template>
<script>
import moment from "moment";
import Compressor from "compressorjs";
export default {
props: {
photoUrl: {
type: String,
},
isCanEdit: {
type: Boolean,
},
dataParams: {
type: Object,
},
ind: {
type: Number,
},
},
created() {
this.userId = sessionStorage.getItem("userId");
},
data() {
return {
userId: "",
fileList: [],
wmConfig1: {
//水印配置1
font: "microsoft yahei", //字体
textArray: [], //水印文本内容,允许数组最大长度3 即:3行水印
density: 1, //密度 建议取值范围1-5 值越大,水印越多,可能会导致水印重叠等问题,慎重!!!
texttranslate: [40, 80],
position: "top",
},
wmConfig2: {
//水印配置2
font: "microsoft yahei", //字体
textArray: [], //水印文本内容,允许数组最大长度3 即:3行水印
density: 1, //密度 建议取值范围1-5 值越大,水印越多,可能会导致水印重叠等问题,慎重!!!
texttranslate: [40, 580],
position: "bom",
},
};
},
watch: {
// 监听父组件数据变化,数据回显
photoUrl: {
handler(newVal) {
this.fileList = [];
if (newVal && newVal.length > 0) {
let arr = newVal.split(";");
arr.map((item) => {
// 数据回显
this.fileList.push({
url: item,
});
});
}
},
immediate: true,
},
},
methods: {
async afterRead(file) {
file.status = "uploading";
file.message = "上传中...";
let base64 = file.content;
this.setWMConfig1();
this.setWMConfig2();
let res = await this.base64AddWaterMaker(base64); //加水印的重点就是这个(小冰)
file.file.content = res;
file.file = this.base64ImgtoFile(res); //base64转图片
// 向后端 请求,返回url地址
this.uploadImg(file.file);
setTimeout(() => {
file.status = "succes";
}, 300);
},
// 水印文字配置,可配置多个setWMConfig1、setWMConfig2...
setWMConfig1() {
// 获取拍照时间
let weekStr = this.getWeekday();
let dateStr = moment().format("YYYY/MM/DD");
let timeStr = moment().format("HH:mm:ss");
this.wmConfig1.textArray.push(weekStr, dateStr, timeStr);
},
setWMConfig2() {
// 获取地址,导购员,门店信息
this.wmConfig2.textArray.push(
this.userId || "",
this.dataParams.addressDetail || "",
this.dataParams.storeMDMCode || ""
);
},
// 1.添加水印
base64AddWaterMaker(base64Img) {
let that = this;
return new Promise((resolve, reject) => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const img = new Image();
let resultBase64 = null;
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
//canvas绘制图片,0 0 为左上角坐标原点
ctx.drawImage(img, 0, 0);
//写入水印
that.drawWaterMark(ctx, img.width, img.height, that.wmConfig1);
that.drawWaterMark(ctx, img.width, img.height, that.wmConfig2);
resultBase64 = canvas.toDataURL("image/png");
if (!resultBase64) {
reject();
} else {
resolve(resultBase64);
}
};
img.src = base64Img;
});
},
// 2.绘制水印
drawWaterMark(ctx, imgWidth, imgHeight, wmConfig) {
// fontSize 文字大小, texttranslate水印位置
let fontSize;
if (imgWidth >= 3456) {
fontSize = 80;
wmConfig.texttranslate[0] = 80;
wmConfig.texttranslate[1] =
wmConfig.position == "top" ? 120 : imgHeight - 400;
} else if (imgWidth >= 2700) {
wmConfig.texttranslate[0] = 70;
wmConfig.texttranslate[1] =
wmConfig.position == "top" ? 100 : imgHeight - 370;
fontSize = 70;
} else if (imgWidth >= 2000) {
fontSize = 60;
wmConfig.texttranslate[0] = 60;
wmConfig.texttranslate[1] =
wmConfig.position == "top" ? 80 : imgHeight - 350;
} else if (imgWidth >= 1436) {
fontSize = 50;
wmConfig.texttranslate[0] = 50;
wmConfig.texttranslate[1] =
wmConfig.position == "top" ? 80 : imgHeight - 300;
} else if (imgWidth >= 800) {
fontSize = 40;
wmConfig.texttranslate[0] = 40;
wmConfig.texttranslate[1] =
wmConfig.position == "top" ? 80 : imgHeight - 250;
} else if (imgWidth >= 500) {
fontSize = 30;
wmConfig.texttranslate[0] = 30;
wmConfig.texttranslate[1] =
wmConfig.position == "top" ? 80 : imgHeight - 200;
} else {
fontSize = 26;
wmConfig.texttranslate[0] = 30;
wmConfig.texttranslate[1] =
wmConfig.position == "top" ? 50 : imgHeight - 200;
}
ctx.fillStyle = "white";
ctx.font = `${fontSize}px ${wmConfig.font}`;
ctx.lineWidth = 1;
ctx.fillStyle = "rgba(255,255,255,1)";
ctx.textAlign = "left";
ctx.textBaseline = "middle";
//文字坐标
const maxPx = Math.max(imgWidth, imgHeight);
const stepPx = Math.floor(maxPx / wmConfig.density);
let arrayX = [0]; //初始水印位置 canvas坐标 0 0 点
while (arrayX[arrayX.length - 1] < maxPx / 2) {
arrayX.push(arrayX[arrayX.length - 1] + stepPx);
}
arrayX.push(
...arrayX.slice(1, arrayX.length).map((el) => {
return -el;
})
);
for (let i = 0; i < arrayX.length; i++) {
for (let j = 0; j < arrayX.length; j++) {
ctx.save();
ctx.translate(0, 0); //画布旋转原点 移到 图片中心
ctx.translate(...wmConfig.texttranslate); //水印位置
// ctx.rotate(-Math.PI / 5);//水印旋转
if (wmConfig.textArray.length > 3) {
wmConfig.textArray = wmConfig.textArray.slice(0, 3);
}
wmConfig.textArray.forEach((el, index) => {
let offsetY = fontSize * index + 2;
ctx.fillText(el, arrayX[i], arrayX[j] + offsetY);
});
ctx.restore();
}
}
},
// 3.base64转文件
base64ImgtoFile(dataurl, filename = "file") {
let arr = dataurl.split(",");
let mime = arr[0].match(/:(.*?);/)[1];
let suffix = mime.split("/")[1];
let bstr = atob(arr[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime,
});
},
// 4.上传给后端,图片存到数据库,并转化为前端需要的图片地址。
uploadImg(file) {
let formData = new FormData();
formData.append("pictureFile", file);//请求体 pictureFile:图片文件
this.$axios({
url: "???这里是后端提供的地址" + this.userId,//地址:"XXX?userId=",入参:this.userId
method: "post",
data: formData,
responseType: "*",
})
.then((res) => {
//这里的url为后端接口
if (res.data.code == 200) {
let url = "";
if (this.photoUrl.length > 0) {
url = this.photoUrl + ";" + res.data.msg;
} else {
url = res.data.msg;
}
this.getImgUrl(url);
}
//res 为接口返回值
})
.catch(() => {});
},
// 点击删除操作
delImgUrl(e) {
this.fileList.filter((item) => item == e);
this.$emit("delImgUrl", this.fileList, this.ind);
let url = this.$api.moblieTask.deleteImage;
let param = { pictureUrl: e.url };
this.$post(url, param).then((res) => {
console.log("deleteImage", res);
});
},
// 暴露数据,修改父组件数据
getImgUrl(url) {
this.$emit("getImgUrl", url, this.ind);
},
// 获取当前时间是星期几
getWeekday(dateStr) {
const weekDays = [
"星期日",
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
];
// const date = new Date(dateStr);
const date = new Date();
const weekday = date.getDay();
return weekDays[weekday];
},
},
};
</script>
<style>
</style>
总结
以上就是今天要讲的内容,本文重点是canvas绘图添加水印的使用。