为什么前端水印有水印?保护知识产权并防止未经授权的盗用。比如淘宝美团的图片背面有水印。保护公司机密资料,防止恶意人士泄露。一般来说,前后端都可以加水印。前端加水印适用场景:资源不绑定单个用户,而是一个资源,多个用户查看。需要在每个用户查看时加一个用户专用的水印,多用于一些机密文件或显示机密信息的页面,加水印的目的是在文件泄露时追查责任人。服务端水印的使用场景:资源对某个用户来说是唯一的,一个原始资源只需要处理一次,存储后不需要再次处理。水印的目的是标记资源的所有者。从前端的角度来看,有哪些实现方案?DOMoverlay使用div作为水印,需要两个关键的css属性。use-select:none和pointer-events,防止用户选择我的水印,允许用户穿透我的水印掩码。然后通过水印区域的宽高和水印块的宽高计算出我需要生成多少个水印块,然后展开。initDivWaterMark(userId:string){constwaterHeight=100constwaterWidth=100const{clientWidth,clientHeight}=document.documentElement||document.bodyconstcolumn=Math.ceil(clientWidth/waterWidth)constrows=Math.ceil/clientwaterHeight)for(leti=0;i,其中包含不可访问的ShadowDOM。因此,为简单起见,我们使用closedinitShadowdomWaterMark(userId:string){constshadowRoot=this.box.attachShadow({mode:'closed'})constwaterHeight=100constwaterWidth=100const{clientWidth,clientHeight}=document.documentElement||document.bodyconstcolumn=Math.ceil(clientWidth/waterWidth)constrows=Math.ceil(clientHeight/waterHeight)for(leti=0;i{func.call(this);};varobserver=newMutationObserver(回调);observer.observe(textNode,{characterData:true});textNode.data='1';//修改文本信息,触发dom更新}监控我们水印节点是否有变化,我们也可以有针对性的恢复。initObserver(){//观察者配置constconfig={attributes:true,childList:true,subtree:true}//观察到变化时执行的回调函数constcallback:MutationCallback=(mutationsList,observer)=>{for(constmutationofmutationsList){mutation.removedNodes.forEach((item)=>{if(item===this.box){this.warnningTargetChanged=true//省事,直接加到bodydocument.body.appendChild(this.box)}})}if(this.warnningTargetChanged){this.warnningTargetChanged=falseconsole.log(`用户${this.userId}的水印发生变化,可能涉嫌非法操作!!!`)}}//监听元素consttargetNode=document.body//创建观察者实例并传入回调函数constobserver=newMutationObserver(callback)//开始观察目标节点,上面配置observer.observe(targetNode,config)}当然,也不是万无一失,因为我们也可以通过disabled来解决javascript。像有些网站,打开F12会导致debugger死循环,也可以解决。暗水印如果有这种人,万事大吉又如何?这时候就需要隐藏水印的出现了。例如,大众点评上的图片实际上带有隐藏版权的水印。如果是商业盗用,是可以查出来的。产生暗水印的方法有很多种,常用的方法是通过修改RGB分量值的微小变化、DWT、DCT和FFT。DFT、DCT和DWT的联系和区别。前端实现主要靠RGB分量值的微小变化。我们都知道图片是由像素组成的,每个像素又是由RGB的三个元素组成的。当我们修改其中一个组件时,人眼很难看到变化,即使是像素眼的设计师也是如此。那么,只要我们能够获取到一张图片上每个像素点的具体信息,我们就可以通过操作RGB来隐藏我们想要的信息。那么如何获取像素信息呢?你需要使用canvas的CanvasRenderingContext2D.getImageData(),这个方法会返回一个ImageData对象,里面包含了一个像素信息数组。所以我们应该可以用这个方法做一个颜色选择器,一个存储所有像素信息的一维数组,一共有2562564=262144个值。一组有4个值,为什么?在浏览器中解析图片时,除了RGB值外,每一组中的第四个值就是透明度值,也就是像素信息,其实就是大家熟知的rgba值。以我们要隐藏的文本信息为例consttxt='testpoint'this.canvas=document.createElement('canvas')this.canvas.width=10this.canvas.height=10this.ctx=this.canvas.getContext('2d')this.ctx.clearRect(0,0,10,10)this.ctx.font=`14px`this.ctx.fillText(txt,0,0)consttextData=this.ctx.getImageData(0,0,10,10).data把上面的代码复制到控制台,可以发现字体数据基本都是0,0,0,xx。现在我们有了文本数据和图像数据,我们可以设计一个算法。以R通道为例,这是红色通道,(255,0,0,255)代表纯红色。我们遍历图像数据并检查它的每个R点。如果该点的文本数据存在,即alpha值不为0,那么我们就强行将当前图像信息的该点的值改为奇数。如果此时没有数字,则将其更改为偶数。那么最终图像数据中的奇数部分就是被文字覆盖的部分。偶数部分无关紧要。当我们最终要找到我们的目标副本时,我们只需要将奇数部分的值更改为255,并将所有其他通道和偶数部分更改为0。文本出现。//加密核心方法for(leti=0;i