最近在学习一些工具的实现方案,恰好认真研究了下前端实现水印的方式和原理,下面让我们一起看看水印究竟是怎么实现的吧
其实目前基于水印的实现方式有很多种,包括但不限于
- 后端基于原图生成水印图片
- 前端基于DOM实现水印效果
- 前端基于Canvas实现水印效果
今天我们着重讲解下基于Canvas实现水印的原理
核心步骤:
- 使用canvas生成画布,并通过canvas.toDataURL("image/png")生成水印之后的图片
- 创建一个新的div用来append已生成的canvas
原理是非常简单的,我们也可以通过不同配置来灵活配置水印
完整代码如下:
// waterMarkUtils.ts
const setWaterMark = (str: string, options?) => {
const setOptions = {
fillStyle: options?.fillStyle || "red",
gradient: options?.gradient || [],
rotate:
options?.rotate === 0
? options.rotate
: options?.rotate || (-20 * Math.PI) / 180,
font: options?.rotate || "12px Vendana",
textBaseline: options?.textBaseline || "middle",
width: options?.width || document.documentElement.clientWidth,
height: options?.height || document.documentElement.clientHeight
};
const id = "1.23452384164";
if (document.getElementById(id) !== null) {
document.body.removeChild(<HTMLElement>document.getElementById(id));
}
const can = document.createElement("canvas");
can.width = 200;
can.height = 130;
const cans = <CanvasRenderingContext2D>can.getContext("2d");
cans.rotate(setOptions.rotate);
cans.font = setOptions.font;
cans.textBaseline = setOptions.textBaseline;
// cans.fillText(str, can.width / 5, can.height / 2);
// 设置线型值
if (setOptions?.gradient?.length > 0) {
setOptions.gradient.forEach(element => {
const grd = cans.createLinearGradient(0, 0, 170, 0);
grd.addColorStop(element.value, element.color);
cans.fillStyle = grd;
cans.fillText(str, can.width / 5, can.height / 2);
});
} else {
// 画布字体的颜色
cans.fillStyle = setOptions.fillStyle;
cans.fillText(str, can.width / 5, can.height / 2);
}
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 = "10000000";
div.style.width = `${setOptions.width}px`;
div.style.height = `${setOptions.height}px`;
div.style.background = `url(${can.toDataURL("image/png")}) left top repeat`;
document.body.appendChild(div);
return id;
};
const watermark = {
// 设置水印
set: (str: string, options = {}) => {
let id = setWaterMark(str, options);
if (document.getElementById(id) === null) id = setWaterMark(str, options);
},
// 删除水印
delWatermark: () => {
const id = setWaterMark("");
if (document.getElementById(id) !== null)
document.body.removeChild(<HTMLElement>document.getElementById(id));
}
};
export default watermark;
示例:
setWaterMark('水印勿动', {
fillStyle:#007fff
gradient: [
{ value: 0, color: 'magenta' },
{ value: 0.5, color: 'red' },
{ value: 1.0, color: 'green' }
]
})
水印配置详解:
| 配置项 | 配置项值设置 |
|---|---|
| fillStyle | 填充样式 |
| gradient | 设置渐变样式,使其可以在两个颜色或多个指定的颜色之间显示平稳过渡, gradient: [ { value: 0, color: 'magenta' }, { value: 0.5, color: 'red' }, { value: 1.0, color: 'green' } ] |
| rotate | 设置水印的旋转方向 |
| font | 设置水印的字体 |
| textBaseline | 设置字体的基准线,属性值可以设置为:alphabetic/top/hanging/middle/ideographic/bottom |
| width | 设置每个水印的宽度 |
| 设置值每个水印的高度 |
看下最终的成效吧: