图片上传大家都不会陌生,就算是一个新人也会干的事儿。但说到加水印,当初我一直以为只能是在后端实现。这不,来csdn那么久了,工作中有不懂的也在csdn里找答案。昨天晚上,我特意在csdn里找了个遍。原来,在前端也是能实现图片上传加水印的。(注:以下的代码都来自于csdn,只是我把两部份代码合在一起来实现我的需求而已,在最后面,我也会附上原作者的接,大家都不容易)
【注:我自己写的注释后面会有个"(小冰)",其他的为原作者的注释】
<van-uploader v-model="fileList" multiple:after-read="afterRead"/>
data(){
fileList:[], //vant中图片上传的双向邦定(小冰)
wmConfig : { //用于水印的东西,下面会用到 (小冰)
font: "microsoft yahei", //字体
textArray: ['张三','2021/11/26 16:44'],//水印文本内容,允许数组最大长度3 即:3行水印
density: 3 //密度 建议取值范围1-5 值越大,水印越多,可能会导致水印重叠等问题,慎重!!!
}
}
methods:{
//dataURLtoBlob 、 blobToFile 两个方法的意思是把base64的图片转换成file文件
//1,先将base64转换为blob
dataURLtoBlob(dataurl) {
var arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
},
//2,再将blob转换为file
blobToFile(theBlob, fileName){
theBlob.lastModifiedDate = new Date(); // 文件最后的修改日期
theBlob.name = fileName; // 文件名
return new File([theBlob], fileName, {type: theBlob.type, lastModified: Date.now()});
},
//这个不多说,都懂,文件上传的方法(小冰)
async afterRead(file){
console.log(file)
let base64 = file.content;
let res = await this.base64AddWaterMaker(base64,this.wmConfig) //加水印的重点就是这个(小冰)
file.content = res;
let blob = this.dataURLtoBlob(file.content);
// 拿到文件名
let fileName = file.file.name;
// 2,在转为 file类型
let file1 = this.blobToFile(blob,fileName);
console.log("file1:",file1);
},
base64AddWaterMaker (base64Img, wmConfig) {
let that = this;
if (wmConfig.textArray.length === 0) {
console.error("****没有水印内容*****");
return base64Img;
}
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, wmConfig);
resultBase64 = canvas.toDataURL("image/png");
if (!resultBase64) {
reject();
} else {
resolve(resultBase64);
}
};
img.src = base64Img;
});
},
drawWaterMark (ctx, imgWidth, imgHeight, wmConfig) {
let fontSize;
if (imgWidth >= 3456) {
fontSize = 50;
} else if (imgWidth >= 2700) {
fontSize = 30;
} else if (imgWidth >= 2000) {
fontSize = 26;
} else if (imgWidth >= 1436) {
fontSize = 20;
} else if (imgWidth >= 800) {
fontSize = 12;
} else if (imgWidth >= 500) {
fontSize = 10;
} else {
fontSize = 8;
}
console.log(imgWidth, imgHeight, fontSize);
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;
}));
console.log(arrayX);
for (let i = 0; i < arrayX.length; i++) {
for (let j = 0; j < arrayX.length; j++) {
ctx.save();
ctx.translate(imgWidth / 2, imgHeight / 2); ///画布旋转原点 移到 图片中心
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();
}
}
}
}
代码就是这些,然而,在此中,我是把图片加水印和图片base64转file两个不同的博文混在一起写的。
图片加水印 :【https://blog.csdn.net/King0217/article/details/121562949】
base64转file:【https://blog.csdn.net/qq_43886365/article/details/126729188】
顺便我也把file文件转base64的封装函数也发一下。当然,这个我也是在网上找的。
function fileToBase64(file, callback) { //callback 是一个回调函数,而回调函数往这儿走一回,它结果就是base64
const fileReader = new FileReader()
fileReader.readAsDataURL(file)
fileReader.onload = function () {
callback(this.result)
}
},
function callbaseFun(res){
console.log(res,'res打印出来就是base64')
},
//---------------------------分割线--------------------
第二种方法,跟上面的方法差不多,只是这种方法可以随意去调整水印的位置和大小什么的。
//将base64转化为二进制
dataURItoBlob(base64Data) {
var date = new Date();
//console.log('将base64转化为二进制', date.getMinutes(), date.getSeconds())
var byteString;
if (base64Data.split(',')[0].indexOf('base64') >= 0)
byteString = atob(base64Data.split(',')[1]);
else
byteString = unescape(base64Data.split(',')[1]);
var mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], { type: mimeString });
},
//将base64转化为二进制
dataURItoBlob(base64Data) {
var date = new Date();
//console.log('将base64转化为二进制', date.getMinutes(), date.getSeconds())
var byteString;
if (base64Data.split(',')[0].indexOf('base64') >= 0)
byteString = atob(base64Data.split(',')[1]);
else
byteString = unescape(base64Data.split(',')[1]);
var mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], { type: mimeString });
},
//Base64转Canvas
async imgToCanvas(base64) {
var date = new Date();
//console.log('Base64转Canvas', date.getMinutes(), date.getSeconds())
// 创建img元素
const img = document.createElement('img')
img.setAttribute('src', base64)
await new Promise((resolve) => (img.onload = resolve))
// 创建canvas DOM元素,并设置其宽高和图片一样
const canvas = document.createElement('canvas')
//console.log(img.height)
//console.log(img.width)
canvas.width = img.width
canvas.height = img.height
// 坐标(0,0) 表示从此处开始绘制,相当于偏移。
canvas.getContext('2d').drawImage(img, 0, 0)
return canvas
},
//写入水印
addWatermark(canvas, txt1, txt2, txt3, tex4) {
//alert(canvas.height)
var date = new Date();
//console.log('写入水印', date.getMinutes(), date.getSeconds())
const ctx = canvas.getContext('2d')
//ctx.font = '50px Arial'
//ctx.strokeText(text, 0, 0)
ctx.font = '20px Arial'
ctx.fillStyle = 'white'
ctx.fillText(txt1, 20, canvas.height - 240)
ctx.fillText(txt2, 20, canvas.height - 200)
ctx.fillText(txt3, 20, canvas.height - 160)
ctx.fillText(tex4, 20, canvas.height - 120)
ctx.fillText(tex4, 20, canvas.height - 80)
return canvas
},
//Canvas转图片文件(Image)
convasToImg(canvas) {
var date = new Date();
//console.log('Canvas转图片文件(Image)', date.getMinutes(), date.getSeconds())
// 新建Image对象,可以理解为DOM
let image = new Image()
// canvas.toDataURL 返回的是一串Base64编码的URL
// 指定格式 PNG
image.src = canvas.toDataURL('image/png')
return image
},
//图片文件(Image)转文件(File)
base64ToFile(urlData, fileName) {
var date = new Date();
//console.log('图片文件(Image)转文件(File)', date.getMinutes(), date.getSeconds())
let arr = urlData.split(',');
let mime = arr[0].match(/:(.*?);/)[1];
let bytes = atob(arr[1]); // 解码base64
let n = bytes.length
let ia = new Uint8Array(n);
while (n--) {
ia[n] = bytes.charCodeAt(n);
}
return new File([ia], fileName, { type: mime });
},
//file对象转base64
fileToBase64(file, callback) { //callback 是一个回调函数,而回调函数往这儿走一回,它结果就是base64
var date = new Date();
//console.log('file对象转base64', date.getMinutes(), date.getSeconds())
return new Promise((resolve, reject) => {
const fileReader = new FileReader()
fileReader.readAsDataURL(file)
fileReader.onload = function () {
let res = callback(this.result)
resolve(res)
}
})
},
callbaseFun(res) {
//console.log('上传的方法')
return new Promise((resolve, reject) => {
let that = this;
//console.log(res, 'base64')
let str = res.split(',');
str[0] = 'data:image/jpeg;base64';
let r = str.toString();
let url = "/ServerAPI/ServerBase64API.ashx"
var obj = {
imgbase64: r,
method: "ServerBase64png",
Module: "DaKa",
id: "a4e89358-9465-4295-87ea-4d0af2bd9133"
}
jQuery.ajax({
url: url,
type: 'POST',
data: obj,
dataType: 'json',
timeout: 8000,
})
.done(res => {
var date = new Date();
//console.log('上传成功', date.getMinutes(), date.getSeconds())
//console.log('成功时执行')
resolve(res)
})
.fail(err => {
//console.log(err, '上传失败')
reject({ msg: '上传失败' })
})
})
},
async oneUpload(e){
let base64 = e.content;
let tempCanvas = await this.imgToCanvas(base64) //得到canvas的值
const canvas = this.addWatermark(tempCanvas , '水印1' , '水印2' , '水印3'); //这儿水印的参数是根据 addWatermark()方法里面的 ctx.fillText(txt1, 20, canvas.height - 240) 进行修改
const img = this.convasToImg(canvas); //Canvas转图片文件(Image)
let newFile = this.base64ToFile(img.src) //图片文件转file对象
let res = await this.fileToBase64(newFile, this.callbaseFun) //file转base64, 注意 第二个参数是一个回调函数,也就是说回调函数里面的参数就是base64的值
}