前言

水印的目的是为了防止信息泄漏,保护版权,在很多网站里都有用到了水印,水印真的只是看到的这么简单吗?
接触到水印相关知识是因为一个需求,也是因为这个需求给我打开了水印相关的大门。当时有个需求是给一个图片添加任意多个水印,并且这些水印可以让用户拖拽到图片的任意位置,做完这个功能之后为了也把它抽出来做成了一个npm插件:add-move-water-picture,不过这个插件是当时随手写的,一直都没有在完善了,准备有时间重新整理一下。

水印的实现

言归正传,水印的添加可以分为前端环境添加和后端环境添加,这两者各有各的好处,网上有很多这里就不一一列举了,这不是文章的重点。
当你在浏览这篇文章时,屏幕上就有你的英文名,这个就是明水印了,稍微懂一些开发的人员就可以让这个水印消失,各位同学可以找一个带有水印的网页,然后大家控制台进入Elements中直接搜索watermark大概率都会搜到一个class为watermark的dom元素,这个就是水印。点击水印可以看到它添加水印的方式,通常都是使用背景图的方式添加。
下面就来一一实现这些水印到添加方式。

全屏覆盖水印

既然想让屏幕出现水印,第一个想到的就是用一个东西遮在网页内容上层不就可以了吗,这样每次截图、拍照都会把上层内容包裹进去。这里的实现方式就是利用绝对定位让一个div在内容上层,这个div的宽高和内容区一样,然后生成一个个固定宽高的小div,求出多少列和多少行生成对应的元素即可。

     <style>.waterWrapper {position: absolute;top: 0;left: 0;display: flex;flex-wrap: wrap;pointer-events: none;overflow: hidden;box-sizing: border-box;}</style><div class="water">我是网页内容<p>我是网页内容</p><p>我是网页内容</p><p>我是网页内容</p></div><script>// div和绝对定位形式function cssHelper(el, prototype) {for (let i in prototype) {el.style[i] = prototype[i]}}function createItem() {const item = document.createElement('div')item.innerHTML = '这是水印'cssHelper(item, {position: 'absolute',top: `50px`,left: `50px`,fontSize: `16px`,color: '#000',lineHeight: 1.5,opacity: 0.1,transform: `rotate(-15deg)`,transformOrigin: '0 0',userSelect: 'none', // 用户无法选中whiteSpace: 'nowrap',})return item;}function createWater() {const waterHeight = 100;const waterWidth = 160;const { clientWidth, clientHeight } = document.documentElement || document.body;// 不能使用ceil向上取整,否则会出现超出一屏的水印const column = Math.floor(clientWidth / waterWidth);const rows = Math.floor(clientHeight / waterHeight);const waterWrapper = document.createElement('div');waterWrapper.className = 'waterWrapper'for (let i = 0; i < column * rows; i++) {const wrap = document.createElement('div');cssHelper(wrap, Object.create({position: 'relative',width: `${waterWidth}px`,height: `${waterHeight}px`,flex: `0 0 ${waterWidth}px`,overflow: 'hidden',}));wrap.appendChild(createItem());waterWrapper.appendChild(wrap)}document.body.appendChild(waterWrapper)}createWater();window.onresize = function() {const wrapper = document.getElementsByClassName('waterWrapper')[0];document.body.removeChild(wrapper);createWater();}

效果如下:

canvas背景图

使用canvas也可以实现水印的添加,这个是利用背景图的方式配置repeat属性来铺满全屏。使用canvas生成一个小的图片,然后导出base64格式的图片设置为背景即可,具体可看代码:

<style>.watermark {position: fixed;top: 0;left: 0;bottom: 0;right: 0;user-select: none;pointer-events: none;background-repeat: repeat;}</style>
</head>
<body><div>这是网页内容</div><script>function createWater(text) {const angle = -20;const canvas = document.createElement('canvas');canvas.width = 120;canvas.height = 50;const ctx = canvas.getContext('2d');ctx.clearRect(0, 0, 120, 50);ctx.fillStyle = '#404040';ctx.globalAlpha = 0.1;ctx.font = `16px Avenir, Helvetica, Arial, sans-serif`ctx.rotate(Math.PI / 180 * angle);ctx.fillText(text, 0, 50);return canvas.toDataURL();}const wrapper = document.createElement('div');wrapper.className = 'watermark';wrapper.style.backgroundImage = `url(${createWater('这是水印')})`;document.body.appendChild(wrapper);</script>
</body>

效果如下:

水印防篡改

像这种明水印可以说是防君子不防小人,因为可以很轻易的就隐藏掉这些水印,所以我们需要给水印做一些保护措施。我们知道想删除这个水印可以选中这个dom元素直接delete,也可以给属性设置display:none隐藏掉。针对这些操作可以监听dom元素的变动。正好MutationObserver可以满足这些条件,在进行dom的删除和修改属性时进行判断修改的是不是水印元素,如果是水印元素就立即生成一个新的添加或者替换进去,保证水印一直存在:

const watermark = document.getElementsByClassName('watermark')[0];
// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };
// 当观察到变动时执行的回调函数
const callback = function (mutationsList, observer) {// Use traditional 'for loops' for IE 11
for (let mutation of mutationsList) {if (mutation.type === 'attributes') { // 监听属性const target = mutation.target;if (target === watermark) {document.body.replaceChild(watermark, target);}}mutation.removedNodes.forEach(function (item) { // 监听节点if (item === watermark) {document.body.appendChild(watermark);}});
}
};
// 监听元素
const targetNode = document.body;
// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);
// 以上述配置开始观察目标节点

这样就万无一失了吗?这当然不可能,浏览器是很强大的,会技术的程序猿更强大,可以利用一切可利用的工具来达到想要的目的。这些防篡改的方法都是基于js的,那我把浏览器的js禁了不就可以了吗,这样还是可以成功的把水印去掉。还有其他方式就不一一列举了。这种添加防篡改的方式只能过滤一些小白,有没有更加安全的水印添加方式呢?当然有了,那就是隐水印,加了水印却看不到,需要通过特定的方式解密后才可以看到。

图片隐水印

隐水印的实现可以利用颜色来实现,颜色可以用rgb或者rgba表示

上面一个div的背景色是rgb(233, 122, 55),下面的div的背景色是rgb(233, 122, 54),只是少了一个像素值,肉眼是看不出来的,可以利用这个原理来对图片做处理。

实现思路

我们拿图片为例,一个图片是由很多的数据组成,文字也是如此,可以通过canvas将图片绘制出来,然后通过getImageData函数拿到这个图片的数据信息,这个数据是一个数字,没有信息的数据是0,通过一定的规律来对图片的颜色修改进行加密,当需要显示出来的时候,再通过相同的规律进行解密。这就是大致的实现思路,图片的数据:

实现加密:
在写代码之前先定下一个加密规律:我们修改R通道的数据,将有信息存在的数据偶数变为奇数,解密的时候只填充偶数位,其余的全部不显示或者变为纯色即可。

const canvas = document.getElementById('canv')
const ctx = canvas.getContext('2d')
const img = new Image()
img.src = './article.jpeg'
let originData;
img.onload = function() {ctx.drawImage(img, 0, 0)
originData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height)
mergeData(textData, 'R')
}

首先写一个canvas标签,也可以通过document.createElement('canvas')来创建,给标签设置一个id位canv,再创建一个img对象,src设置为想加水印的图片,在img.onload方法中用canvas绘制出图片并获取到他的数据信息,其中的mergeData就是加密方法。

let textData;
ctx.font = '15px Microsoft Yahei';
ctx.fillStyle = 'red'
ctx.fillText('bruce好帅我好爱', 60, 130);
textData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height).data;

上面是添加水印文字,也是通过canvas来生成同时获取到他的data数据。实际上就是对两组像素数据信息进行处理。接下来就是加密的方法

// 加密过程
function mergeData(newData, color) {// 获取图片的datavar oData = originData.dataconsole.log(newData, originData)// bit是要改的通道所在的位置, offset是alpha相对于通道所在的位置var bit, offset// 判断是修改哪个颜色通道switch(color) {case 'R':bit = 0offset = 3break;case 'G':bit = 1offset = 2break;case 'B':bit = 2offset = 1break;}// r g b afor (var i = 0; i < oData.length; i++) {if (i % 4 === bit) {// 只修改目标通道if (newData[i + offset] === 0 && (oData[i] % 2 === 1)) {// 没有信息的像素,将目标通道的奇数像素改为偶数if (oData[i] === 255) {oData[i]--} else {oData[i]++}} else if (newData[i + offset] !== 0 && (oData[i] % 2 === 0)) {// 有信息的像素oData[i]++}}}ctx.putImageData(originData, 0, 0)
}

只需要在图片的onload方法中调用这个加密函数即可。现在我们就来对一张图片进行加密

看着和原图是一样的,但是其实已经被加密了,上面已经被打入了文字。

实现解密

解密就比较简单了,只需要知道加密的方法然后对应的解出就可以,这里是对R通道进行的处理,所以我们这里判断R通道是否为奇数即可,是奇数就填充,不是奇数就全部置为0,实现代码:

// 解密函数
function processData(originData) {var data = originData.datafor (var i = 0; i < data.length; i++) {if (i % 4 === 0) {// 像素通道if (data[i] % 2 == 0) {data[i] = 0} else {data[i] = 255}}}ctx.putImageData(originData, 0, 0)
}

解密的时候调用这个函数就可以,把页面上的图片右击保存,现在我们来看一下效果:

这就是解密后的样子,如果我们不想要原图的背景,可以在解密的循环里将其他通道都设为纯色,比如设置为0就是黑色背景:

以上就是暗水印的简单示例。这样加密也不是百分百安全,可以通过ps或者裁剪等一些其他方式把水印破坏,但是已经可以起到一定的安全保障。还有一种更好的添加隐水印的方法就是利用傅里叶变换来进行添加更加精准的水印,安全性会更高,感兴趣的同学可以阅读学习一下:傅里叶变换。

前端明水印到隐水印你了解了吗相关推荐

  1. 前端给页面添加暗水印的办法

    前端给页面添加暗水印的办法 上一篇文章讲到了在页面上添加明水印的方法,但是明水印比较好清除,而且对于一些没做处理的图片,当用户直接保存的时候,是没有水印的,这时候信息泄露问题依然存在.为了解决这样的问 ...

  2. Stegsolve查看隐水印(暗水印)java jar包工具

    Stegsolve查看隐水印.隐写水印.暗水印的java jar包工具 (1)下载jar包 file/Stegsolve.jar at main · zhangphil/file · GitHubCo ...

  3. 用java实现给图片增加图片水印或者文字水印(也支持视频图像帧添加水印)

    javaCV图像处理系列: javaCV图像处理之1:实时视频添加文字水印并截取视频图像保存成图片,实现文字水印的字体.位置.大小.粗度.翻转.平滑等操作 javaCV图像处理之2:实时视频添加图片水 ...

  4. Javascript实现网页水印(非图片水印)

    1 概述 1.1 定义 在一些B/S结构的应用系统中,有很多页面是需要有水印的.常见的就是公文系统.合同系统等.大家常常关注的是网站图片增加水印,而很少关注页面水印.刚去Google了一圈,关于页面水 ...

  5. c# 图片加图片水印、文字水印和图片文字水印

    加水印类: Code using System; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Dr ...

  6. php加图片源码_PHP添加文字水印或图片水印的水印类完整源代码与使用示例

    PHP实现的给图片添加水印功能,可添加文字水印或图片水印,使用文字水印时需要提供字体文件,使用图片水印时需要提供水印图片,水印图片不能比要添加水印的图片大,请使用背景透明的水印图片. 该水印类支持自定 ...

  7. php 上传加水印,php 图片上传加水印(自动增加水印)

    function upload($uploadfile,$watermark=1,$watertype=1,$content){ foreach($uploadfile['name'] as $key ...

  8. python给视频加水印_视频水印_Python SDK_服务端SDK_视频点播 - 阿里云

    初始化客户端 使用前请先初始化客户端,请参见 添加水印 调用AddWatermark接口,完成添加水印功能. 接口参数和返回字段请参见 说明水印文件OSS上传详细参数请参见from aliyunsdk ...

  9. 网页怎么在图片上添加文字_教你同时将图片水印和文字水印添加到视频画面

    随着剪辑软件的层出不穷,现在会制作创意视频的人越来越多.今天小编给大家分享一款剪辑软件--视频剪辑高手,以同时给视频添加图片水印和文字水印的效果为例,教大家如何操作. 编辑文字水印命令 勾选视频剪辑高 ...

最新文章

  1. codeforces 383D
  2. 汇总:一些不错的使用频率比较高的JS函数
  3. mqtt协议详解_IoT物联网设备上云技术方案详解
  4. BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊(LCT)
  5. python 中re模块学习随笔
  6. 2018-2019-1 20165320 《信息安全系统设计基础》第八周学习总结
  7. jquery遍历节点
  8. vue项目基本环境的配置与初始化
  9. 企业微信可以同步微信好友吗?怎么同步?
  10. 白话ES 的分布式架构原理
  11. java分词支持拼音_ik中文分词器及拼音分词器试用
  12. 纯文本,富文本,超文本
  13. 你不知道的电脑36个小技巧(纪念2011教师节)
  14. java周志第二周_20165325 2017-2018-2 《Java程序设计》结对编程_第二周:四则运算
  15. Hadoop-1-大数据概述
  16. python内置库求复数的辐角_皮肤与美容—医学专家如是说_中国大学MOOC(慕课)_章节测验答案...
  17. XXL-JOB原理--任务调度中心执行器注册(三)
  18. 以下html标记语言表示网页标题的标记是,第8章 HTML标记语言.ppt
  19. python天勤金叉编程代码大全_这些Python编程黑科技,装逼指南,高逼格代码,让你惊叹不已...
  20. Matlab Figure 窗口最大化方法

热门文章

  1. 电源负载怎么测试软件,测试电源负载瞬态响应的常用方法,拿走不谢!
  2. 一款开源的强横数据可视化分析工具,支持对excel文件进行转换分析
  3. 先做接口测试还是功能测试
  4. echarts 柱状图--柱体的点击事件
  5. 华摄氏度和摄氏度的转换小数点处理 单片机
  6. 《互联网信贷风险与大数据》读书笔记(三)
  7. 「工具」三分钟了解一款在线流程绘制工具:Whimsical
  8. 格式化数据#2:图灵奖(A.M. Turing Award)
  9. python 类型标注-typing --- 类型标注支持 — Python 3.7.9 文档
  10. 抖音小程序调起支付宝支付php微擎代码和HTML代码