概要
需求:后端返回的url图片,预览时需要加上水印
// 全局绘制水印参数
objmsg: {
rotate: 20, //旋转角度 默认20
fontcolor: "255, 255, 255, 0.8", //字体颜色 rgba类型 默认 255, 255, 255, 0.2(黑色)
density: 3, //稠密度 数值越大,水印越多
str: ["文印文字"], //水印文字 数组类型 最大三行(即length<=3)[必传]
}
一、创建XMLHttpRequest对象
源代码如下:
let that = this;
var xhr = new XMLHttpRequest();
xhr.open('get', file.url, true);
// 设置请求头(这一步得设置不然oss图片还是跨域)
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.responseType = 'blob';
xhr.onload = function () {
if (this.status == 200) {
let imgFile = URL.createObjectURL(this.response);
console.log('this.response',this.response)
console.log('====================',imgFile)
that.objmsg.imgpath = imgFile; //需要添加水印图片路径 [必传]
that.objmsg.domid = "dialogImage"; //图片dom的id 用来更换添加水印后的图片
drawWaterMark.init(that.objmsg);
}
};
提示:使用XMLHttpRequest来异步地获取一个图片文件,并将其转换为Blob对象。之后,它创建一个表示该Blob对象的URL,并将其用于绘制水印。下面是对这段代码的详细解释
- 创建XMLHttpRequest对象,配置请求,设置请求头,设置响应类型为Blob:
var xhr = new XMLHttpRequest(); xhr.open('get', file.url, true); xhr.setRequestHeader("Cache-Control", "no-cache"); xhr.responseType = 'blob';
- 处理响应:
当请求完成时,onload事件会被触发。如果响应状态码为200(表示成功),则执行以下操作:xhr.onload = function () { if (this.status == 200) { let imgFile = URL.createObjectURL(this.response); console.log('this.response', this.response); console.log('====================', imgFile); that.objmsg.imgpath = imgFile; //需要添加水印图片路径 [必传] that.objmsg.domid = "dialogImage"; //图片dom的id 用来更换添加水印后的图片 drawWaterMark.init(that.objmsg); // 添加水印 } };
a. 使用URL.createObjectURL方法,将Blob对象转换为可以直接在浏览器中使用的URL。
b. 将Blob的URL和响应的Blob对象打印到控制台。
c. 将imgFile(图片的URL)赋值给that.objmsg.imgpath,这个值之后将用于绘制水印。
d. 设置that.objmsg.domid为"dialogImage",这可能是用于将添加水印后的图片显示在指定DOM元素上的ID。
c. 调用drawWaterMark.init(that.objmsg)来开始绘制水印。 - 发送请求:
xhr.send();
二、添加水印
提示:这段JavaScript代码的主要功能是给图片添加水印。下面我将详细解释代码的每一部分
// 添加水印源码
var drawWaterMark = {};
drawWaterMark.init = function (objmsg) {
console.log('--------------',objmsg)
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = objmsg.imgpath;
img.setAttribute("crossOrigin", 'Anonymous');
img.onload = function () {
//绘制和图片大小相同的canvas
canvas.width = img.width;
canvas.height = img.height;
//canvas绘制图片,0 0 为左上角坐标原点
ctx.drawImage(img, 0, 0);
//绘制水印
if (objmsg.rotate != undefined && objmsg.rotate != null) {//旋转角度[默认20]
ctx.rotate((Math.PI / 120) * -objmsg.rotate);
} else {
ctx.rotate((Math.PI / 120) * -20);
};
var fontsize = 20; // 字体大小[默认20px]
if (img.width >= 3456) { // 根据图片大小改变水印文案字体大小
fontsize = 50;
} else if (img.width >= 2700) {
fontsize = 30;
} else if (img.width >= 2000) {
fontsize = 26;
} else if (img.width >= 1436) {
fontsize = 20;
} else if (img.width >= 800) {
fontsize = 12;
} else if (img.width >= 500) {
fontsize = 10;
} else {
fontsize = 8;
};
ctx.font = fontsize + "px Microsoft Yahei";
var fontcolor = '255, 255, 255, 0.2';
if (objmsg.fontcolor != undefined && objmsg.fontcolor != null) {//字体颜色透明度[默认白色]
fontcolor = objmsg.fontcolor;
};
ctx.fillStyle = "rgba(" + fontcolor + ")";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
var density = 3;
if (objmsg.density != undefined && objmsg.density != null) {//稠密度[默认3]
density = objmsg.density
};
let maxpixel;
let minPixel;
if(img.width > img.height) {
maxpixel = img.width;
minPixel = img.height;
}else {
maxpixel = img.height;
minPixel = img.width
};
for (var i = -1000; i < maxpixel; i += minPixel / density) {
for (var k = 0; k < maxpixel; k += minPixel / density) {
var str = objmsg.str;
if (str.length == 1) {
ctx.fillText(str[0], i, k);
} else if(str.length==2){
ctx.fillText(str[0], i, k);
ctx.fillText(str[1], i, k + (fontsize-0+5));//多行
} else if (str.length == 3 || str.length > 3) {
ctx.fillText(str[0], i, k);
ctx.fillText(str[1], i, k + (fontsize - 0 + 5));//多行
ctx.fillText(str[2], i, k + (fontsize*2 - 0 + 5));//多行
}
}
};
var base64 = canvas.toDataURL("image/jpeg");//添加过水印的base64图片
if (objmsg.domid != undefined && objmsg.domid != null) {//id图片
let url1 = that.getBase64URL(base64)
that.dialogImageUrl = url1;
};
if (objmsg.cb != undefined && objmsg.cb != null) {//回调函数
objmsg.cb(base64);//回调函数
}
}
};
-
变量定义,初始化函数
let that = this; var drawWaterMark = {}; drawWaterMark.init = function (objmsg) { ... }
a. 定义了一个空对象drawWaterMark,用于存放与绘制水印相关的功能
b. 在drawWaterMark对象上定义了一个init方法,它接受一个对象参数objmsg,该对象应该包含绘制水印所需的各种信息,如图片路径、旋转角度、字体大小、字体颜色等。 -
创建Canvas和Context,Image对象
var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); var img = new Image(); img.src = objmsg.imgpath; img.setAttribute("crossOrigin", 'Anonymous');
a. 使用document.createElement创建了一个新的元素,并通过getContext(‘2d’)获取其2D渲染上下文。
b. 创建了一个新的Image对象,并设置其src属性为objmsg.imgpath(图片的路径)。crossOrigin属性被设置为’Anonymous’,这用于跨域图片加载。 -
当图片加载完成后,onload事件触发,绘制图片到Canvas
// 绘制和图片大小相同的canvas canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0);
-
设置水印选择角度
if (objmsg.rotate != undefined && objmsg.rotate != null) { ctx.rotate((Math.PI / 120) * -objmsg.rotate); } else { ctx.rotate((Math.PI / 120) * -20); }
根据objmsg.rotate的值(如果存在)旋转Canvas。如果不存在或为空,则默认旋转20度。
-
设置水印字体样式(大小,颜色,对齐方式)
var fontsize = 20; if (img.width >= 3456) { // 根据图片大小改变水印文案字体大小 fontsize = 50; } else if (img.width >= 2700) { fontsize = 30; } else if (img.width >= 2000) { fontsize = 26; } else if (img.width >= 1436) { fontsize = 20; } else if (img.width >= 800) { fontsize = 12; } else if (img.width >= 500) { fontsize = 10; } else { fontsize = 8; }; var fontcolor = '255, 255, 255, 0.2'; if (objmsg.fontcolor != undefined && objmsg.fontcolor != null) { fontcolor = objmsg.fontcolor; }; ctx.font = fontsize + "px Microsoft Yahei"; // 字体大小[默认20px] ctx.fillStyle = "rgba(" + fontcolor + ")"; // 字体颜色透明度[默认白色] ctx.textAlign = "center"; // 水平对齐方式:居中对齐 ctx.textBaseline = "middle"; // 垂直对齐方式:居中对齐
-
绘制水印
var density = 3; if (objmsg.density != undefined && objmsg.density != null) {//稠密度[默认3] density = objmsg.density }; let maxpixel; let minPixel; if(img.width > img.height) { maxpixel = img.width; minPixel = img.height; }else { maxpixel = img.height; minPixel = img.width }; // 开始绘制水印文字 for (var i = -1000; i < maxpixel; i += minPixel / density) { for (var k = 0; k < maxpixel; k += minPixel / density) { var str = objmsg.str; if (str.length == 1) { ctx.fillText(str[0], i, k); } else if(str.length==2){ ctx.fillText(str[0], i, k); ctx.fillText(str[1], i, k + (fontsize-0+5));//多行 } else if (str.length == 3 || str.length > 3) { ctx.fillText(str[0], i, k); ctx.fillText(str[1], i, k + (fontsize - 0 + 5));//多行 ctx.fillText(str[2], i, k + (fontsize*2 - 0 + 5));//多行 } } };
a. density 设置水印稠密度,默认为3。值越大绘制的水印越多
b. maxpixel;minPixel。获取图片的最大值像素值和最小值像素值,方便绘制水印时,确保不会遗落部分地方无水印情况
c. 使用两层循环来遍历图片的像素区域,并在这个区域内绘制水印文字。- 外层循环的变量i从-1000开始,每次增加minPixel / density,直到maxpixel。这里从-1000开始为了确保水印文字不会完全贴着图片的边界绘制。
- 内层循环的变量k从0开始,每次增加minPixel / density,直到maxpixel。
- 在每次循环中,获取objmsg.str的值,根据其长度绘制水印文字。
- 如果str的长度为1,则直接在(i, k)位置绘制这个字符。
- 如果str的长度为2,则在(i, k)位置绘制第一个字符,在(i, k + (fontsize-0+5))位置绘制第二个字符。这里fontsize-0+5为了调整第二行文字与第一行文字的垂直间距。
- 如果str的长度为3或更多,则按照类似的方式绘制前三个字符,每个字符都在不同的垂直位置。
-
拿到含有水印的base64图片
var base64 = canvas.toDataURL("image/jpeg");//添加过水印的base64图片 let url1 = that.getBase64URL(base64); // 获取url图片地址
三、将base64转化为url
getBase64URL(pic) {
const blob = this.base64ImgtoFile(pic)
const blobUrl = window.URL.createObjectURL(blob);
return blobUrl
},
base64ImgtoFile (dataurl, filename = 'file') {
//将base64格式分割:['data:image/png;base64','XXXX']
const arr = dataurl.split(',')
// .*? 表示匹配任意字符到下一个符合条件的字符 刚好匹配到:
// image/png
const mime = arr[0].match(/:(.*?);/)[1] //image/png
//[image,png] 获取图片类型后缀
const suffix = mime.split('/')[1] //png
const bstr = atob(arr[1]) //atob() 方法用于解码使用 base-64 编码的字符串
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime
})
},