下面我们通过几种方案来实现飞书里的这种水印效果:
一. js + fixed
第一种方案是最为简单最直觉的一种方案,就是创造许多个含有水印内容的div 标签,使用fixed 固定在页面上
js代码与css样式如下:
1 | const body = document.body;const container = document.createElement("div");for (let i = 0; i < 200; i++) px`; mark.style.left = `$px`; container.insertAdjacentElement("afterbegin", mark);}body.insertAdjacentElement("afterbegin", container); |
1 | .mark |
最终实现效果:
但是这种方案相当不安全,但凡懂点前端的人都能打开控制台把水印内容修改成别人的或者把它直接删掉,下面我们再看看如何来解决这些问题
二. MutationObserver
先放上MutationObserver的文档
概括来讲就是一个能够检测DOM树变化的接口。
简单写一个例子来看它是如何工作的:
1 | // 得到要观察的元素,注意,被观察的元素的删除是无法检测到的const body = document.body;// 创建一个叫 `observer` 的新 `MutationObserver` 实例,// 并将回调函数传给它const observer1 = new MutationObserver((mutationList, observer) => );// 在 MutationObserver 实例上调用 `observe` 方法// 接收两个参数,第一个为要观察的元素,第二个为一些配置observer1.observe(body, ); |
有了这个接口就可以防止别人打开控制台修改水印了,对前面的代码修改如下:
1 | function createMark() px`; mark.style.left = `$px`; container.insertAdjacentElement("afterbegin", mark); } body.insertAdjacentElement("afterbegin", container); // 由于该接口无法检测出本身的删除,所以需要在body上检测container的删除 let bodyObserver = new MutationObserver((mutationList, observer) => }); bodyObserver.observe(body, ); // 检查水印容器与每一个水印的修改 let containerObserver = new MutationObserver((mutationList) => else }); containerObserver.observe(container, );}createMark(); |
至此已经完成检测水印被修改的功能了。
三. 使用canvas绘制水印
最重要的MutationObserver已经讲完了,再聊一些别的绘制水印的方法。
在前面的方法里,为了能在不同大小屏幕的设备上都显示满水印使用了上百个div标签展示水印,这显然会造成一些浪费,更好的解决方法是使用canvas生成图片水印在背景上repeat
1 | const body = document.body;const canvas = document.createElement("canvas");canvas.setAttribute("width", "280px");canvas.setAttribute("height", "140px");const ctx = canvas.getContext("2d");ctx.textAlign = "center";ctx.textBaseline = "top";ctx.font = "16px Microsoft Yahei";ctx.fillStyle = "rgba(184, 184, 184, 0.6)";ctx.rotate((Math.PI / 180) * -20);ctx.fillText("这是水印", 140, 70);const base64Url = canvas.toDataURL();const container = document.querySelector(".mark_container") || document.createElement("div");const containerStyle = ` position:fixed; top:0; left:0; width:100%; height:100%; z-index:999; pointer-events:none; background-repeat:repeat; background-image:url('$')`;container.setAttribute("style", containerStyle);body.insertAdjacentElement("afterbegin", container);// ...省去MutationObserver的部分,逻辑基本都是一样的 |
效果图如下:
还有些类似的方法,例如使用svg绘制,总体来讲都是差不多的