这个功能需要的都是一些canvas基础的api,可自行学习
给图片添加水印
把图片绘制到canvas上
思路:
- 生成
HTMLImageElement实例 - 图片加载完成后,创建canvas环境
- 把图片绘制到canvas上
- 绘制水印到canvas上
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<canvas height="200" id="myCanvas" width="200">
</div>
<script>
var img = new Image();//浏览器提供一个原生构造函数`Image`,用于生成`HTMLImageElement`实例
img.src = 'https://img0.baidu.com/it/u=530426417,2082848644&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500';
// img.setAttribute("crossorigin", "crossorigin");// 这句代码是为了解决跨域问题,如果你的图片是自己的,没有跨域问题可以去掉
img.onload = () => {
// 准备canvas环境
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
// 先把图片绘制到canvas上
ctx.drawImage(img, 0, 0, 200, 200);
// 绘制水印到canvas上
for (let i = 0; i < 20; i++) {
ctx.rotate((-45 * Math.PI) / 180); // 水印初始偏转角度
ctx.font = "20px microsoft yahei";
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.fillText("rong rong rong rong rong", -120, i * 25);
ctx.rotate((45 * Math.PI) / 180); // 把水印偏转角度调整为原来的,不然他会一直转
}
}
</script>
</body>
</html>
如下:
给页面添加水印
利用canvas生成图片放到容器背景中
思路:
- 创建一个固定宽高的canvas
- 使用fillText、fillStyle、font 填充文字、颜色及字号
- 利用canvas.toDataURL() 将canvas转换图片为base64图片
- 将图片放到容器背景中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<canvas height="40" id="myCanvas" width="200">
</div>
<div id="box" style="height:200px;width:200px;">234444444</div>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "20px microsoft yahei";
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.fillText("w3school.com.cn", 10, 20);
let dataURL = canvas.toDataURL(); //转换图片为dataURL
var box = document.getElementById("box");
box.style.backgroundImage = "url(" + dataURL + ")";
</script>
</body>
</html>
缺点:手动改变节点样式可导致水印被取消
改进:使用Mutation Observer监听DOM变化
我们需要明确一点那就是MutationObserver是一个构造函数。它接受一个回调函数作为参数,回调函数在监听到目标DOM变化时会执行。
const mutation = new MutationObserver(callback);
当构建出对象之后我们需要确定我们要用这个对象监听哪个DOM,这个是通过提供的方法observe()来进行绑定的。这个方法接受两个参数,第一个参数就是目标DOM,第二个参数就是一个配置对象config,用来配置我们要对什么属性进行监听。
config的配置属性:
- attributes 值为布尔值,配置是否监听DOM的属性变化
- attributeFilter 值为一个数组,配置应该监听那些属性变化。
- attributeOldValue 值为布尔值,如果为true,那么回调函数的第一个参数对象中将多一个属性oldValue用来记录上一次的属性值。
- characterData 值为布尔值,配置是否监听元素内文本内容的变化(这个说法可能不太准确,或者TextNode内的文本会更准确)。
- characterDataOldValue 值为布尔值,功能同attributeOldValue。
- childList 值为布尔值,配置是否监听子元素的数量变化。
- subtree 值为布尔值,配置是否对所有后代元素也执行监听。
const config = {
attributes: true
}
mutation.observe(document.getElementById("id"), config);
既然我们可以监听DOM变化,那我们必然也可以解除监听。我们可以通过disconnect()来实现。
mutation.disconnect();
eg:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "20px microsoft yahei";
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.fillText("w3school.com.cn", 10, 20);
let dataURL = canvas.toDataURL(); //转换图片为dataURL
function fun() {
var box = document.getElementById("box");
box.style.backgroundImage = "url(" + dataURL + ")";
}
fun();
//监听元素
const mutation = new MutationObserver((el) => {
console.log('el: ', el);
fun();
});
const config = {
attributes: true,
subtree: true,
childList: true
}
mutation.observe(document.getElementById("box"), config);
// mutation.disconnect();
vue-watermark添加水印工具类
需求:系统中所有页面都加水印 思路:
- 在首页创建一个浏览器显示区域宽高大小一致的div
div.style.width = document.documentElement.clientWidth + 'px'
div.style.height = document.documentElement.clientHeight + 'px'
- 将该div固定定位到最上层,并禁用其鼠标事件
div.style.pointerEvents = 'none';//禁用鼠标事件
div.style.top = '30px'
div.style.left = '0px'
div.style.position = 'fixed'
div.style.zIndex = '999999'
- 利用canvas生成图片放到容器背景中
具体: 1.在utils文件夹下新建watermark.js工具类:
const watermark = {}
const setWatermark = (str) => {
const id = '1.8989898989898989.123412416'
if (document.getElementById(id) !== null) {
document.body.removeChild(document.getElementById(id))
}
// 创建一个画布
const can = document.createElement('canvas')
// 设置画布的长宽
can.width = 500
can.height = 200
const cans = can.getContext('2d')
// 旋转角度
cans.rotate(-9 * Math.PI / 180)
cans.font = '18px Vedana'
// 设置填充绘画的颜色、渐变或者模式
cans.fillStyle = 'rgba(200, 200, 200, 0.40)'
// 设置文本内容的当前对齐方式
cans.textAlign = 'left'
// 设置在绘制文本时使用的当前文本基线
cans.textBaseline = 'Middle'
// 在画布上绘制填色的文本(输出的文本,开始绘制文本的X坐标位置,开始绘制文本的Y坐标位置)
cans.fillText(str, can.width / 8, can.height / 2)
const div = document.createElement('div')
div.id = id
div.style.pointerEvents = 'none';//禁用鼠标事件
div.style.top = '30px'
div.style.left = '0px'
div.style.position = 'fixed'
div.style.zIndex = '999999'
div.style.width = document.documentElement.clientWidth + 'px'
div.style.height = document.documentElement.clientHeight + 'px'
div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat'
document.body.appendChild(div)
mutationFun(str, id);//监听元素
return id
}
// 该方法只允许调用一次
watermark.set = (str) => {
var id = setWatermark(str)
setInterval(() => {
if (document.getElementById(id) === null) {
id = setWatermark(str)
}
}, 500)
window.onresize = () => {
setWatermark(str)
}
}
//监听元素
function mutationFun(str, id) {
// eslint-disable-next-line no-undef
const mutation = new MutationObserver((el) => {
mutation.disconnect();
setWatermark(str)
});
const config = {
attributes: true,
subtree: true,
childList: true
}
mutation.observe(document.getElementById(id), config);
}
export default watermark
2.在需要的页面使用,只需在第一个页面引入即可:
import Watermark from '../utils/watermark'
Watermark.set('111111111')
补充:由于水印的内容不确定,按照上面的思路会出现水印显示不全的情况,所以,改进为如下思路
思路:
- 使用canvas创建一个与窗口大小一致的画布,然后使用canvas的fillText的方法绘制水印
- 画布不能覆盖网页上的有效事件,因些需要给canvas设置pointer-events: none;样式
- 创建的画布大小需要是窗口对角线尺寸的正方形,保证在画布在旋转某个角度时4个角不会出现空白区。同时需要将canvas的与当前窗口的尺寸进行计算出向左、向上的偏移量。
- 计算绘制的文字长度时,一定要设置font属性,否则会使用画布默认的字体大小计算长度,容易出现bug。
- 计算行数、列数时,需要注意,尤其在fillText时规划好x,y坐标
- 在旋转画布时,一定要将旋转中心设置到画布的x,y轴的中心位置,旋转完毕后再复原置原来位置。
- 使用canvas的toDataURL方法导出图片的base64的代码,使用css background-repeat属性将图片作为背景进行填充
具体: 1.在utils文件夹下新建watermark.js工具类:
const watermark = {}
const setWatermark = (strObj) => {
let { str1, str2 } = strObj
const id = '1.69823457311989.14567716'
if (document.getElementById(id) !== null) {
document.body.removeChild(document.getElementById(id))
}
/**
* 共用属性
*/
const rotate = -10
/**
* 1.创建画布
*/
const canvas1 = document.createElement('canvas')
const canvas2 = document.createElement('canvas')
// 设置画布的长宽
const canvasParent = document.body
// 画布大小需要为容器的对角线长度,
// 画布在旋转时不会出现对角空白无水印!!!
const maxSizeContainer = Math.max(canvasParent.offsetWidth, canvasParent.offsetHeight)
const minSizeCanvas = Math.ceil(Math.SQRT2 * maxSizeContainer)
canvas1.setAttribute('width', String(minSizeCanvas))
canvas1.setAttribute('height', String(minSizeCanvas))
canvas2.setAttribute('width', String(minSizeCanvas))
canvas2.setAttribute('height', String(minSizeCanvas))
// 修改画布在容器中的偏移,使其中心位置与容器的中心位置永远保持一致
// 在旋转角度时也要以其中心点为中心进行旋转
// canvas.style.position = 'fixed'
// canvas.style.top = - Math.ceil((minSizeCanvas - maxSizeContainer) / 2) + 'px'
// canvas.style.left = - Math.ceil((minSizeCanvas - maxSizeContainer) / 2) + 'px'
/**
* 2.创建画笔
*/
const cans1 = canvas1.getContext('2d')
const cans2 = canvas2.getContext('2d')
// 旋转角度
cans1.font = '18px Vedana'
cans2.font = '18px Vedana'
// 设置填充绘画的颜色、渐变或者模式
cans1.fillStyle = 'rgba(160, 160, 160, 0.20)'
cans2.fillStyle = 'rgba(160, 160, 160, 0.20)'
// 设置文本内容的当前对齐方式
cans1.textAlign = 'left'
cans2.textAlign = 'left'
// 设置在绘制文本时使用的当前文本基线
cans1.textBaseline = 'middle'
cans2.textBaseline = 'middle'
// 计算字符串的横向与纵向间距,设置了font的大小与字体
const offsetX1 = Math.ceil(cans1.measureText(str1).width) + 60 // 可以获取text的宽度
const offsetX2 = Math.ceil(cans1.measureText(str2).width) + 100 // 可以获取text的宽度
const offsetY = 200
// 列/行数
const rowCount = Math.ceil(minSizeCanvas / offsetY)
const colCount1 = Math.ceil(minSizeCanvas / offsetX1)
const colCount2 = Math.ceil(minSizeCanvas / offsetX2)
// 旋转
cans1.translate(minSizeCanvas / 2, minSizeCanvas / 2)
cans1.rotate(rotate * Math.PI / 180)
cans1.translate(-minSizeCanvas / 2, -minSizeCanvas / 2)
cans2.translate(minSizeCanvas / 2, minSizeCanvas / 2)
cans2.rotate(rotate * Math.PI / 180)
cans2.translate(-minSizeCanvas / 2, -minSizeCanvas / 2)
// 行
for (let i = 0; i < rowCount; i++) {
// 列
for (let j = 0; j < colCount1; j++) {
cans1.fillText(str1, offsetX1 * j, offsetY * i)
}
for (let j = 0; j < colCount2; j++) {
cans2.fillText(str2, offsetX2 * j, offsetY * i + 100)
}
}
const div = document.createElement('div')
div.id = id
div.style.pointerEvents = 'none';//禁用鼠标事件
div.style.top = '0px'
div.style.left = '0px'
div.style.position = 'fixed'
div.style.zIndex = '999999'
div.style.width = document.documentElement.clientWidth + 'px'
div.style.height = document.documentElement.clientHeight + 'px'
// div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat'
div.style.backgroundImage = 'url(' + canvas1.toDataURL('image/png') + '),url(' + canvas2.toDataURL('image/png') + ')'
div.style.backgroundPosition = 'center center'
div.style.backgroundRepeat = 'repeat repeat'
document.body.appendChild(div)
mutationFun(strObj, id);//监听元素
return id
}
// 该方法只允许调用一次
watermark.set = (strObj) => {
var id = setWatermark(strObj)
setInterval(() => {
if (document.getElementById(id) === null) {
id = setWatermark(strObj)
}
}, 500)
window.onresize = () => {
setWatermark(strObj)
}
}
//监听元素
function mutationFun(strObj, id) {
// eslint-disable-next-line no-undef
const mutation = new MutationObserver((el) => {
mutation.disconnect();
setWatermark(strObj)
});
const config = {
attributes: true,
subtree: true,
childList: true
}
mutation.observe(document.getElementById(id), config);
}
export default watermark
2.在需要的页面使用,只需在第一个页面引入即可:
import Watermark from '../utils/watermark'
let strObj = {
str1:'aaa',
str2:'bbbbbb'
};
Watermark.set(strObj); // 增加水印