js 实现文档水印效果

用js实现一个水印效果。效果图如下:

watermark

之前在网上看到一个思路,先用canvas画一个小的水印,然后再在一个大的画布上重复平铺之前画好的canvas。
实际做出来之后发现如果水印区域过大的话,平铺canvas需要耗费巨大的性能,甚至会导致页面崩溃。所以改用背景图的形式展现,只需要将生成的canvas到处为base64的数据,并赋给水印区域的背景属性,同时设置background-repeat: repeat;即可。

接下来我们一步一步的实现我们想要的效果。

HTML

由于水印一般为当前用户的名字或者邮箱,所以需要实时计算小水印的宽高。
最准确的方式当然是直接放到页面里面,再用js去取。
初始html源代码如下:

1
2
3
4
5
6
7
<body>
<!-- 用来计算一块水印宽高的元素 -->
<div id="watermark-text">我是一个水印</div>

<!-- 水印区域 -->
<div class="container"></div>
</body>

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* 单个水印块 需要设置好字体和字号,不需要出现在可视区域 */
#watermark-text {
font-family: 'Microsoft YaHei';
position: absolute;
top: -1000px;
display: inline-block;
font-size: 20px;
line-height: 20px;
padding: 10px;
}
.container {
width: 1000px;
height: 500px;
border: 1px solid blue;
position: relative;
}

/* 背景图元素,贴合主体显示区域,并设置较高的层级 */
.repeat-watermark {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
background-repeat: repeat;
}

JS

水印倾斜角度为30度。示意图如下:
水印块计算示意图

初始位置如下
水印初始位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

var Watermark = function(containerCls) {
// canvas 容器
var container = document.querySelector(containerCls);

// 计算水印块大小
var markTextEl = document.querySelector('#watermark-text');
var watermarkText = markTextEl.innerText;

// 水印旋转 -30 度,由高中几何知识,得出水印宽高
// 见上图
var markWidth = markTextEl.clientWidth * 0.87 + markTextEl.clientHeight * 0.5;
var markHeight = markTextEl.clientWidth * 0.5 + markTextEl.clientHeight * 0.87;

// 根据上传计算的结果创建 canvas 标签
var canvasEl1 = document.createElement('canvas');
canvasEl1.setAttribute('class', 'watermark');
canvasEl1.setAttribute('width', markWidth);
canvasEl1.setAttribute('height', markHeight);
canvasEl1.setAttribute('style', 'display: none');

// 插入文档
container.appendChild(canvasEl1);

// 设置背景图元素
var imgBgEl = document.createElement('div');
imgBgEl.setAttribute('class', 'repeat-watermark');
container.appendChild(imgBgEl);

this.opt = {
container: container,
markWidth: markWidth || 160,
markHeight: markHeight || 100,
watermark: watermarkText,
docWidth: container.clientWidth,
docHeight: container.clientHeight,
fontStyle: "20px Microsoft YaHei", //水印字体设置
rotateAngle: -30 * Math.PI / 180, //水印字体倾斜角度设置
fontColor: "rgba(220, 220, 220, 127)", //水印字体颜色设置

// 初始位置见上图
firstLinePositionX: -(markHeight - markTextEl.clientHeight * 0.87) * 0.5, //canvas第一行文字起始X坐标
firstLinePositionY: (markHeight - markTextEl.clientHeight * 0.87) * 0.87 + markTextEl.clientHeight //Y
};
this.draw(this.opt.docWidth, this.opt.docHeight);
};

Watermark.prototype = {

draw: function(docWidth, docHeight) {
var cw = this.opt.container.querySelector('.watermark');
var imgBg = this.opt.container.querySelector('.repeat-watermark');

var ctx = cw.getContext("2d");
//清除小画布
ctx.clearRect(0, 0, this.opt.markWidth, this.opt.markHeight);

// 设置文字样式
ctx.font = this.opt.fontStyle;
ctx.rotate(this.opt.rotateAngle);
ctx.fillStyle = this.opt.fontColor;
ctx.fillText(this.opt.watermark, this.opt.firstLinePositionX, this.opt.firstLinePositionY);
//坐标系还原
ctx.rotate(-this.opt.rotateAngle);

// 输出为base64数据
var data = cw.toDataURL('image/png', .1);
imgBg.style.background = "url(" + data + ")"
}
};
0%